23

I have this task for the project with 4 nested subprojects using Maven:

  1. For each child: jar-up resource directory including project dependencies
  2. Move up to the parent project
  3. With a single command extract all created archives into various remote destinations (full install), that may include http server, app server, file server, etc. (mostly *NIX). Destination is provided on subproject level
  4. It should also be possible to unzip/copy from the individual subproject (partial install)

Files are not Java - mostly various scripts and HTML

I'm looking at the various plugins to help with the task: assembly, dependency, antrun, unzip. Dependency looks promising but I need to unzip not only dependency jars but the (sub)project content as well. Also since I can't really tight the operation to the Maven lifecycle how would I trigger remote install? mvn dependency:unpack? That's not very descriptive or intuitive. Is is possible to create a custom goal (e.g. project:install) without writing a plugin?

Using Maven is company standard so please do not offer alternatives - I'm pretty much stuck with what I have

Rich Seller
  • 83,208
  • 23
  • 172
  • 177
Bostone
  • 36,858
  • 39
  • 167
  • 227
  • 1
    Ha ha. I wish I was "stuck" with Maven at my company. – SingleShot Sep 10 '09 at 15:27
  • I didn't mean it in derogatory way :) – Bostone Sep 10 '09 at 15:28
  • I don't understand what "Destination is provided on subproject level" means. Do you deliver all files to every destination? – Rich Seller Sep 10 '09 at 15:31
  • 4 subprojects. One deploys to http server, other 3 to one different fileserver each. The destination setting is provided in the child POM – Bostone Sep 10 '09 at 15:35
  • So all files in one sub-project are delivered to the single destination – Bostone Sep 10 '09 at 15:36
  • why do they need to be delivered by the parent then, is it not sufficient to deliver each project in its own build? – Rich Seller Sep 10 '09 at 15:39
  • Basically I want to be able to deploy individually (from subproject level) but then move up to the parent and deploy all 4 with a single command. "Deploy" refers to the remote deployment (not Maven deploy) – Bostone Sep 10 '09 at 15:42
  • Won't running "deploy" on the parent project just call "deploy" on all the child projects as well? I don't see the problem – Glen Sep 10 '09 at 15:45
  • when you deploy all 4 with the single command, does that mean each project goes only to the destination defined in its own pom? or something else? – Rich Seller Sep 10 '09 at 15:53
  • There should be no difference. One set of files goes to one destination (per subproject) – Bostone Sep 10 '09 at 16:05
  • my current understanding of your problem is this: You have a number of projects, each with a target location defined in its pom. 1. You want to be able to deliver each project to its target location individually. 2. You want to be able to run a single build that will deliver each project to its target location Before I launch into a full blown explanation of how you might do this, ccould you confirm if I have the understanding correct please – Rich Seller Sep 10 '09 at 16:05
  • Glen - I'm not talking about "mvn deploy". – Bostone Sep 10 '09 at 16:05
  • Rich - yes. The only detail - I would like to have uber-build delivered from a parent POM. So the actual files/configurations resign in child POMs and yes - each one should be able to build/deliver it's own files. Go one level up and I should build/deliver all 4 subprojects to their individual destinations – Bostone Sep 10 '09 at 16:09

5 Answers5

15

Ok, I think the following might do what you need. The drawback of this approach is that there will be an interval between each deployment as the subsequent build is executed. Is this acceptable?

Define a profile in each project with the same name (say "publish"). Within that profile you can define a configuration to use the antrun-plugin to deliver the files with FTP (see below).

In the parent project you'll have a modules element, defining each project as a module. If you run mvn install -P publish, each project will be built in turn with the publish profile enabled, and the final artifact published to the target during the install phase. If you need to deploy additional files, modify the include element accordingly.

Note the parameters for the FTP task have been set as properties, this allows them to be overridden from the command-line and/or inherited from the parent POM.

<profiles>
  <profile>
    <id>publish</id>
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-antrun-plugin</artifactId>
      <executions>
        <execution>
          <id>ftp</id>
          <phase>install</phase>
          <configuration>
            <tasks>
              <ftp action="send" 
                  server="${ftp.host}" remotedir="${ftp.remotedir}" 
                  userid="${ftp.userid}" password="${ftp.password}" 
                  depends="${ftp.depends}" verbose="${ftp.verbose}">
                <fileset dir="${project.build.directory}">
                  <include 
                    name="${project.build.finalName}.${project.packaging}"/>
                </fileset>
              </ftp>
            </tasks>
          </configuration>
          <goals>
            <goal>run</goal>
          </goals>
        </execution>
      </executions>
      <dependencies>
        <dependency>
          <groupId>commons-net</groupId>
          <artifactId>commons-net</artifactId>
          <version>1.4.1</version>
        </dependency>
        <dependency>
          <groupId>ant</groupId>
          <artifactId>ant-commons-net</artifactId>
          <version>1.6.5</version>
        </dependency>
        <dependency>
          <groupId>ant</groupId>
          <artifactId>ant-nodeps</artifactId>
          <version>1.6.5</version>
        </dependency>
      </dependencies>
    </plugin>
    <properties>
      <ftp.host>hostname</ftp.host>
      <ftp.remotedir>/opt/path/to/install</ftp.remotedir>
      <ftp.userid>user</ftp.userid>
      <ftp.password>mypassword</ftp.password>
      <ftp.depends>yes</ftp.depends>
      <ftp.verbose>no</ftp.verbose>          
    </properties>
  </profile>
</profiles>

Update: based on your comment: You could use the dependency plugin to download each dependency, except that a parent can't have a dependency on a child, and it will be built before the child. It would have to be another project. you also need to have somewhere the information for where to deploy them to. At the moment you have the target information in the individual projects so it isn't accessible in the deployer project.

Taking this approach, you can define multiple profiles in the new project, one for each artifact. Each profile defines a dependency:copy execution to obtain the jar and an antrun execution for one of the projects. Common configuration (such as the dependencies for the antrun plugin) can be pulled out of the profiles. Also be aware that the properties will be merged if you define multiple profiles, so yo may need to qualify them with the artifact name, for example ftp.artifact1.host.

<profiles>
  <profile>
    <id>deploy-artifact1</id>
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-dependency-plugin</artifactId>
      <executions>
        <execution>
          <id>copy-dependency</id>
          <phase>prepare-package</phase>
          <goals>
            <goal>copy</goal>
          </goals>
          <configuration>
            <artifactItems>
              <artifactItem>
                <groupId>name.seller.rich</groupId>
                <artifactId>artifact1</artifactId>
                <version>1.0.0</version>
                <type>jar</type>
                <overWrite>false</overWrite>
              </artifactItem>
            </artifactItems>
            <outputDirectory>${project.build.directory}/deploy-staging</outputDirectory>
            <overWriteReleases>false</overWriteReleases>
          </configuration>
        </execution>
      </executions>
    </plugin>
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-antrun-plugin</artifactId>
      <executions>
        <execution>
          <id>ftp</id>
          <phase>install</phase>
          <configuration>
            <tasks>
              <ftp action="send" 
                  server="${ftp.host}" remotedir="${ftp.remotedir}" 
                  userid="${ftp.userid}" password="${ftp.password}" 
                  depends="${ftp.depends}" verbose="${ftp.verbose}">
                <fileset dir="${project.build.directory} includes="deploy-staging/"/>
              </ftp>
            </tasks>
          </configuration>
          <goals>
            <goal>run</goal>
          </goals>
        </execution>
      </executions>
    </plugin>
    <properties>
      <!--if the properties differ between targets, qualify them with the artifact name-->
      <ftp.host>hostname</ftp.host>
      <ftp.remotedir>/opt/path/to/install</ftp.remotedir>
      <ftp.userid>user</ftp.userid>
      <ftp.password>mypassword</ftp.password>
      <ftp.depends>yes</ftp.depends>
      <ftp.verbose>no</ftp.verbose>          
    </properties>
  </profile>
</profiles>  
Rich Seller
  • 83,208
  • 23
  • 172
  • 177
  • This should work. The profile is what I've been missing all together. Here's another idea that should help with "hiccups" - instead of defining subprojects as modules define these as dependencies in the parent POM and extract artifact JARs to their destinations using dependency plugin. Will that work? – Bostone Sep 10 '09 at 17:09
  • I forgot to mention that in our enterprise we use NAC to be able to map/access any server just as mapped directory (both Win/*NIX) so there's really no need for FTP or SSH – Bostone Sep 10 '09 at 17:24
  • well in that case, the dependency-plugin's copy goal is all you need, just modify th outputDirectory property to the path of the target – Rich Seller Sep 10 '09 at 17:45
  • Fantastic! Thanks for your time and advise Rich, once I'll finish doing what I'm doing I'll update the post, but I'm accepting your suggestion as a solution – Bostone Sep 10 '09 at 19:26
  • Nothing like executing scripts using an XML - shorn scrotum :( – AlikElzin-kilaka Dec 31 '14 at 09:16
5

Below POM will help to copy jar's file from project build directory to remote SFTP/FTP server.

  1. Use command mvn install -Dftp.password=password

Since I want to pass password from command prompt for security reason, I have used -Dftp.password=password After execution of above command all the jar files from maven project target folder will be deployed in MAVEN folder on server.com

    <plugin>          <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-antrun-plugin</artifactId>

    <executions>
        <execution>
            <id>ftp</id>
            <phase>install</phase>
            <configuration>
                <tasks>
                    <scp todir="user@server.com:/MAVEN/"
                        sftp="true" port="22" trust="true" password="${ftp.password}"
                        failonerror="false" verbose="true" passphrase="">
                        <fileset dir="${project.build.directory}">
                            <include name="*.jar" />
                        </fileset>
                    </scp>
                </tasks>
            </configuration>
            <goals>
                <goal>run</goal>
            </goals>
        </execution>
    </executions>
    <dependencies>
        <dependency>
            <groupId>org.apache.ant</groupId>
            <artifactId>ant-jsch</artifactId>
            <version>1.9.4</version>
        </dependency>
    </dependencies>

</plugin>
Vinayak Dornala
  • 1,609
  • 1
  • 21
  • 27
2

Does not work without passphrase.

    <profile>
        <id>publish</id>
        <build>
            <plugins>
                <plugin>
                    <artifactId>maven-antrun-plugin</artifactId>
                    <executions>
                        <execution>
                            <id>scp</id>
                            <phase>deploy</phase>
                            <configuration>
                                <tasks>
                                    <scp todir="user@host:some/remote/dir"
                                         sftp="true"
                                         keyfile="${user.home}/.ssh/devel-deploy.id_dsa"
                                         failonerror="false"
                                         verbose="true"
                                         passphrase="nopass"
                                    >
                                        <fileset dir="${project.build.directory}">
                                            <include
                                                name="${project.build.finalName}.${project.packaging}"/>
                                        </fileset>
                                    </scp>
                                </tasks>
                            </configuration>
                            <goals>
                                <goal>run</goal>
                            </goals>
                        </execution>
                    </executions>
                    <dependencies>
                        <dependency>
                            <groupId>org.apache.ant</groupId>
                            <artifactId>ant-jsch</artifactId>
                            <version>1.9.4</version>
                        </dependency>
                    </dependencies>
                </plugin>
            </plugins>
        </build>
    </profile>

However, my favourite is

    <profile>
        <id>upload-devel</id>
        <build>
            <plugins>
                <plugin>
                    <artifactId>maven-antrun-plugin</artifactId>
                    <executions>
                        <execution>
                            <id>upload-devel</id>
                            <phase>deploy</phase>
                            <configuration>
                                <target>
                                    <exec executable="rsync" failonerror="false">
                                        <arg value="-aiz" />
                                        <arg value="${project.build.directory}/${project.artifactId}.${project.packaging}" />
                                        <arg value="user@host:some/remote/dir/." />
                                    </exec>
                                </target>
                            </configuration>
                            <goals>
                                <goal>run</goal>
                            </goals>
                        </execution>
                    </executions>
                </plugin>
            </plugins>
        </build>
    </profile>

though I don't know how compatible that is over different platforms.

user1050755
  • 11,218
  • 4
  • 45
  • 56
  • To get `rsync` to work in this way I had to add `1.8` after `maven-antrun-plugin`. Without that it didn't actually execute the command, even though it did print `[INFO] Executed tasks`. – Tom Jan 25 '17 at 15:26
0

I would look at using the maven-assembly-plugin to do this.

Something like this can be used to grab the files from the child projects and stuff them in output directories.

<assembly>
  <id>xyzzy</id>
  <formats>
   <format>zip</format>
  </formats>
  <fileSets>
   <fileSet>
      <directory>../subproject1/target/</directory>
      <outputDirectory>/foo</outputDirectory>
      <includes>
        <include>*.jar</include>
      </includes>
   </fileSet>
   <fileSet>
      <directory>../subproject1/target/html-output/</directory>
      <outputDirectory>/foo</outputDirectory>
      <includes>
        <include>*.html</include>
        <include>*.js</include>
        <include>*.css</include>
      </includes>
    </fileSet>     
    <fileSet>
      <directory>../subproject2/target/</directory>
      <outputDirectory>/bar</outputDirectory>
      <includes>
        <include>**/**</include>
      </includes>
      <excludes>
        <exclude>**/*.exclude-this</exclude>
      </excludes>
    </fileSet>
  </fileSets> 
</assembly>
sal
  • 23,373
  • 15
  • 66
  • 85
  • 1
    As I understand - assembly plugin will create some sort of archive (jar, war, zip) - that doesn't work for me - I need actual files/folders to be copied to the destination. Actually - maven-dependency plugin seems to be better suitable since it can "unpack" but then I'm stuck since I cannot define subprojects both as modules and dependencies. So the route I'm going is actually after Rich's suggestions - define profile and copy files during install phase. – Bostone Sep 10 '09 at 21:18
0

Maven is not really designed to deploy jars to a remote location; its main use is compiling and packaging artifacts. The assembly and dependency targets are primarily used to gather dependencies and files to package into an artifact.

Having said that, maven does have a deploy goal which uses a component called wagon. This is primarily intended to deploy to a maven repository. There is a plugin called Cargo that can be used to deploy artifacts to a remote server, but that doesn't explode the jar contents by itself (it relies on the target app server to do all that). You might be able to extend the Maven Wagon functionality yourself.

Also, it is possible to package a custom lifecycle, but that is getting into some pretty low level maven mojo (pun intended).

Ken Liu
  • 22,503
  • 19
  • 75
  • 98