maven 自定义插件小示例

maven作为一个核心是插件的架构,所有的工作都通过插件来执行,主要分为两类build和report
  • Build plugins:在build阶段执行,在pom中必须配置在<build/>标签里面
  • Reporting plugins:在生成site阶段执行,在pom中配置在<reporting/>标签里面。

maven官方已经给我们提供了各种各样的插件 maven plugins,但有时我们还是需要自己定义我们特殊的插件,比如我们项目中根据特定的文件,自动生成对应的java类,这时就需要我们实现自己的特定插件了,本示例包含两个工程,
  • 插件工程:maven-plugin-test
  • 使用插件的工程:maven-plugin-client-test

为了使示例保持简单,插件工程只演示了从特定目录下读取特定文件类型,并没有生成java类。

1.插件工程
1.1 工程结构



1.2 Mojo类
/**
 * 
 */
package falcon.chengf.maven.plugin.test;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.Map;

import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.project.MavenProject;
import org.apache.maven.shared.model.fileset.FileSet;
import org.apache.maven.shared.model.fileset.util.FileSetManager;
import org.yaml.snakeyaml.Yaml;

/**
 * read content from yaml files (.yml)
 *
 * @goal readyml
 * @phase generate-sources
 * @threadSafe
 */
public class ReadYmlMojo extends AbstractMojo {

	/**
	 * The source directory of yml files. This directory is added to the
	 * classpath at schema compiling time. All files can therefore be referenced
	 * as classpath resources following the directory structure under the source
	 * directory.
	 *
	 * @parameter property="sourceDirectory"
	 *            default-value="${basedir}/src/main/resources"
	 */
	private File sourceDirectory;

	/**
	 * @parameter property="outputDirectory"
	 *            default-value="${project.build.directory}/generated-sources/yml"
	 */
	private File outputDirectory;

	/**
	 * @parameter property="sourceDirectory"
	 *            default-value="${basedir}/src/test/resources"
	 */
	private File testSourceDirectory;

	/**
	 * @parameter property="outputDirectory"
	 *            default-value="${project.build.directory}/generated-test-sources/yml"
	 */
	private File testOutputDirectory;

	/**
	 * A set of Ant-like inclusion patterns used to select files from the source
	 * directory for processing. By default, the pattern
	 * <code>**&#47;*.yml</code> is used to select grammar files.
	 * 
	 * @parameter property="includes"
	 **/

	private String[] includes = new String[] { "**/*.yml" };

	/**
	 * A set of Ant-like inclusion patterns used to select files from the source
	 * directory for processing. By default, the pattern
	 * <code>**&#47;*.yml</code> is used to select grammar files.
	 *
	 * @parameter
	 */
	private String[] testIncludes = new String[] { "**/*.yml" };

	/**
	 * A set of Ant-like exclusion patterns used to prevent certain files from
	 * being processed. By default, this set is empty such that no files are
	 * excluded.
	 *
	 * @parameter
	 */
	protected String[] excludes = new String[0];

	/**
	 * A set of Ant-like exclusion patterns used to prevent certain files from
	 * being processed. By default, this set is empty such that no files are
	 * excluded.
	 *
	 * @parameter
	 */
	protected String[] testExcludes = new String[0];

	/**
	 * The current Maven project.
	 *
	 * @parameter default-value="${project}"
	 * @readonly
	 * @required
	 */
	protected MavenProject project;

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.apache.maven.plugin.Mojo#execute()
	 */
	public void execute() throws MojoExecutionException, MojoFailureException {
		getLog().info("start to read content from yml file");
		getLog().info("sourceDirectory:" + sourceDirectory.getAbsolutePath());
		getLog().info("outputDirectory:" + outputDirectory.getAbsolutePath());
		getLog().info("includes:" + Arrays.asList(includes));
		boolean hasSourceDir = null != sourceDirectory && sourceDirectory.isDirectory();
		boolean hasTestDir = null != testSourceDirectory && testSourceDirectory.isDirectory();
		if (!hasSourceDir && !hasTestDir) {
			throw new MojoExecutionException("neither sourceDirectory: " + sourceDirectory + " or testSourceDirectory: "
					+ testSourceDirectory + " are directories");
		}
		if (hasSourceDir) {
			String[] includedFiles = getIncludedFiles(sourceDirectory.getAbsolutePath(), excludes, getIncludes());
			readFiles(includedFiles, sourceDirectory, outputDirectory);
		}

		if (hasTestDir) {
			String[] includedFiles = getIncludedFiles(testSourceDirectory.getAbsolutePath(), testExcludes,
					getTestIncludes());
			readFiles(includedFiles, testSourceDirectory, testOutputDirectory);
			project.addTestCompileSourceRoot(testOutputDirectory.getAbsolutePath());
		}
		
		
		getLog().info("read from yml file end");
	}

	/**
	 * 根据条件过滤需要处理的文件
	 * @param absPath
	 * @param excludes
	 * @param includes
	 * @return
	 */
	private String[] getIncludedFiles(String absPath, String[] excludes, String[] includes) {
		FileSetManager fileSetManager = new FileSetManager();
		FileSet fs = new FileSet();
		fs.setDirectory(absPath);
		fs.setFollowSymlinks(false);

		for (String include : includes) {
			fs.addInclude(include);
		}
		for (String exclude : excludes) {
			fs.addExclude(exclude);
		}
		return fileSetManager.getIncludedFiles(fs);
	}

	private void readFiles(String[] files, File sourceDir, File outDir) throws MojoExecutionException {
		for (String filename : files) {
			getLog().info("read content from file " + filename);
			try {
				doRead(filename, sourceDir, outDir);
			} catch (IOException e) {
				throw new MojoExecutionException("Error compiling protocol file " + filename + " to " + outDir, e);
			}
		}
	}

	private static final Yaml yaml = new Yaml();

	protected void doRead(String filename, File sourceDirectory, File outputDirectory) throws IOException {
		File src = new File(sourceDirectory, filename);

		// 通过snakeyml读取yml文件内容
		Map<String,Object> content= (Map)yaml.load(new FileInputStream(src));
		getLog().info(filename+"'s content is"+content.toString());
	}


	protected String[] getIncludes() {
		return includes;
	}

	protected String[] getTestIncludes() {
		return testIncludes;
	}
}



注意点
  • 1.ReadYmlMojo类上面的注释中有三个注解,这三个注解有特定含义,不能省略,其中@goal 对应的值(readyml)必须和pom.xml中的goal一致
  • 2.属性上面注释中的@parameter注解也有特殊含义,不能省略,如sourceDirectory上的@parameter property="sourceDirectory"要和使用插件工程的pom中指定的参数一致

1.3 插件工程的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>falcon.chengf</groupId>
	<artifactId>maven-plugin-test</artifactId>
	<version>1.0</version>

	<packaging>maven-plugin</packaging>

	<name>maven-plugin-test</name>
	<url>http://maven.apache.org</url>
	
	<properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.version>2.0.10</maven.version>
        <file-management.version>1.2.1</file-management.version>
    </properties>
    
    <dependencies>
        <dependency>
            <groupId>org.apache.maven</groupId>
            <artifactId>maven-plugin-api</artifactId>
            <version>2.0</version>
        </dependency>
        <dependency>
            <groupId>org.yaml</groupId>
            <artifactId>snakeyaml</artifactId>
            <version>1.10</version>
        </dependency>
        <dependency>
            <groupId>org.apache.maven</groupId>
            <artifactId>maven-project</artifactId>
            <version>${maven.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.maven.shared</groupId>
            <artifactId>file-management</artifactId>
            <version>${file-management.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.maven.plugin-tools</groupId>
            <artifactId>maven-plugin-annotations</artifactId>
            <version>3.2</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.codehaus.plexus</groupId>
            <artifactId>plexus-utils</artifactId>
            <version>3.0.8</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-plugin-plugin</artifactId>
                <version>3.2</version>
                <configuration>
                    <goalPrefix>readyml</goalPrefix>
                    <skipErrorNoDescriptorsFound>true</skipErrorNoDescriptorsFound>
                </configuration>
                <executions>
                    <execution>
                        <id>mojo-descriptor</id>
                        <goals>
                            <goal>descriptor</goal>
                        </goals>
                    </execution>
                    <execution>
                        <id>help-goal</id>
                        <goals>
                            <goal>helpmojo</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
    <profiles>
        <profile>
            <id>jdk-1.8</id>
            <activation>
                <activeByDefault>true</activeByDefault>
                <jdk>1.8</jdk>
            </activation>
            <properties>
                <maven.compiler.source>1.8</maven.compiler.source>
                <maven.compiler.target>1.8</maven.compiler.target>
                <maven.compiler.compilerVersion>1.8</maven.compiler.compilerVersion>
            </properties>
        </profile>
    </profiles>
</project>


可以看到 goalPrefix 对应的属性值和mojojava类中的值时一样的

2 使用插件的工程
2.1 工程结构


2.2 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>falcon.chengf</groupId>
	<artifactId>maven-plugin-client-test</artifactId>
	<version>0.0.1-SNAPSHOT</version>

	<name>maven-plugin-client-test</name>
	<url>http://maven.apache.org</url>
	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
	</properties>
	<dependencies>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>4.12</version>
			<scope>test</scope>
		</dependency>

	</dependencies>

	<build>
		<sourceDirectory>${project.basedir}/target/generated-sources</sourceDirectory>
		<plugins>
			<plugin>
				<groupId>falcon.chengf</groupId>
				<artifactId>maven-plugin-test</artifactId>
				<version>1.0</version>
				<configuration>
					<skipErrorNoDescriptorsFound>true</skipErrorNoDescriptorsFound>
				</configuration>
				<executions>
					<execution>
						<id>readyml</id>
						<phase>generate-sources</phase>
						<goals>
							<goal>readyml</goal>
						</goals>
						<configuration>
							<sourceDirectory>${project.basedir}/src/main/resources/yml</sourceDirectory>
							<outputDirectory>${project.basedir}/target/generated-sources</outputDirectory>
							<includes>
								<include>**/*.yml</include>
							</includes>
						</configuration>
					</execution>
				</executions>
			</plugin>
		</plugins>
		<pluginManagement>
			<plugins>
				<!--This plugin's configuration is used to store Eclipse m2e settings 
					only. It has no influence on the Maven build itself. -->
				<plugin>
					<groupId>org.eclipse.m2e</groupId>
					<artifactId>lifecycle-mapping</artifactId>
					<version>1.0.0</version>
					<configuration>
						<lifecycleMappingMetadata>
							<pluginExecutions>
								<pluginExecution>
									<pluginExecutionFilter>
										<groupId>falcon.chengf</groupId>
										<artifactId>
											maven-plugin-test
										</artifactId>
										<versionRange>
											[0.0.1-SNAPSHOT,)
										</versionRange>
										<goals>
											<goal>readyml</goal>
										</goals>
									</pluginExecutionFilter>
									<action>
										<ignore></ignore>
									</action>
								</pluginExecution>
							</pluginExecutions>
						</lifecycleMappingMetadata>
					</configuration>
				</plugin>
			</plugins>
		</pluginManagement>
	</build>
</project>



注意点:
  • execution中的id属性要和插件工程对应的goal一致
  • configuration中的属性如outputDirectory要和插件工程中@parameter的property值一致
  • 在eclipse中引用插件工程后回报如下错误



  • 直接选择第二项忽略即可


在使用插件的工程中创建/src/main/resources/yml文件,存入test.yml内容如下:
name: chengf
level: 11


执行maven package命令,可以在控制台看到maven插件正确执行,解析的test.yml文件的内容


完整工程代码请参考附件

猜你喜欢

转载自fengyilin.iteye.com/blog/2389638