View Javadoc
1   /*
2    * Copyright 2001-2005 The Apache Software Foundation.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.codehaus.plexus.archiver.dir;
17  
18  import java.io.File;
19  import java.io.IOException;
20  import java.util.ArrayList;
21  import java.util.List;
22  import org.codehaus.plexus.archiver.AbstractArchiver;
23  import org.codehaus.plexus.archiver.ArchiveEntry;
24  import org.codehaus.plexus.archiver.ArchiverException;
25  import org.codehaus.plexus.archiver.ResourceIterator;
26  import org.codehaus.plexus.archiver.exceptions.EmptyArchiveException;
27  import org.codehaus.plexus.archiver.util.ArchiveEntryUtils;
28  import org.codehaus.plexus.archiver.util.ResourceUtils;
29  import org.codehaus.plexus.components.io.attributes.SymlinkUtils;
30  import org.codehaus.plexus.components.io.functions.SymlinkDestinationSupplier;
31  import org.codehaus.plexus.components.io.resources.PlexusIoResource;
32  
33  /**
34   * A plexus archiver implementation that stores the files to archive in a directory.
35   */
36  public class DirectoryArchiver
37      extends AbstractArchiver
38  {
39  
40      private final List<Runnable> directoryChmods = new ArrayList<Runnable>();
41  
42      public void resetArchiver()
43          throws IOException
44      {
45          cleanUp();
46      }
47  
48      @Override
49      public void execute()
50          throws ArchiverException, IOException
51      {
52          // Most of this method was copied from org.codehaus.plexus.archiver.tar.TarArchiver
53          // and modified to store files in a directory, not a tar archive.
54          final ResourceIterator iter = getResources();
55          if ( !iter.hasNext() )
56          {
57              throw new EmptyArchiveException( "archive cannot be empty" );
58          }
59  
60          final File destDirectory = getDestFile();
61          if ( destDirectory == null )
62          {
63              throw new ArchiverException( "You must set the destination directory." );
64          }
65          if ( destDirectory.exists() && !destDirectory.isDirectory() )
66          {
67              throw new ArchiverException( destDirectory + " is not a directory." );
68          }
69          if ( destDirectory.exists() && !destDirectory.canWrite() )
70          {
71              throw new ArchiverException( destDirectory + " is not writable." );
72          }
73  
74          getLogger().info( "Copying files to " + destDirectory.getAbsolutePath() );
75  
76          try
77          {
78              while ( iter.hasNext() )
79              {
80                  final ArchiveEntry f = iter.next();
81                  // Check if we don't add directory file in itself
82                  if ( ResourceUtils.isSame( f.getResource(), destDirectory ) )
83                  {
84                      throw new ArchiverException( "The destination directory cannot include itself." );
85                  }
86                  String fileName = f.getName();
87                  final String destDir = destDirectory.getCanonicalPath();
88                  fileName = destDir + File.separator + fileName;
89                  PlexusIoResource resource = f.getResource();
90                  if ( resource instanceof SymlinkDestinationSupplier )
91                  {
92                      String dest = ( (SymlinkDestinationSupplier) resource ).getSymlinkDestination();
93                      File target = new File( dest );
94                      SymlinkUtils.createSymbolicLink( new File( fileName ), target );
95                  }
96                  else
97                  {
98                      copyFile( f, fileName );
99                  }
100             }
101 
102             for ( Runnable directoryChmod : directoryChmods )
103             {
104                 directoryChmod.run();
105             }
106             directoryChmods.clear();
107         }
108         catch ( final IOException ioe )
109         {
110             final String message = "Problem copying files : " + ioe.getMessage();
111             throw new ArchiverException( message, ioe );
112         }
113     }
114 
115     /**
116      * Copies the specified file to the specified path, creating any ancestor directory structure as necessary.
117      *
118      * @param entry The file to copy (IOException will be thrown if this does not exist)
119      * @param vPath The fully qualified path to copy the file to.
120      *
121      * @throws ArchiverException If there is a problem creating the directory structure
122      * @throws IOException If there is a problem copying the file
123      */
124     protected void copyFile( final ArchiveEntry entry, final String vPath )
125         throws ArchiverException, IOException
126     {
127         // don't add "" to the archive
128         if ( vPath.length() <= 0 )
129         {
130             return;
131         }
132 
133         final PlexusIoResource in = entry.getResource();
134         final File outFile = new File( vPath );
135 
136         final long inLastModified = in.getLastModified();
137         final long outLastModified = outFile.lastModified();
138         if ( ResourceUtils.isUptodate( inLastModified, outLastModified ) )
139         {
140             return;
141         }
142 
143         if ( !in.isDirectory() )
144         {
145             if ( !outFile.getParentFile().exists() )
146             {
147                 // create the parent directory...
148                 if ( !outFile.getParentFile().mkdirs() )
149                 {
150                     // Failure, unable to create specified directory for some unknown reason.
151                     throw new ArchiverException( "Unable to create directory or parent directory of " + outFile );
152                 }
153             }
154             ResourceUtils.copyFile( entry.getInputStream(), outFile );
155 
156             setFileModes( entry, outFile, inLastModified );
157         }
158         else
159         { // file is a directory
160             if ( outFile.exists() )
161             {
162                 if ( !outFile.isDirectory() )
163                 {
164                     // should we just delete the file and replace it with a directory?
165                     // throw an exception, let the user delete the file manually.
166                     throw new ArchiverException(
167                         "Expected directory and found file at copy destination of " + in.getName() + " to " + outFile );
168                 }
169             }
170             else if ( !outFile.mkdirs() )
171             {
172                 // Failure, unable to create specified directory for some unknown reason.
173                 throw new ArchiverException( "Unable to create directory or parent directory of " + outFile );
174             }
175 
176             directoryChmods.add( new Runnable()
177             {
178 
179                 @Override
180                 public void run()
181                 {
182                     setFileModes( entry, outFile, inLastModified );
183                 }
184 
185             } );
186         }
187 
188     }
189 
190     private void setFileModes( ArchiveEntry entry, File outFile, long inLastModified )
191     {
192         if ( !isIgnorePermissions() )
193         {
194             ArchiveEntryUtils.chmod( outFile, entry.getMode() );
195         }
196 
197         outFile.setLastModified( inLastModified == PlexusIoResource.UNKNOWN_MODIFICATION_DATE
198                                      ? System.currentTimeMillis()
199                                      : inLastModified );
200     }
201 
202     @Override
203     protected void cleanUp()
204         throws IOException
205     {
206         super.cleanUp();
207         setIncludeEmptyDirs( false );
208         setIncludeEmptyDirs( true );
209     }
210 
211     @Override
212     protected void close()
213         throws IOException
214     {
215     }
216 
217     @Override
218     protected String getArchiveType()
219     {
220         return "directory";
221     }
222 
223 }