《Maven实战》一书中介绍了jetty-maven-plugin插件的使用。不过,该书写的时候Maven才发布3.0版,而现在已经是3.6版,其“约定优于配置”得到了更进一步发扬。比如对于插件前缀,只要在POM中配置了插件,对于匹配maven-${prefix}-plugin或${prefix}-maven-plugin模式的插件artifact,会自动把匹配到的{perfix}部分作为前缀,所以对于jetty-maven-plugin这个插件来说,就无需在setting.xml中设置,就可以直接使用。

jetty是一个轻量级的servlet的容器,可以独立(Standalone)运行,也可以嵌入(Embedded)运行,而jetty-maven-plugin这个插件就是以嵌入方式运行jetty,并且把当前项目发布到容器里。同样地,tomcat也有maven插件,tomcat的maven插件是区分tomcat主版本的,tomcat6-maven-plugin、tomcat7-maven-plugin、tomcat8-maven-plugin分别运行的是tomcat6、7、8的Embedded版,但是目前没有tomcat9的maven插件。

如果不想用嵌入式容器,或者要用的容器没有对应的maven插件的话(比如tomcat9),就要用《Maven实战》中介绍的cargo-maven2-plugin插件了。cargo的官网地址是https://codehaus-cargo.github.io/,这个插件支持的大多数常用的servlet容器甚至EAR容器。同样,maven现在也能自动匹配cargo-maven2-plugin这个插件的前缀,而无需修改setting.xml文件。可以使用的最小配置如下:

[...]
  <build>
    <plugins>
      <plugin>
        <groupId>org.codehaus.cargo</groupId>
        <artifactId>cargo-maven2-plugin</artifactId>
      </plugin>
    </plugins>
  </build>
[...]

最小配置实际上只是声明了插件,并没有什么配置。配置插件最主要是通过container、configuration配置容器和对容器进行设置。而container里最重要的又是containerId和type,其中:containerId是容器的标记,默认是jetty8x,虽然可以随便命名,但是如果像默认值那么规范的命名,那么剩余的参数大多可以省略,因为插件会根据Id在支持的容器中去查找容器,甚至会自动下载安装。这也是前面的最小配置会自动下载安装jetty8并安装、运行的原因。容器的type有installed、embedded、remote三种,默认是installed,也就是本地安装、嵌入式和远程。remote容器指的是在cargo插件之外启动的容器和后面容器configuration节里的Runtime类型对应,通过配置的deployer进行布署,其运行参数只能用远程容器里的参数,是不可以设置的。

容器的类型installed是指“安装版”,和embedded“嵌入版”对比起来就容易明白了,而并不是已安装的意思。当然,配置为已经安装好的容器也是可以的,这时就要用home指明已安装容器的主目录,如果没有指定,且根据containerId能够解析出是插件支持的哪一个容器软件的话,就会自动去下载。当然,从哪下?下载以后安装(解压)在哪里?可以通过zipUrlInstaller或artifactInstaller指定。不过,如果配置了zipUrlInstaller或artifactInstaller,插件就会忽略home配置。显然zipUrlInstaller用来配置下载zip或tar.gz包,默认的downloadDir是${java.io.tmpdir}/cargo/installs,而artifactInstaller很明显就是下载在maven本地仓库里。它们都有一个extractDir参数指出解压在哪个位置,默认是${project.build.directory}/cargo/installs。另外,zipUrlInstaller或artifactInstaller只对installed类的窗口有效,也就是embedded的容器,没办法控制从哪下,下什么版本的。

需要注意的是插件目标的调用。cargo:start这个命令,从字面上来看,是启动服务的命令,它显然能够启动一个本地已经安装好的容器。并且,如果需要,它还能下载并安装一个。但是,这个命令其实只合适用来启动embedded类型的容器,因为一般的容器会随着父进程的结束而结束,简单的启动是不行的,需要用脚本建立一个守护进程,这显然不是一个插件需要考虑的事情。所以,如果要用cargo来运行一个安装好的容器的话,应该用cargo:run命令,这个命令启动容器后挂起,等待用户按Ctrl+C中止运行,可以进行页面测试,但这里运行mvn插件的命令行窗口就不能再输入别的命令了。所以,如果要反反复复的测试,又不想反反复复的重启容器,就需要用布署(deploy)功能。

对于cargo:deploy、cargo:redeploy、cargo:undeploy这几个命令,它们分别是cargo:deployer-deploy、cargo:deployer-redeploy、cargo:deployer-undeploy的别名,是用来把项目发布到容器中的,发布的目标类型有Standalone、Existing和Runtime三种。其中独立的是指配置是独立的,意思是把运行时配置复制到一个目录,然后发布到这个目录,这样修改配置不会影响已经安装好的容器。默认就是这种方式。缺省配置文件是复制在项目target目录下,由于这个目录会被clean掉,在没有配置的情况下,deploy是不可能成功的。所以,如果项目被clean了,就需要运行cargo:configure从容器的安装目录复制配置,当然,如果用缺省的配置文件目录,得先得有target目录,那么执行maven的顺序应该是package->cargo:configure->cargo:deploy->cargo:run。其中的deploy这个目标倒是会由run自动调用。如果配置方式是Existing的话,就不用configure这个过程了。但是需要配置已经有配置文件的目录通常就是tomcat安装目录。比如:

          <configuration>
          <!-- Container configuration -->
          <container>
            <containerId>tomcat9x</containerId>
            <home>/Library/apache-tomcat-9.0.24</home>
          </container>
          <configuration>
            <type>existing</type>
            <home>/Library/apache-tomcat-9.0.24</home>
          </configuration>

并且,由于是发布到容器的安装目录,所以可以用安装的startup.sh启动tomcat,在unix/linux下这个命令启动tomcat之后退出,而tomcat继续运行,这样就可以在同一个命令窗口运行其他命令,而且可以反复发布,缺点就是需要当前用户拥有容器发布目录的写权限。

还可以配置发布目标为Runtime类型,这种发布类型对应前面的Remote类型容器,要求容器已经运行,并且支持远程发布。一般需要用户名密码,对于Tomcat来说,需要修改conf/tomcat-users.xml文件,定义manager-script角色并授予一个用户如:

<role rolename="manager-script"/>
<user username="tomcat" password="s3cret" roles="manager-script"/>

然后像下面这样配置cargo插件

<configuration>
          <container>
            <containerId>tomcat9x</containerId>
            <type>remote</type>
          </container>
          <configuration>
            <type>runtime</type>
            <properties>
            <cargo.remote.username>tomcat</cargo.remote.username>
            <cargo.remote.password>s3cret</cargo.remote.password>
            </properties>
          </configuration>
        </configuration>

这种方法实际最好用,就是第一次要配置Tomcat用户,稍稍复杂了一点。

Remote在这里并不仅仅是“远程”,只要不是通过同一cargo进程启动的容器,包括本机上运行的容器,都算是“远程”。配置Remote布署,有一个properties默认值是localhost,只需要配置成远程主机名或者IP,就可以真正的发布到远程服务器上,当然根据远程容器的配置,可能还需要配置属性,当然,如果使用的是容器的默认端口,cargo是会根据容器的类型自动设置,无需专门配置。另外,在本机Tomcat上测试Remote发布,用manager-script角色就可以了,但是网上说布署到真正的远程服务器还需要manager-jmx角色,这个没有测试。如果发布到失败,仔细看错误提示应该不难解决。