Aug 232011
 

写完一个插件之后,我们就需要对他进行测试
如果我们用人工测试的话,将会非常麻烦,因为maven插件本身的发布流程就非常复杂
你需要编译打包你的maven插件,然后安装到本地库(或远程私库中),然后写一个sample project,再运行,看一下是否正确
当然你也可以用单元测试来解决一些问题,但是单元测试比较难保证插件最终正确,而且maven插件很经常是跟文件打交道

这里我们就需要对maven插件进行自动化的集成测试
maven的生命周期是包含集成测试的,默认是没有绑定任何功能。不过如果你google一下,会找到一些maven进行集成测试的例子,大致就是启动jetty,然后通过http访问验证,再关闭jetty

我们这里使用的是maven-invoker-plugin,它就是用于maven插件的集成测试

先在pom.xml中加入

<build>
	<plugins>
		<plugin>
			<artifactId>maven-invoker-plugin</artifactId>
			<configuration>
				<cloneProjectsTo>${project.build.directory}/it</cloneProjectsTo>
			</configuration>
			<executions>
				<execution>
					<id>integration-test</id>
					<goals>
						<goal>install</goal>
						<goal>run</goal>
					</goals>
				</execution>
			</executions>
		</plugin>
	</plugins>
</build>

cloneProjectsTo是先将测试案例拷贝出来再运行
execution段的设定是把maven-invoker-plugin的两个goal绑定到integration-test上
integration-test这个生命周期会在mvn install之前调用

集成测试的内容放在 src/it 目录下,每新建一个目录代表一个独立的测试,里面放一个完整的maven项目,当然你在这个项目里面需要引入自己编写的maven插件并且运行
另外还需要一个postbuild.groovy文件,放在测试案例的根目录,这个脚本的用处是检查运行后的maven项目是否达到自己要的效果。很明显,看名字就知道用groovy来写,一般我们会检查一下,是否产生了某某文件等等来判定,如果不正确的话抛出异常

然后我们在maven插件目录运行mvn integration-test就能进行集成测试了

Aug 222011
 

Mojo是一个很简单的Java Bean模式的类,你会发现Mojo所继承的AbstractMojo里面之后非常少的方法。那我们需要在Mojo.execute里面获取当前运行中的上下文如何处理呢?答案是注入,就是跟spring ioc差不多的注入方式。

常用的注入主要有两种,第一种是xml配置中的额外设置
比如说我们有这么一个plugin的配置

<plugin>
	<artifactId>maven-eclipse-plugin</artifactId>
	<configuration>
		<downloadSources>true</downloadSources>
	</configuration>
</plugin>

所有写在configuration里面的属性都可以注入到Mojo中,比如说以下代码

/**
 * @parameter
 */
private boolean downloadSources;

就可以通过downloadSources变量获得配置中的值
值得注意的是,这里是不用生成完整的JavaBean模式的get/set的,并且private是有效的

javadoc里面还可以加入其他属性,比如说

/**
 * @parameter default-value="true"
 * @readonly
 */
private boolean downloadSources;

就是默认为true,并且不能通过配置修改(当然我们这里肯定不会有这样的需求)
更多的javadoc可以参看官方文档中的说明

第二种注入的数据就是上下文,跟HttpServlet.getServletContext这种写法不一样,如果我们需要Mojo运行期的上下文,也是通过注入获得的

/**
 * @parameter expression="${project}"
 * @readonly
 */
private MavenProject project;
 
/**
 * @component
 * @readonly
 */
private ArtifactFactory artifactFactory;

例如这里我们就能获得ArtifactFactory和MavenProject
需要注意的是这里可能有两种方法,第一种跟xml配置获得的方法差不多,通过expression指定名字
实际上,你在xml里面,也可以通过${project}获得相应的东西进行一些简单的操作(当然xml里面只能文本描述,这里是一个类)

另外一种就是使用@component这个标注,可以获得一些基本的组件实例

Aug 192011
 

当maven内置的功能不能满足需求的时候怎么办,那就只能给它写插件了。
(话说回来,给maven扩展只能写一个很完整的插件,而不能是一个简单的script,真的是太笨重了)

网络上很多maven的文章,但基本很少谈及如何给它写插件,即使你搜索maven plugin,也只是给你返回一堆如何使用maven插件的文章。希望这边文章能给一些maven使用者带来帮助。

我在这里先假设你已经懂得使用maven,我不会贴出完整的pom.xml文件

首先,你需要创建一个maven项目,插件是一种特殊的maven项目
然后修改pom.xml,将packaging改为maven-plugin

<packaging>maven-plugin</packaging>

通过properties定义maven的版本

<properties>
	<maven.version>2.2.1</maven.version>
</properties>

maven3已经出了很久,并且兼容maven2,因此我们团队内部都是统一使用maven3,但是我这里编写插件使用的是maven2,可以同时在maven2和maven3下使用,不过其实这个原因并不重要,真正的原因是因为maven3的代码实在太烂了,最初的时候我用maven3的api lib来写,发现里面很多代码根本没有注释,而且很多代码已经废弃,但是并没有明确说明究竟用什么方法代替。最后我使用了maven2中被maven3废弃的api来完成我的功能,跑的挺好的,就是有时可能会有一些使用准备废弃的api的提醒而已。

接着添加依赖

<dependencies>
	<dependency>
		<groupId>org.apache.maven</groupId>
		<artifactId>maven-plugin-api</artifactId>
		<version>${maven.version}</version>
	</dependency>
	<dependency>
		<groupId>org.apache.maven</groupId>
		<artifactId>maven-core</artifactId>
		<version>${maven.version}</version>
	</dependency>
</dependencies>

然后开始创建Mojo类,maven插件里面每一个具体的功能都是一个Mojo
比如说eclipse:clean和eclipse:eclipse就是两个Mojo

/**
 * @goal helloWorld
 */
public class HelloWorldMojo extends AbstractMojo {
    public void execute() throws MojoExecutionException
    {
        getLog().info("Hello, world!");
    }
}

首先继承AbstractMojo,并且实现execute()方法,这个就是每次调用进入的地方
然后需要在类的Javadoc上定义,这是一个annotation出来之前常用的定义方法(或许未来maven会将它改成annotation,那就能提供编译校验和IDE校验)。我们必须定义@goal,代表运行目标,简单来说就是eclipse:clean中的clean
Mojo写在哪个package底下都是可以的

这样,我们就完成了一个简单的maven plugin,然后我们需要一个简单的测试来确定他正确运行
先通过maven install将它安装到本地仓库

然后打开任意maven的项目(比如说我们原来已经在用maven的项目),在pom.xml增加一个plugin

<build>
    <plugins>
      <plugin>
        <groupId>xxx</groupId>
        <artifactId>xxx</artifactId>
        <version>xxx</version>
        <executions>
          <execution>
            <phase>compile</phase>
            <goals>
              <goal>helloWorld</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>

需要留意的是phase部分,我们将这个plugin绑定到compile这个周期
然后我们运行mvn compile,就能成功看见Hello, world!输出
(当然你也可以直接通过命令行运行,需要带上完整的groupId和artifactId才能调用)

Aug 162011
 

目标:
1、命令行用maven进行scala项目构建
2、产生eclipse项目文件

pom.xml文件

<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/maven-v4_0_0.xsd">
	<groupId>com.xxx</groupId>
	<artifactId>xxx</artifactId>
	<version>1.0-SNAPSHOT</version>
	<modelVersion>4.0.0</modelVersion>
 
	<properties>
		<scala.version>2.8.1</scala.version>
	</properties>
 
	<build>
		<plugins>
			<plugin>
				<artifactId>maven-eclipse-plugin</artifactId>
				<configuration>
					<downloadSources>true</downloadSources>
					<buildcommands>
						<buildcommand>org.scala-ide.sdt.core.scalabuilder</buildcommand>
					</buildcommands>
					<projectnatures>
						<projectnature>org.scala-ide.sdt.core.scalanature</projectnature>
						<projectnature>org.eclipse.jdt.core.javanature</projectnature>
					</projectnatures>
					<classpathContainers>
						<classpathContainer>org.eclipse.jdt.launching.JRE_CONTAINER</classpathContainer>
						<classpathContainer>org.scala-ide.sdt.launching.SCALA_CONTAINER</classpathContainer>
					</classpathContainers>
					<sourceIncludes>
						<sourceInclude>**/*.scala</sourceInclude>
					</sourceIncludes>
				</configuration>
			</plugin>
			<plugin>
				<groupId>org.scala-tools</groupId>
				<artifactId>maven-scala-plugin</artifactId>
				<executions>
					<execution>
						<goals>
							<goal>compile</goal>
							<goal>testCompile</goal>
						</goals>
					</execution>
				</executions>
			</plugin>
			<plugin>
				<groupId>org.codehaus.mojo</groupId>
				<artifactId>build-helper-maven-plugin</artifactId>
				<executions>
					<execution>
						<id>add-source</id>
						<phase>generate-sources</phase>
						<goals>
							<goal>add-source</goal>
						</goals>
						<configuration>
							<sources>
								<source>src/main/scala</source>
							</sources>
						</configuration>
					</execution>
					<execution>
						<id>add-test-source</id>
						<phase>generate-sources</phase>
						<goals>
							<goal>add-test-source</goal>
						</goals>
						<configuration>
							<sources>
								<source>src/test/scala</source>
							</sources>
						</configuration>
					</execution>
				</executions>
			</plugin>
		</plugins>
	</build>
</project>

要点分析

	<properties>
		<scala.version>2.8.1</scala.version>
	</properties>

通过properties定义scala的版本
因为scala的版本是不兼容的,比如说2.8编译的class文件不能跟2.9的类库一起使用
这个定义同时会影响maven-scala-plugin中使用的scala版本
当然你也可以在dependencies中通过${scala.version}使用这个版本号

maven-eclipse-plugin部分是产生eclipse项目,对应使用scala-ide
打开downloadSources下载类库源代码,可以在eclipse中直接查看
sourceIncludes段必须加入,不然会出现代码目录中看不到scala文件的情况

maven-scala-plugin段用于命令行用maven进行构建

build-helper-maven-plugin段用于引入额外的代码目录
这个配置同时对命令行构建和eclipse项目生成有效
我这个是一个mixed Java/Scala项目