Coverage Report - org.apache.maven.wagon.providers.webdav.WebDavWagon
 
Classes in this File Line Coverage Branch Coverage Complexity
WebDavWagon
92 %
89/96
76 %
40/52
6
 
 1  
 package org.apache.maven.wagon.providers.webdav;
 2  
 
 3  
 /*
 4  
  * Licensed to the Apache Software Foundation (ASF) under one
 5  
  * or more contributor license agreements.  See the NOTICE file
 6  
  * distributed with this work for additional information
 7  
  * regarding copyright ownership.  The ASF licenses this file
 8  
  * to you under the Apache License, Version 2.0 (the
 9  
  * "License"); you may not use this file except in compliance
 10  
  * with the License.  You may obtain a copy of the License at
 11  
  *
 12  
  *   http://www.apache.org/licenses/LICENSE-2.0
 13  
  *
 14  
  * Unless required by applicable law or agreed to in writing,
 15  
  * software distributed under the License is distributed on an
 16  
  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 17  
  * KIND, either express or implied.  See the License for the
 18  
  * specific language governing permissions and limitations
 19  
  * under the License.
 20  
  */
 21  
 
 22  
 import org.apache.commons.httpclient.HttpException;
 23  
 import org.apache.commons.httpclient.HttpStatus;
 24  
 import org.apache.jackrabbit.webdav.DavConstants;
 25  
 import org.apache.jackrabbit.webdav.DavException;
 26  
 import org.apache.jackrabbit.webdav.MultiStatus;
 27  
 import org.apache.jackrabbit.webdav.MultiStatusResponse;
 28  
 import org.apache.jackrabbit.webdav.client.methods.MkColMethod;
 29  
 import org.apache.jackrabbit.webdav.client.methods.PropFindMethod;
 30  
 import org.apache.jackrabbit.webdav.property.DavProperty;
 31  
 import org.apache.jackrabbit.webdav.property.DavPropertyName;
 32  
 import org.apache.jackrabbit.webdav.property.DavPropertyNameSet;
 33  
 import org.apache.jackrabbit.webdav.property.DavPropertySet;
 34  
 import org.apache.maven.wagon.PathUtils;
 35  
 import org.apache.maven.wagon.ResourceDoesNotExistException;
 36  
 import org.apache.maven.wagon.TransferFailedException;
 37  
 import org.apache.maven.wagon.WagonConstants;
 38  
 import org.apache.maven.wagon.authorization.AuthorizationException;
 39  
 import org.apache.maven.wagon.repository.Repository;
 40  
 import org.apache.maven.wagon.shared.http.AbstractHttpClientWagon;
 41  
 import org.codehaus.plexus.util.FileUtils;
 42  
 import org.codehaus.plexus.util.StringUtils;
 43  
 import org.w3c.dom.Node;
 44  
 
 45  
 import java.io.File;
 46  
 import java.io.IOException;
 47  
 import java.net.URLDecoder;
 48  
 import java.util.ArrayList;
 49  
 import java.util.List;
 50  
 
 51  
 /**
 52  
  * <p>WebDavWagon</p>
 53  
  * <p/>
 54  
  * <p>Allows using a webdav remote repository for downloads and deployments</p>
 55  
  *
 56  
  * @author <a href="mailto:hisidro@exist.com">Henry Isidro</a>
 57  
  * @author <a href="mailto:joakime@apache.org">Joakim Erdfelt</a>
 58  
  * @author <a href="mailto:carlos@apache.org">Carlos Sanchez</a>
 59  
  * @author <a href="mailto:james@atlassian.com">James William Dumay</a>
 60  
  * @plexus.component role="org.apache.maven.wagon.Wagon"
 61  
  * role-hint="dav"
 62  
  * instantiation-strategy="per-lookup"
 63  
  */
 64  148
 public class WebDavWagon
 65  
     extends AbstractHttpClientWagon
 66  
 {
 67  
     protected static final String CONTINUE_ON_FAILURE_PROPERTY = "wagon.webdav.continueOnFailure";
 68  
 
 69  148
     private final boolean continueOnFailure = Boolean.getBoolean( CONTINUE_ON_FAILURE_PROPERTY );
 70  
 
 71  
     /**
 72  
      * Defines the protocol mapping to use.
 73  
      * <p/>
 74  
      * First string is the user definition way to define a webdav url,
 75  
      * the second string is the internal representation of that url.
 76  
      * <p/>
 77  
      * NOTE: The order of the mapping becomes the search order.
 78  
      */
 79  1
     private static final String[][] protocolMap =
 80  
         new String[][]{ { "dav:http://", "http://" },    /* maven 2.0.x url string format. (violates URI spec) */
 81  
             { "dav:https://", "https://" },  /* maven 2.0.x url string format. (violates URI spec) */
 82  
             { "dav+http://", "http://" },    /* URI spec compliant (protocol+transport) */
 83  
             { "dav+https://", "https://" },  /* URI spec compliant (protocol+transport) */
 84  
             { "dav://", "http://" },         /* URI spec compliant (protocol only) */
 85  
             { "davs://", "https://" }        /* URI spec compliant (protocol only) */ };
 86  
 
 87  
     /**
 88  
      * This wagon supports directory copying
 89  
      *
 90  
      * @return <code>true</code> always
 91  
      */
 92  
     public boolean supportsDirectoryCopy()
 93  
     {
 94  8
         return true;
 95  
     }
 96  
 
 97  
     /**
 98  
      * Create directories in server as needed.
 99  
      * They are created one at a time until the whole path exists.
 100  
      *
 101  
      * @param dir path to be created in server from repository basedir
 102  
      * @throws IOException
 103  
      * @throws HttpException
 104  
      * @throws TransferFailedException
 105  
      */
 106  
     protected void mkdirs( String dir )
 107  
         throws IOException
 108  
     {
 109  104
         Repository repository = getRepository();
 110  104
         String basedir = repository.getBasedir();
 111  
 
 112  104
         String baseUrl = repository.getProtocol() + "://" + repository.getHost();
 113  104
         if ( repository.getPort() != WagonConstants.UNKNOWN_PORT )
 114  
         {
 115  104
             baseUrl += ":" + repository.getPort();
 116  
         }
 117  
 
 118  
         // create relative path that will always have a leading and trailing slash
 119  104
         String relpath = FileUtils.normalize( getPath( basedir, dir ) + "/" );
 120  
 
 121  104
         PathNavigator navigator = new PathNavigator( relpath );
 122  
 
 123  
         // traverse backwards until we hit a directory that already exists (OK/NOT_ALLOWED), or that we were able to
 124  
         // create (CREATED), or until we get to the top of the path
 125  104
         int status = SC_NULL;
 126  
         do
 127  
         {
 128  186
             String url = baseUrl + "/" + navigator.getPath();
 129  186
             status = doMkCol( url );
 130  186
             if ( status == HttpStatus.SC_OK || status == HttpStatus.SC_CREATED
 131  
                 || status == HttpStatus.SC_METHOD_NOT_ALLOWED )
 132  
             {
 133  32
                 break;
 134  
             }
 135  
         }
 136  98
         while ( navigator.backward() );
 137  
 
 138  
         // traverse forward creating missing directories
 139  186
         while ( navigator.forward() )
 140  
         {
 141  82
             String url = baseUrl + "/" + navigator.getPath();
 142  82
             status = doMkCol( url );
 143  82
             if ( status != HttpStatus.SC_OK && status != HttpStatus.SC_CREATED )
 144  
             {
 145  0
                 throw new IOException( "Unable to create collection: " + url + "; status code = " + status );
 146  
             }
 147  82
         }
 148  104
     }
 149  
 
 150  
     private int doMkCol( String url )
 151  
         throws IOException
 152  
     {
 153  268
         MkColMethod method = null;
 154  
         try
 155  
         {
 156  268
             method = new MkColMethod( url );
 157  268
             return execute( method );
 158  
         }
 159  
         finally
 160  
         {
 161  268
             if ( method != null )
 162  
             {
 163  268
                 method.releaseConnection();
 164  
             }
 165  
         }
 166  
     }
 167  
 
 168  
     /**
 169  
      * Copy a directory from local system to remote webdav server
 170  
      *
 171  
      * @param sourceDirectory      the local directory
 172  
      * @param destinationDirectory the remote destination
 173  
      * @throws TransferFailedException
 174  
      * @throws ResourceDoesNotExistException
 175  
      * @throws AuthorizationException
 176  
      */
 177  
     public void putDirectory( File sourceDirectory, String destinationDirectory )
 178  
         throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
 179  
     {
 180  44
         File[] listFiles = sourceDirectory.listFiles();
 181  
 
 182  116
         for ( int i = 0; i < listFiles.length; i++ )
 183  
         {
 184  72
             if ( listFiles[i].isDirectory() )
 185  
             {
 186  36
                 putDirectory( listFiles[i], destinationDirectory + "/" + listFiles[i].getName() );
 187  
             }
 188  
             else
 189  
             {
 190  36
                 String target = destinationDirectory + "/" + listFiles[i].getName();
 191  
 
 192  36
                 put( listFiles[i], target );
 193  
             }
 194  
         }
 195  
 
 196  44
     }
 197  
 
 198  
     private boolean isDirectory( String url )
 199  
         throws IOException, DavException
 200  
     {
 201  14
         DavPropertyNameSet nameSet = new DavPropertyNameSet();
 202  14
         nameSet.add( DavPropertyName.create( DavConstants.PROPERTY_RESOURCETYPE ) );
 203  
 
 204  14
         PropFindMethod method = null;
 205  
         try
 206  
         {
 207  14
             method = new PropFindMethod( url, nameSet, DavConstants.DEPTH_0 );
 208  14
             execute( method );
 209  14
             if ( method.succeeded() )
 210  
             {
 211  10
                 MultiStatus multiStatus = method.getResponseBodyAsMultiStatus();
 212  10
                 MultiStatusResponse response = multiStatus.getResponses()[0];
 213  10
                 DavPropertySet propertySet = response.getProperties( HttpStatus.SC_OK );
 214  10
                 DavProperty property = propertySet.get( DavConstants.PROPERTY_RESOURCETYPE );
 215  10
                 if ( property != null )
 216  
                 {
 217  10
                     Node node = (Node) property.getValue();
 218  10
                     return node.getLocalName().equals( DavConstants.XML_COLLECTION );
 219  
                 }
 220  
             }
 221  4
             return false;
 222  
         }
 223  
         finally
 224  
         {
 225  14
             if ( method != null )
 226  
             {
 227  14
                 method.releaseConnection();
 228  
             }
 229  
         }
 230  
     }
 231  
 
 232  
     public List<String> getFileList( String destinationDirectory )
 233  
         throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
 234  
     {
 235  14
         String repositoryUrl = repository.getUrl();
 236  14
         String url = repositoryUrl + ( repositoryUrl.endsWith( "/" ) ? "" : "/" ) + destinationDirectory;
 237  
 
 238  14
         PropFindMethod method = null;
 239  
         try
 240  
         {
 241  14
             if ( isDirectory( url ) )
 242  
             {
 243  10
                 DavPropertyNameSet nameSet = new DavPropertyNameSet();
 244  10
                 nameSet.add( DavPropertyName.create( DavConstants.PROPERTY_DISPLAYNAME ) );
 245  
 
 246  10
                 method = new PropFindMethod( url, nameSet, DavConstants.DEPTH_1 );
 247  10
                 int status = execute( method );
 248  10
                 if ( method.succeeded() )
 249  
                 {
 250  10
                     ArrayList<String> dirs = new ArrayList<String>();
 251  10
                     MultiStatus multiStatus = method.getResponseBodyAsMultiStatus();
 252  
 
 253  46
                     for ( int i = 0; i < multiStatus.getResponses().length; i++ )
 254  
                     {
 255  
 
 256  36
                         MultiStatusResponse response = multiStatus.getResponses()[i];
 257  
 
 258  36
                         String entryUrl = response.getHref();
 259  36
                         String fileName = PathUtils.filename( URLDecoder.decode( entryUrl ) );
 260  36
                         if ( entryUrl.endsWith( "/" ) )
 261  
                         {
 262  18
                             if ( i == 0 )
 263  
                             {
 264  
                                 //by design jackrabbit webdav sticks parent directory as the first entry
 265  
                                 // so we need to ignore this entry
 266  
                                 // http://www.nabble.com/Extra-entry-in-get-file-list-with-jackrabbit-webdav-td21262786.html
 267  
                                 // http://www.webdav.org/specs/rfc4918.html#rfc.section.9.1
 268  10
                                 continue;
 269  
                             }
 270  
 
 271  
                             //extract "dir/" part of "path.to.dir/"
 272  8
                             fileName = PathUtils.filename( PathUtils.dirname( URLDecoder.decode( entryUrl ) ) ) + "/";
 273  
                         }
 274  
 
 275  26
                         if ( !StringUtils.isEmpty( fileName ) )
 276  
                         {
 277  26
                             dirs.add( fileName );
 278  
                         }
 279  
                     }
 280  10
                     return dirs;
 281  
                 }
 282  
 
 283  0
                 if ( status == HttpStatus.SC_NOT_FOUND )
 284  
                 {
 285  0
                     throw new ResourceDoesNotExistException( "Destination directory does not exist: " + url );
 286  
                 }
 287  
             }
 288  
         }
 289  0
         catch ( DavException e )
 290  
         {
 291  0
             throw new TransferFailedException( e.getMessage(), e );
 292  
         }
 293  0
         catch ( IOException e )
 294  
         {
 295  0
             throw new TransferFailedException( e.getMessage(), e );
 296  
         }
 297  
         finally
 298  
         {
 299  14
             if ( method != null )
 300  
             {
 301  10
                 method.releaseConnection();
 302  
             }
 303  
         }
 304  4
         throw new ResourceDoesNotExistException(
 305  
             "Destination path exists but is not a " + "WebDAV collection (directory): " + url );
 306  
     }
 307  
 
 308  
     public String getURL( Repository repository )
 309  
     {
 310  148
         String url = repository.getUrl();
 311  
 
 312  
         // Process mappings first.
 313  852
         for ( int i = 0; i < protocolMap.length; i++ )
 314  
         {
 315  812
             String protocol = protocolMap[i][0];
 316  812
             if ( url.startsWith( protocol ) )
 317  
             {
 318  108
                 return protocolMap[i][1] + url.substring( protocol.length() );
 319  
             }
 320  
         }
 321  
 
 322  
         // No mapping trigger? then just return as-is.
 323  40
         return url;
 324  
     }
 325  
 
 326  
 
 327  
     public void put( File source, String resourceName )
 328  
         throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
 329  
     {
 330  
         try
 331  
         {
 332  86
             super.put( source, resourceName );
 333  
         }
 334  10
         catch ( TransferFailedException e )
 335  
         {
 336  10
             if ( continueOnFailure )
 337  
             {
 338  
                 // TODO use a logging mechanism here or a fireTransferWarning
 339  2
                 System.out.println(
 340  
                     "WARN: Skip unable to transfer '" + resourceName + "' from '" + source.getPath() + "' due to "
 341  
                         + e.getMessage() );
 342  
             }
 343  
             else
 344  
             {
 345  8
                 throw e;
 346  
             }
 347  72
         }
 348  74
     }
 349  
 }