ChatGPT解决这个技术问题 Extra ChatGPT

Best practices for copying files with Maven

I have config files and various documents that I want to copy from the dev environment to the dev-server directory using Maven2. Strangely, Maven does not seem strong at this task.

Some of the options:

Simple use a copy task in Maven

Use the Ant plugin to execute copy from Ant. Construct an artifact of type zip, alongside the "main" artifact of the POM which is usually of type jar, then unpack that artifact from the repository into the target directory. maven-resources plugin, as mentioned below. Maven Assembly plugin -- but this seems to require a lot of manual definitions, when I want to do things simply and "conventionally." This page even shows how to build a plugin to do copying! maven-upload plugin, as mentioned below. maven-dependency-plugin with copy, as mentioned below.

Construct an artifact of type zip, alongside the "main" artifact of the POM which is usually of type jar, then unpack that artifact from the repository into the target directory.

maven-resources plugin, as mentioned below.

Maven Assembly plugin -- but this seems to require a lot of manual definitions, when I want to do things simply and "conventionally."

This page even shows how to build a plugin to do copying!

maven-upload plugin, as mentioned below.

maven-dependency-plugin with copy, as mentioned below.

All these seem needlessly ad hoc: Maven is supposed to excel at doing these standard tasks without fuss and bother.

Any advice?

Maven is build around the idea of a life cycle with phases, the copy random files to a remote server task does not really fit into this. Always think of your project as a whole.
"All these seem needlessly ad hoc: Maven is supposed to excel at doing these standard tasks without fuss and bother. " What you are doing isn't a standard task, per se. If your artifact was a war/ear, then this would be as simple as using the cargo plugin( cargo.codehaus.org/Maven2+plugin#Maven2plugin-get… ). What you are describing sounds highly specific to how you are doing deployments and not standard java application container deployments. Maven is not really geared to handle deploy time activities to live servers - it's geared more to build/dev activities.
@André: I hear that argument over and over again, but sorry, that's BS. There's nothing wrong with thinking of the project as a whole, but part of any decent build system should be functionality that lets me achieve task X in a straight forward way, such as copying files, and Maven cannot do that. There's a reason why so many projects popped up lately that embrace the build-scripts-are-code paradigm (like Gradle, SBT, or Buildr).
I would recommend having a pom.xml for building the artifacts and another for deploying a given artifact.
All suggestions above still don't seem to allow me to copy a specific file from a different project/artifact into a maven project. I have some files under src/main/folder in an artifact that becomes a jar and I have tried using the dependency-copy maven plugin however I haven't found a way to say which files I want to copy and I get the whole jar file in the assembly file all the time. All the other suggestions here, like resources, doesn't seem to allow me to specify an artifact rather than the resources inside the project

k
kay
<build>
    <plugins>
        ...
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-resources-plugin</artifactId>
            <version>2.3</version>
        </plugin>
    </plugins>
    <resources>
        <resource>
            <directory>src/main/java</directory>
            <includes>
                <include> **/*.properties</include>
            </includes>
        </resource>
    </resources>
    ...
</build>

Thanks @Peter, that was useful. I now use the resources-plugin copy-resources goal instead of antrun. The latter is actually much simpler and intuitive to define, but I couldn't get it (version 1.3) to pass all Maven custom properties (defined in section) to antrun, so I switched to resources-plugin.
I used to think this was the correct answer ... until I realized that the resources plugin doesn't have a skip configuration. Antrun is the way to go.
It shouldn't be difficult to create a skip profile. Haven't used antrun, so I can't say which is easier/better
j
jaw

Don't shy away from the Antrun plugin. Just because some people tend to think that Ant and Maven are in opposition, they are not. Use the copy task if you need to perform some unavoidable one-off customization:

<project>
  [...]
  <build>
    <plugins>
      [...]
      <plugin>
        <artifactId>maven-antrun-plugin</artifactId>
        <executions>
          <execution>
            <phase>deploy</phase>
            <configuration>
              <target>

                <!--
                  Place any Ant task here. You can add anything
                  you can add between <target> and </target> in a
                  build.xml.
                -->

              </target>
            </configuration>
            <goals>
              <goal>run</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
  [...]
</project>

In answering this question, I'm focusing on the details of what you asked. How do I copy a file? The question and the variable name lead me to a larger questions like: "Is there a better way to deal with server provisioning?" Use Maven as a build system to generate deployable artifact, then perform these customizations either in separate modules or somewhere else entirely. If you shared a bit more of your build environment, there might be a better way - there are plugins to provision a number of servers. Could you attach an assembly that is unpacked in the server's root? What server are you using?

Again, I'm sure there's a better way.


Is the task descriptor now deprecated?
@Matt Yes, the task parameter is now deprecated (Antrun Plugin). You should use target instead (since 1.5). Unfortuately there are examples which mix this up; e.g. target parameter and version < 1.5.
How can this be the accepted answer? Definitely there should be a change request to maven to make copy a simple thing.
O
Ondra Žižka

In order to copy a file use:

        <plugin>
            <artifactId>maven-resources-plugin</artifactId>
            <version>3.1.0</version>
            <executions>
                <execution>
                    <id>copy-resource-one</id>
                    <phase>install</phase>
                    <goals>
                        <goal>copy-resources</goal>
                    </goals>

                    <configuration>
                        <outputDirectory>${basedir}/destination-folder</outputDirectory>
                        <resources>
                            <resource>
                                <directory>/source-folder</directory>
                                <includes>
                                    <include>file.jar</include>
                                </includes>
                            </resource>
                        </resources>
                    </configuration>
                </execution>
           </executions>
        </plugin>

In order to copy folder with sub-folders use next configuration:

           <configuration>
              <outputDirectory>${basedir}/target-folder</outputDirectory>
              <resources>          
                <resource>
                  <directory>/source-folder</directory>
                  <filtering>true</filtering>
                </resource>
              </resources>              
            </configuration>  

Filtering in Maven refers to string interpolation, so I'd omit <filtering> to prevent unwanted changes to e.g. script files which use ${...} variables.
j
james.garriss

For a simple copy-tasks I can recommend copy-rename-maven-plugin. It's straight forward and simple to use:

<project>
  ...
  <build>
    <plugins>
      <plugin>
        <groupId>com.coderplus.maven.plugins</groupId>
        <artifactId>copy-rename-maven-plugin</artifactId>
        <version>1.0</version>
        <executions>
          <execution>
            <id>copy-file</id>
            <phase>generate-sources</phase>
            <goals>
              <goal>copy</goal>
            </goals>
            <configuration>
              <sourceFile>src/someDirectory/test.environment.properties</sourceFile>
              <destinationFile>target/someDir/environment.properties</destinationFile>
            </configuration>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
</project>

If you would like to copy more than one file, replace the <sourceFile>...</destinationFile> part with

<fileSets>
  <fileSet>
    <sourceFile>src/someDirectory/test.environment.properties</sourceFile>
    <destinationFile>target/someDir/environment.properties</destinationFile>
  </fileSet>
  <fileSet>
    <sourceFile>src/someDirectory/test.logback.xml</sourceFile>
    <destinationFile>target/someDir/logback.xml</destinationFile>
  </fileSet>                
</fileSets>

Furthermore you can specify multiple executions in multiple phases if needed, the second goal is "rename", which simply does what it says while the rest of the configuration stays the same. For more usage examples refer to the Usage-Page.

Note: This plugin can only copy files, not directories. (Thanks to @james.garriss for finding this limitation.)


Though I like this plugin, it's astounding that it can't copy directories.
@james.garriss I wasn't aware of this limitation but unfortunately you're right. I'll edit this into my answer to maybe save some people the time finding this by themself.
It should be noted that the plugin cannot interpolate variables.I wanted to store the project version from the POM into various config files in the project using this plugin, but due to this limitation, this plugin is useless for me.
D
DwB

The maven dependency plugin saved me a lot of time fondling with ant tasks:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-dependency-plugin</artifactId>
    <executions>
        <execution>
            <id>install-jar</id>
            <phase>install</phase>
            <goals>
                <goal>copy</goal>
            </goals>
            <configuration>
                <artifactItems>
                    <artifactItem>
                        <groupId>...</groupId>
                        <artifactId>...</artifactId>
                        <version>...</version>
                    </artifactItem>
                </artifactItems>
                <outputDirectory>...</outputDirectory>
                <stripVersion>true</stripVersion>
            </configuration>
        </execution>
    </executions>
</plugin>

The dependency:copy is documentend, and has more useful goals like unpack.


I didn't use Ant for years, and I don't want to start doing it for such a simple thing. So thanks for this answer.
K
Kyle Renfro

The ant solution above is easiest to configure, but I have had luck using the maven-upload-plugin from Atlassian. I was unable to find good documentation, here is how I use it:

<build>
  <plugin>
    <groupId>com.atlassian.maven.plugins</groupId>
    <artifactId>maven-upload-plugin</artifactId>
    <version>1.1</version>
    <configuration>
       <resourceSrc>
             ${project.build.directory}/${project.build.finalName}.${project.packaging}
       </resourceSrc>
       <resourceDest>${jboss.deployDir}</resourceDest>
       <serverId>${jboss.host}</serverId>
       <url>${jboss.deployUrl}</url>
     </configuration>
  </plugin>
</build>

The variables like "${jboss.host}" referenced above are defined in my ~/.m2/settings.xml and are activated using maven profiles. This solution is not constrained to JBoss, this is just what I named my variables. I have a profile for dev, test, and live. So to upload my ear to a jboss instance in test environment I would execute:

mvn upload:upload -P test

Here is a snipet from settings.xml:

<server>
  <id>localhost</id>
  <username>username</username>
  <password>{Pz+6YRsDJ8dUJD7XE8=} an encrypted password. Supported since maven 2.1</password>
</server>
...
<profiles>
  <profile>
    <id>dev</id>
    <properties>
      <jboss.host>localhost</jboss.host> 
      <jboss.deployDir>/opt/jboss/server/default/deploy/</jboss.deployDir>
      <jboss.deployUrl>scp://root@localhost</jboss.deployUrl>
    </properties>
  </profile>
  <profile>
    <id>test</id>
    <properties>
       <jboss.host>testserver</jboss.host>
       ...

Notes: The Atlassian maven repo that has this plugin is here: https://maven.atlassian.com/public/

I recommend downloading the sources and looking at the documentation inside to see all the features the plugin provides.

`


s
siddhadev

Well, maven is not supposed to be good in doing fine granular tasks, it is not a scripting language like bash or ant, it is rather declarative - you say - i need a war, or an ear, and you get it. However if you need to customize how the war or ear should look like inside, you have a problem. It is just not procedural like ant, but declarative. This have some pros in the beginning, and could have a lot of cons at the end.

I guess the initial concept was to have fine plugins, that "just work" but the reality is different if you do non-standard stuff.

If you however put enough effort in your poms and few custom plugins, you'll get a much better build environment as with ant for example (depends on you project of course, but it gets more and more true for bigger projects).


a
azerole

I've had very good experience with copy-maven-plugin. It has a much more convenient and concise syntax in comparison to maven-resources-plugin.


Unfortunately, copy-maven-plugin is not compatible with maven 3.1.x
The issue tracking the compatibility with maven 3.1 is there: github.com/evgeny-goldin/maven-plugins/issues/10
Forget about this plugin... Look for its forks
ᄂ ᄀ

A generic way to copy arbitrary files is to utilize Maven Wagon transport abstraction. It can handle various destinations via protocols like file, HTTP, FTP, SCP or WebDAV.

There are a few plugins that provide facilities to copy files through the use of Wagon. Most notable are:

Out-of-the-box Maven Deploy Plugin There is the deploy-file goal. It it quite inflexible but can get the job done: mvn deploy:deploy-file -Dfile=/path/to/your/file.ext -DgroupId=foo -DartifactId=bar -Dversion=1.0 -Durl= -DgeneratePom=false Significant disadvantage to using Maven Deploy Plugin is that it is designated to work with Maven repositories. It assumes particular structure and metadata. You can see that the file is placed under foo/bar/1.0/file-1.0.ext and checksum files are created. There is no way around this.

Wagon Maven Plugin Use the upload-single goal: mvn org.codehaus.mojo:wagon-maven-plugin:upload-single -Dwagon.fromFile=/path/to/your/file.ext -Dwagon.url= The use of Wagon Maven Plugin for copying is straightforward and seems to be the most versatile.


In the examples above <url> can be of any supported protocol. See the list of existing Wagon Providers. For example

copying file locally: file:///copy/to

copying file to remote host running SSH: scp://host:22/copy/to


The examples above pass plugin parameters in the command line. Alternatively, plugins can be configured directly in POM. Then the invocation will simply be like mvn deploy:deploy-file@configured-execution-id. Or it can be bound to particular build phase.


Please note that for protocols like SCP to work you will need to define an extension in your POM:

<build>
  [...]
  <extensions>
    <extension>
      <groupId>org.apache.maven.wagon</groupId>
      <artifactId>wagon-ssh</artifactId>
      <version>2.12</version>
    </extension>
  </extensions>


If the destination you are copying to requires authentication, credentials can be provided via Server settings. repositoryId/serverId passed to the plugins must match the server defined in the settings.


w
whaley

I can only assume that your ${project.server.config} property is something custom defined and is outside of the standard directory layout.

If so, then I'd use the copy task.


Let's say I take care to put the files into the standard directory layout. Can Maven copy them to the target as-is, not in a zip/jar?
B
Brian Fox

Another way is to bundle these things into an artifact using the assembly plugin. Then you can use the dependency plugin to unpack these files where you want. There are also copy goals in the dependency plugin to copy artifacts.


G
Gerold Broser

I was able to piece together a number of different sources for this answer:

...
<repository>
    <id>atlassian</id>
    <name>Atlassian Repo</name>
    <url>https://maven.atlassian.com/content/repositories/atlassian-public</url>
</repository>
...
<dependency>
    <groupId>com.atlassian.maven.plugins</groupId>
    <artifactId>maven-upload-plugin</artifactId>
    <version>1.1</version>
</dependency>
...
<plugin>
    <groupId>com.atlassian.maven.plugins</groupId>
    <artifactId>maven-upload-plugin</artifactId>
    <version>1.1</version>
    <configuration>
        <serverId>jira-repo</serverId>
        <resourceSrc>
            ${project.build.directory}/${project.build.finalName}.${project.packaging}
        </resourceSrc>
        <resourceDest>opt/jira/webapps</resourceDest> <!-- note: no leading slash -->
        <url>scp://root@jira</url>
    </configuration>
</plugin>
...

From ~/.m2/settings.xml:

...
<servers>
  <server>
    <id>jira-repo</id>
    <username>myusername</username>
    <password>mypassword</password>
  </server>
</servers>
...

Then run the command: (the -X is for debug)

mvn -X upload:upload


B
Binita Bharati

If someone wants total control over the path of the source and destination paths, then using maven-antrun-plugin's copy task is the best option. This approach will allow you to copy between any paths on the system, irrespective of the concerned paths being within the mvn project or not. I had a situation where I had to do some unusual stuff like copy generated source files from target directory back to the src directory for further processing. In my situation, this was the only option that worked without fuss. Sample code snippet from pom.xml:

<plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-antrun-plugin</artifactId>
        <version>1.8</version>
        <executions>
            <execution>
                <phase>process-resources</phase>
                <configuration>
                <tasks>
                    <copy file="${basedir}/target/myome/minifyJsSrcDir/myome.min.js" todir="${basedir}/src/main/webapp/app/minifyJsSrcDir"/>
                </tasks>
                </configuration>
                <goals>
                <goal>run</goal>
                </goals>
            </execution>
        </executions>
</plugin>

A
Alireza

To summarize some of the fine answers above: Maven is designed to build modules and copy the results to a Maven repository. Any copying of modules to a deployment/installer-input directory must be done outside the context of Maven's core functionality, e.g. with the Ant/Maven copy command.


Ant belongs to Maven's core functionality and so does Wagon (though the plugin around it is not an official Maven core plugin).