View Javadoc

1   /*
2    * Copyright (C) 2007-2008 JVending Masa
3    * Copyright (C) 2011 Jayway AB
4    *
5    * Licensed under the Apache License, Version 2.0 (the "License");
6    * you may not use this file except in compliance with the License.
7    * You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package com.jayway.maven.plugins.android.standalonemojos;
18  
19  import com.android.ddmlib.AdbCommandRejectedException;
20  import com.android.ddmlib.FileListingService;
21  import com.android.ddmlib.FileListingService.FileEntry;
22  import com.android.ddmlib.IDevice;
23  import com.android.ddmlib.SyncException;
24  import com.android.ddmlib.SyncService;
25  import com.android.ddmlib.TimeoutException;
26  import com.jayway.maven.plugins.android.AbstractAndroidMojo;
27  import com.jayway.maven.plugins.android.DeviceCallback;
28  import com.jayway.maven.plugins.android.common.DeviceHelper;
29  import com.jayway.maven.plugins.android.common.LogSyncProgressMonitor;
30  import com.jayway.maven.plugins.android.config.ConfigHandler;
31  import com.jayway.maven.plugins.android.config.ConfigPojo;
32  import com.jayway.maven.plugins.android.config.PullParameter;
33  import com.jayway.maven.plugins.android.configuration.Pull;
34  import org.apache.commons.io.FilenameUtils;
35  import org.apache.commons.lang.StringUtils;
36  import org.apache.maven.plugin.MojoExecutionException;
37  import org.apache.maven.plugin.MojoFailureException;
38  
39  import java.io.File;
40  import java.io.IOException;
41  
42  /**
43   * Copy file or directory from all the attached (or specified)
44   * devices/emulators.
45   *
46   * @author Manfred Moser <manfred@simpligility.com>
47   * @goal pull
48   * @requiresProject false
49   */
50  public class PullMojo extends AbstractAndroidMojo
51  {
52  
53      /**
54       * <p>The configuration for the pull goal can be set up in the plugin configuration in the pom file as:</p>
55       * <pre>
56       * &lt;pull&gt;
57       *     &lt;source&gt;path&lt;/source&gt;
58       *     &lt;destination&gt;path&lt;/destination&gt;
59       * &lt;/pull&gt;
60       * </pre>
61       * <p>The parameters can also be configured as property in the pom or settings file
62       * <pre>
63       * &lt;properties&gt;
64       *     &lt;android.pull.source&gt;pathondevice&lt;/android.pull.source&gt;
65       *     &lt;android.pull.destination&gt;path&lt;/android.pull.destination&gt;
66       * &lt;/properties&gt;
67       * </pre>
68       * or from command-line with parameter <code>-Dandroid.pull.source=path</code>
69       * and <code>-Dandroid.pull.destination=path</code>.</p>
70       *
71       * @parameter
72       */
73      @ConfigPojo
74      private Pull pull;
75  
76      /**
77       * The path of the source file or directory on the emulator/device.
78       *
79       * @parameter expression="${android.pull.source}"
80       */
81      private String pullSource;
82  
83      @PullParameter( required = true )
84      private String parsedSource;
85  
86      /**
87       * The path of the destination to copy the file to.
88       * <p/>
89       * If destination ends with {@link File#separator}, it is supposed to be a
90       * directory. Therefore the source - whether it refers to a file or
91       * directory - will be copied into the destination directory.
92       * <p/>
93       * If destination does not end with {@link File#separator}, the last path
94       * segment will be assumed as the new file or directory name (depending on
95       * the type of source).
96       * <p/>
97       * Any missing directories will be created.
98       *
99       * @parameter expression="${android.pull.destination}"
100      */
101     private String pullDestination;
102 
103     @PullParameter( required = true )
104     private String parsedDestination;
105 
106     public void execute() throws MojoExecutionException, MojoFailureException
107     {
108 
109         ConfigHandler configHandler = new ConfigHandler( this );
110         configHandler.parseConfiguration();
111 
112         doWithDevices( new DeviceCallback()
113         {
114             public void doWithDevice( final IDevice device ) throws MojoExecutionException
115             {
116                 String deviceLogLinePrefix = DeviceHelper.getDeviceLogLinePrefix( device );
117 
118                 // message will be set later according to the processed files
119                 String message = "";
120                 try
121                 {
122                     SyncService syncService = device.getSyncService();
123                     FileListingService fileListingService = device.getFileListingService();
124 
125                     FileEntry sourceFileEntry = getFileEntry( parsedSource, fileListingService );
126 
127                     if ( sourceFileEntry.isDirectory() )
128                     {
129                         // pulling directory
130                         File destinationDir = new File( parsedDestination );
131                         if ( ! destinationDir.exists() )
132                         {
133                             getLog().info( "Creating destination directory " + destinationDir );
134                             destinationDir.mkdirs();
135                             destinationDir.mkdir();
136                         }
137                         String destinationDirPath = destinationDir.getAbsolutePath();
138 
139                         FileEntry[] fileEntries;
140                         if ( parsedDestination.endsWith( File.separator ) )
141                         {
142                             // pull source directory directly
143                             fileEntries = new FileEntry[]{ sourceFileEntry };
144                         }
145                         else
146                         {
147                             // pull the children of source directory only
148                             fileEntries = fileListingService.getChildren( sourceFileEntry, true, null );
149                         }
150 
151                         message = deviceLogLinePrefix + "Pull of " + parsedSource + " to " + destinationDirPath 
152                                 + " from ";
153 
154                         syncService.pull( fileEntries, destinationDirPath, new LogSyncProgressMonitor( getLog() ) );
155                     }
156                     else
157                     {
158                         // pulling file
159                         File parentDir = new File( FilenameUtils.getFullPath( parsedDestination ) );
160                         if ( ! parentDir.exists() )
161                         {
162                             getLog().info( deviceLogLinePrefix + "Creating destination directory " + parentDir );
163                             parentDir.mkdirs();
164                         }
165 
166                         String destinationFileName;
167                         if ( parsedDestination.endsWith( File.separator ) )
168                         {
169                             // keep original filename
170                             destinationFileName = FilenameUtils.getName( parsedSource );
171                         }
172                         else
173                         {
174                             // rename filename
175                             destinationFileName = FilenameUtils.getName( parsedDestination );
176                         }
177 
178                         File destinationFile = new File( parentDir, destinationFileName );
179                         String destinationFilePath = destinationFile.getAbsolutePath();
180                         message = deviceLogLinePrefix + "Pull of " + parsedSource + " to " + destinationFilePath 
181                                 + " from " + DeviceHelper.getDescriptiveName( device );
182 
183                         syncService.pullFile( sourceFileEntry, destinationFilePath,
184                                 new LogSyncProgressMonitor( getLog() ) );
185                     }
186 
187                     getLog().info( message + " successful." );
188                 }
189                 catch ( SyncException e )
190                 {
191                     throw new MojoExecutionException( message + " failed.", e );
192                 }
193                 catch ( IOException e )
194                 {
195                     throw new MojoExecutionException( message + " failed.", e );
196                 }
197                 catch ( TimeoutException e )
198                 {
199                     throw new MojoExecutionException( message + " failed.", e );
200                 }
201                 catch ( AdbCommandRejectedException e )
202                 {
203                     throw new MojoExecutionException( message + " failed.", e );
204                 }
205             }
206         } );
207     }
208 
209     /**
210      * Retrieves the corresponding {@link FileEntry} on the emulator/device for
211      * a given file path.
212      * <p/>
213      * If the file path starts with the symlink "sdcard", it will be resolved
214      * statically to "/mnt/sdcard".
215      *
216      * @param filePath           path to file or directory on device or emulator
217      * @param fileListingService {@link FileListingService} for retrieving the
218      *                           {@link FileEntry}
219      * @return a {@link FileEntry} object for the given file path
220      * @throws MojoExecutionException if the file path could not be found on the device
221      */
222     private FileEntry getFileEntry( String filePath, FileListingService fileListingService )
223             throws MojoExecutionException
224     {
225         // static resolution of symlink
226         if ( filePath.startsWith( "/sdcard" ) )
227         {
228             filePath = "/mnt" + filePath;
229         }
230 
231         String[] destinationPathSegments = StringUtils.split( filePath, "/" );
232 
233         FileEntry fileEntry = fileListingService.getRoot();
234         for ( String destinationPathSegment : destinationPathSegments )
235         {
236             // build up file listing cache
237             fileListingService.getChildren( fileEntry, true, null );
238 
239             fileEntry = fileEntry.findChild( destinationPathSegment );
240             if ( fileEntry == null )
241             {
242                 throw new MojoExecutionException( "Cannot execute goal: " + filePath + " does not exist on device." );
243             }
244         }
245         return fileEntry;
246     }
247 }