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.simpligility.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.simpligility.maven.plugins.android.AbstractAndroidMojo;
27  import com.simpligility.maven.plugins.android.DeviceCallback;
28  import com.simpligility.maven.plugins.android.common.DeviceHelper;
29  import com.simpligility.maven.plugins.android.common.LogSyncProgressMonitor;
30  import com.simpligility.maven.plugins.android.config.ConfigHandler;
31  import com.simpligility.maven.plugins.android.config.ConfigPojo;
32  import com.simpligility.maven.plugins.android.config.PullParameter;
33  import com.simpligility.maven.plugins.android.configuration.Pull;
34  
35  import org.apache.commons.io.FilenameUtils;
36  import org.apache.commons.lang3.StringUtils;
37  import org.apache.maven.plugin.MojoExecutionException;
38  import org.apache.maven.plugin.MojoFailureException;
39  import org.apache.maven.plugins.annotations.Mojo;
40  import org.apache.maven.plugins.annotations.Parameter;
41  
42  import java.io.File;
43  import java.io.IOException;
44  
45  /**
46   * Copy file or directory from all the attached (or specified)
47   * devices/emulators.
48   *
49   * @author Manfred Moser - manfred@simpligility.com
50   */
51  @Mojo( name = "pull", requiresProject = false )
52  public class PullMojo extends AbstractAndroidMojo
53  {
54  
55      /**
56       * <p>The configuration for the pull goal can be set up in the plugin configuration in the pom file as:</p>
57       * <pre>
58       * &lt;pull&gt;
59       *     &lt;source&gt;path&lt;/source&gt;
60       *     &lt;destination&gt;path&lt;/destination&gt;
61       * &lt;/pull&gt;
62       * </pre>
63       * <p>The parameters can also be configured as property in the pom or settings file
64       * <pre>
65       * &lt;properties&gt;
66       *     &lt;android.pull.source&gt;pathondevice&lt;/android.pull.source&gt;
67       *     &lt;android.pull.destination&gt;path&lt;/android.pull.destination&gt;
68       * &lt;/properties&gt;
69       * </pre>
70       * or from command-line with parameter <code>-Dandroid.pull.source=path</code>
71       * and <code>-Dandroid.pull.destination=path</code>.</p>
72       */
73      @Parameter
74      @ConfigPojo
75      private Pull pull;
76  
77      /**
78       * The path of the source file or directory on the emulator/device.
79       */
80      @Parameter( property = "android.pull.source" )
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       * 
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       * 
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       * 
97       * Any missing directories will be created.
98       */
99      @Parameter( property = "android.pull.destination" )
100     private String pullDestination;
101 
102     @PullParameter( required = true )
103     private String parsedDestination;
104 
105     public void execute() throws MojoExecutionException, MojoFailureException
106     {
107 
108         ConfigHandler configHandler = new ConfigHandler( this, this.session, this.execution );
109         configHandler.parseConfiguration();
110 
111         doWithDevices( new DeviceCallback()
112         {
113             public void doWithDevice( final IDevice device ) throws MojoExecutionException
114             {
115                 String deviceLogLinePrefix = DeviceHelper.getDeviceLogLinePrefix( device );
116 
117                 // message will be set later according to the processed files
118                 String message = "";
119                 try
120                 {
121                     SyncService syncService = device.getSyncService();
122                     FileListingService fileListingService = device.getFileListingService();
123 
124                     FileEntry sourceFileEntry = getFileEntry( parsedSource, fileListingService );
125 
126                     if ( sourceFileEntry.isDirectory() )
127                     {
128                         // pulling directory
129                         File destinationDir = new File( parsedDestination );
130                         if ( ! destinationDir.exists() )
131                         {
132                             getLog().info( "Creating destination directory " + destinationDir );
133                             destinationDir.mkdirs();
134                             destinationDir.mkdir();
135                         }
136                         String destinationDirPath = destinationDir.getAbsolutePath();
137 
138                         FileEntry[] fileEntries;
139                         if ( parsedDestination.endsWith( File.separator ) )
140                         {
141                             // pull source directory directly
142                             fileEntries = new FileEntry[]{ sourceFileEntry };
143                         }
144                         else
145                         {
146                             // pull the children of source directory only
147                             fileEntries = fileListingService.getChildren( sourceFileEntry, true, null );
148                         }
149 
150                         message = deviceLogLinePrefix + "Pull of " + parsedSource + " to " + destinationDirPath 
151                                 + " from ";
152 
153                         syncService.pull( fileEntries, destinationDirPath, new LogSyncProgressMonitor( getLog() ) );
154                     }
155                     else
156                     {
157                         // pulling file
158                         File parentDir = new File( FilenameUtils.getFullPath( parsedDestination ) );
159                         if ( ! parentDir.exists() )
160                         {
161                             getLog().info( deviceLogLinePrefix + "Creating destination directory " + parentDir );
162                             parentDir.mkdirs();
163                         }
164 
165                         String destinationFileName;
166                         if ( parsedDestination.endsWith( File.separator ) )
167                         {
168                             // keep original filename
169                             destinationFileName = FilenameUtils.getName( parsedSource );
170                         }
171                         else
172                         {
173                             // rename filename
174                             destinationFileName = FilenameUtils.getName( parsedDestination );
175                         }
176 
177                         File destinationFile = new File( parentDir, destinationFileName );
178                         String destinationFilePath = destinationFile.getAbsolutePath();
179                         message = deviceLogLinePrefix + "Pull of " + parsedSource + " to " + destinationFilePath 
180                                 + " from " + DeviceHelper.getDescriptiveName( device );
181 
182                         syncService.pullFile( sourceFileEntry, destinationFilePath,
183                                 new LogSyncProgressMonitor( getLog() ) );
184                     }
185 
186                     getLog().info( message + " successful." );
187                 }
188                 catch ( SyncException e )
189                 {
190                     throw new MojoExecutionException( message + " failed.", e );
191                 }
192                 catch ( IOException e )
193                 {
194                     throw new MojoExecutionException( message + " failed.", e );
195                 }
196                 catch ( TimeoutException e )
197                 {
198                     throw new MojoExecutionException( message + " failed.", e );
199                 }
200                 catch ( AdbCommandRejectedException e )
201                 {
202                     throw new MojoExecutionException( message + " failed.", e );
203                 }
204             }
205         } );
206     }
207 
208     /**
209      * Retrieves the corresponding {@link FileEntry} on the emulator/device for
210      * a given file path.
211      * 
212      * If the file path starts with the symlink "sdcard", it will be resolved
213      * statically to "/mnt/sdcard".
214      *
215      * @param filePath           path to file or directory on device or emulator
216      * @param fileListingService {@link FileListingService} for retrieving the
217      *                           {@link FileEntry}
218      * @return a {@link FileEntry} object for the given file path
219      * @throws MojoExecutionException if the file path could not be found on the device
220      */
221     private FileEntry getFileEntry( String filePath, FileListingService fileListingService )
222             throws MojoExecutionException
223     {
224         // static resolution of symlink
225         if ( filePath.startsWith( "/sdcard" ) )
226         {
227             filePath = "/mnt" + filePath;
228         }
229 
230         String[] destinationPathSegments = StringUtils.split( filePath, "/" );
231 
232         FileEntry fileEntry = fileListingService.getRoot();
233         for ( String destinationPathSegment : destinationPathSegments )
234         {
235             // build up file listing cache
236             fileListingService.getChildren( fileEntry, true, null );
237 
238             fileEntry = fileEntry.findChild( destinationPathSegment );
239             if ( fileEntry == null )
240             {
241                 throw new MojoExecutionException( "Cannot execute goal: " + filePath + " does not exist on device." );
242             }
243         }
244         return fileEntry;
245     }
246 }