0
votes

I have a source directory that contains some file updates that I need to copy in to my web app dir. Before I copy the files into place I need to backup each original that is going to be over written to backup. I can't backup the whole web app to the backup location as that would be 00's of files. I need to keep the dir structure (no flattening) & all 3 locations have the same structure.

My curent idea is to do this in 2 phases. Backup then update. The update is trivial (copy & fileset). The backup is causing me the problems as I only want to backup the files that will be updated.

Below is my current iteration (which doesn't work)... Use a copy with todir as the backup location, then have a fileset that searches the source files so I have the set of changing files. What I obviously need to do is change the paths of the files returned by the fileset so that they reference the existing files rather than the new ones & that's where I'm stuck. Anyone know how to change the paths of files returned by fileset? I use pathconvert below which I know is wrong, but I left it in to show the conversion I'm after - anyone know how to do this?? Or a better way maybe!!

${backup.dir} --> root of file backup
${console.war} --> location of existing files - the top of the application dir
${update.dir} --> root of updated files

<copy todir="${backup.dir}/app/console.war">
    <fileset dir="${update.dir}/app/console.war" includes="**" id="id.update.files"/>
    <pathconvert refid="id.update.files" property="custom.file.target">
        <map from="${update.dir}/app/console.war" to="${console.war}"/>
    </pathconvert>
</copy>
2
Maybe you should use a tool like rsync, which is designed for syncing files and doing backups. If you still want everything controled in an Ant script, you can just use the exec task to invoke rsync to do the backups before copying over updated files. If you adverse to using rsync, have a look at the sync task for performing the backup before doing the update.ewh

2 Answers

0
votes

Worked it out in the end. Had to use for from ant-contrib, but that's ok for this site. The key was using with and from then on my pathconvert's were ok. Below is the working code for reference - I'm sure it could be sharper, but it's working.

    <for param="source.filename">
        <path>
            <fileset dir="${update.dir}" id="id.src.files">
                <include name="**"/>
            </fileset>
        </path>

        <sequential>
            <!-- using the source.filename, build the target.filename -->
            <local name="target.filename"/>
            <local name="target.dirname"/>
            <pathconvert property="target.filename">
                <file file="@{source.filename}" />
                <map from="${update.dir}" to="${console.war}" />
            </pathconvert>              
            <dirname property="target.dirname" file="${target.filename}"/>

            <!-- using the source filename build the backup filename -->
            <local name="backup.filename"/>
            <pathconvert property="backup.filename">
                <file file="@{source.filename}" />
                <map from="${update.dir}" to="${backup.dir}" />
            </pathconvert>              

            <if>
                <available file="${target.filename}"/>
                <then>
                    <copy tofile="${backup.filename}">
                        <filelist dir="${target.dirname}">
                            <file name="${target.filename}"/>
                        </filelist>
                    </copy>
                </then>
            </if>
            <copy file="@{source.filename}" tofile="${target.filename}"/>
            <echo message=""/>

        </sequential>
    </for>
0
votes

You might want to try the following:

<copy todir="${backup.dir}/app/console.war">
  <fileset dir="${console.war}">
    <and>
      <different targetdir="${update.dir}/app/console.war"
          ignorecontents="true" ignorefiletimes="false"/>
      <not>
        <depend targetdir="${update.dir}/app/console.war"/>
      </not>
    </and>
  </fileset>
</copy>

Here, and, different, not, and depend are selectors. The above code selects those files from the fileset which are different to their corresponding files in the update directory (with respect to file size and modification time) except those which are newer than their corresponding files in the update directory.

(There is a subtle difference between how the above code determines which files are out of date and how the copy task does this by default, but only for corresponding files with the same modification time yet different file lengths.)

The following code can be used to create a self-contained example build.xml to try out the above code:

<project name="backup">

  <property name="backup.dir" value="backup.dir"/>
  <property name="update.dir" value="update.dir"/>
  <property name="console.war" value="console.war"/>

  <delete dir="${backup.dir}/app/console.war"/>
  <delete dir="${update.dir}/app/console.war"/>
  <delete dir="${console.war}"/>
  <mkdir dir="${backup.dir}/app/console.war"/>
  <mkdir dir="${update.dir}/app/console.war"/>
  <mkdir dir="${console.war}"/>

  <touch datetime="03/27/2015 09:00 AM" file="${update.dir}/app/console.war/aaa"/>
  <touch datetime="03/27/2015 09:11 AM" file="${update.dir}/app/console.war/bbb"/>
  <touch datetime="03/27/2015 09:11 AM" file="${console.war}/aaa"/>
  <touch datetime="03/27/2015 09:11 AM" file="${console.war}/bbb"/>
  <touch datetime="03/21/2015 09:11 AM" file="${console.war}/ccc"/>
  <touch datetime="03/27/2015 09:22 AM" file="${update.dir}/app/console.war/ccc"/>
  <touch datetime="03/27/2015 09:22 AM" file="${update.dir}/app/console.war/ddd"/>

  <!-- insert above copy task -->

</project>

Verify the contents of the created backup.dir (file ccc). (The other files are ignored because aaa is newer, bbb is same age, and ddd is not yet there. Only ccc is out of date.)