Coverage Report - org.apache.maven.wagon.providers.webdav.WebDavWagon
 
Classes in this File Line Coverage Branch Coverage Complexity
WebDavWagon
92%
80/87
77%
37/48
5,714
 
 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 java.io.File;
 23  
 import java.io.IOException;
 24  
 import java.net.URLDecoder;
 25  
 import java.util.ArrayList;
 26  
 import java.util.List;
 27  
 
 28  
 import org.apache.commons.httpclient.HttpException;
 29  
 import org.apache.commons.httpclient.HttpStatus;
 30  
 import org.apache.jackrabbit.webdav.DavConstants;
 31  
 import org.apache.jackrabbit.webdav.DavException;
 32  
 import org.apache.jackrabbit.webdav.MultiStatus;
 33  
 import org.apache.jackrabbit.webdav.MultiStatusResponse;
 34  
 import org.apache.jackrabbit.webdav.client.methods.MkColMethod;
 35  
 import org.apache.jackrabbit.webdav.client.methods.PropFindMethod;
 36  
 import org.apache.jackrabbit.webdav.property.DavProperty;
 37  
 import org.apache.jackrabbit.webdav.property.DavPropertyName;
 38  
 import org.apache.jackrabbit.webdav.property.DavPropertyNameSet;
 39  
 import org.apache.jackrabbit.webdav.property.DavPropertySet;
 40  
 import org.apache.maven.wagon.PathUtils;
 41  
 import org.apache.maven.wagon.ResourceDoesNotExistException;
 42  
 import org.apache.maven.wagon.TransferFailedException;
 43  
 import org.apache.maven.wagon.WagonConstants;
 44  
 import org.apache.maven.wagon.authorization.AuthorizationException;
 45  
 import org.apache.maven.wagon.repository.Repository;
 46  
 import org.apache.maven.wagon.shared.http.AbstractHttpClientWagon;
 47  
 import org.codehaus.plexus.util.FileUtils;
 48  
 import org.codehaus.plexus.util.StringUtils;
 49  
 import org.w3c.dom.Node;
 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  
  *
 61  
  * @plexus.component role="org.apache.maven.wagon.Wagon"
 62  
  *   role-hint="dav"
 63  
  *   instantiation-strategy="per-lookup"
 64  
  */
 65  138
 public class WebDavWagon
 66  
     extends AbstractHttpClientWagon
 67  
 {
 68  
     /**
 69  
      * Defines the protocol mapping to use.
 70  
      *
 71  
      * First string is the user definition way to define a webdav url,
 72  
      * the second string is the internal representation of that url.
 73  
      *
 74  
      * NOTE: The order of the mapping becomes the search order.
 75  
      */
 76  1
     private static final String[][] protocolMap = new String[][] {
 77  
         { "dav:http://", "http://" },    /* maven 2.0.x url string format. (violates URI spec) */
 78  
         { "dav:https://", "https://" },  /* maven 2.0.x url string format. (violates URI spec) */
 79  
         { "dav+http://", "http://" },    /* URI spec compliant (protocol+transport) */
 80  
         { "dav+https://", "https://" },  /* URI spec compliant (protocol+transport) */
 81  
         { "dav://", "http://" },         /* URI spec compliant (protocol only) */
 82  
         { "davs://", "https://" }        /* URI spec compliant (protocol only) */
 83  
     };
 84  
 
 85  
     /**
 86  
      * This wagon supports directory copying
 87  
      *
 88  
      * @return <code>true</code> always
 89  
      */
 90  
     public boolean supportsDirectoryCopy()
 91  
     {
 92  8
         return true;
 93  
     }
 94  
 
 95  
     /**
 96  
      * Create directories in server as needed.
 97  
      * They are created one at a time until the whole path exists.
 98  
      *
 99  
      * @param dir path to be created in server from repository basedir
 100  
      * @throws IOException
 101  
      * @throws HttpException
 102  
      * @throws TransferFailedException
 103  
      */
 104  
     protected void mkdirs( String dir ) throws HttpException, IOException
 105  
     {
 106  96
         Repository repository = getRepository();
 107  96
         String basedir = repository.getBasedir();
 108  
 
 109  96
         String baseUrl = repository.getProtocol() + "://" + repository.getHost();
 110  96
         if ( repository.getPort() != WagonConstants.UNKNOWN_PORT )
 111  
         {
 112  96
             baseUrl += ":" + repository.getPort();
 113  
         }
 114  
         
 115  
         // create relative path that will always have a leading and trailing slash
 116  96
         String relpath = FileUtils.normalize( getPath( basedir, dir ) + "/" );
 117  
 
 118  96
         PathNavigator navigator = new PathNavigator(relpath);
 119  
 
 120  
         // traverse backwards until we hit a directory that already exists (OK/NOT_ALLOWED), or that we were able to
 121  
         // create (CREATED), or until we get to the top of the path
 122  96
         int status = SC_NULL;
 123  
         do
 124  
         {
 125  174
             String url = baseUrl + "/" + navigator.getPath();
 126  174
             status = doMkCol( url );
 127  174
             if ( status == HttpStatus.SC_OK || status == HttpStatus.SC_CREATED
 128  
                 || status == HttpStatus.SC_METHOD_NOT_ALLOWED )
 129  
             {
 130  32
                 break;
 131  
             }
 132  
         }
 133  90
         while ( navigator.backward() );
 134  
 
 135  
         // traverse forward creating missing directories
 136  174
         while ( navigator.forward() )
 137  
         {
 138  78
             String url = baseUrl + "/" + navigator.getPath();
 139  78
             status = doMkCol( url );
 140  78
             if ( status != HttpStatus.SC_OK && status != HttpStatus.SC_CREATED )
 141  
             {
 142  0
                 throw new IOException( "Unable to create collection: " + url + "; status code = " + status );
 143  
             }
 144  78
         }
 145  96
     }
 146  
 
 147  
     private int doMkCol( String url ) throws HttpException, IOException
 148  
     {
 149  252
         MkColMethod method = null;
 150  
         try
 151  
         {
 152  252
             method = new MkColMethod( url );
 153  252
             return execute( method );
 154  
         }
 155  
         finally
 156  
         {
 157  252
             if ( method != null )
 158  
             {
 159  252
                 method.releaseConnection();
 160  
             }
 161  
         }
 162  
     }
 163  
 
 164  
     /**
 165  
      * Copy a directory from local system to remote webdav server
 166  
      *
 167  
      * @param sourceDirectory      the local directory
 168  
      * @param destinationDirectory the remote destination
 169  
      * @throws TransferFailedException
 170  
      * @throws ResourceDoesNotExistException
 171  
      * @throws AuthorizationException
 172  
      */
 173  
     public void putDirectory( File sourceDirectory, String destinationDirectory )
 174  
         throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
 175  
     {
 176  44
         File[] listFiles = sourceDirectory.listFiles();
 177  
 
 178  116
         for ( int i = 0; i < listFiles.length; i++ )
 179  
         {
 180  72
             if ( listFiles[i].isDirectory() )
 181  
             {
 182  36
                 putDirectory( listFiles[i], destinationDirectory + "/" + listFiles[i].getName() );
 183  
             }
 184  
             else
 185  
             {
 186  36
                 String target = destinationDirectory + "/" + listFiles[i].getName();
 187  
 
 188  36
                 put( listFiles[i], target );
 189  
             }
 190  
         }
 191  
 
 192  44
     }
 193  
 
 194  
     private boolean isDirectory( String url ) throws IOException, DavException
 195  
     {
 196  14
         DavPropertyNameSet nameSet = new DavPropertyNameSet();
 197  14
         nameSet.add( DavPropertyName.create( DavConstants.PROPERTY_RESOURCETYPE ) );
 198  
 
 199  14
         PropFindMethod method = null;
 200  
         try
 201  
         {
 202  14
             method = new PropFindMethod( url, nameSet, DavConstants.DEPTH_0 );
 203  14
             execute( method );
 204  14
             if ( method.succeeded() )
 205  
             {
 206  10
                 MultiStatus multiStatus = method.getResponseBodyAsMultiStatus();
 207  10
                 MultiStatusResponse response = multiStatus.getResponses()[0];
 208  10
                 DavPropertySet propertySet = response.getProperties( HttpStatus.SC_OK );
 209  10
                 DavProperty property = propertySet.get( DavConstants.PROPERTY_RESOURCETYPE );
 210  10
                 if ( property != null )
 211  
                 {
 212  10
                     Node node = (Node) property.getValue();
 213  10
                     return node.getLocalName().equals( DavConstants.XML_COLLECTION );
 214  
                 }
 215  
             }
 216  4
             return false;
 217  
         }
 218  
         finally
 219  
         {
 220  14
             if ( method != null )
 221  
             {
 222  14
                 method.releaseConnection();
 223  
             }
 224  
         }
 225  
     }
 226  
 
 227  
     public List getFileList( String destinationDirectory )
 228  
         throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
 229  
     {
 230  14
         String url = getRepository().getUrl() + '/' + destinationDirectory;
 231  
 
 232  14
         PropFindMethod method = null;
 233  
         try
 234  
         {
 235  14
             if ( isDirectory( url ) )
 236  
             {
 237  10
                 DavPropertyNameSet nameSet = new DavPropertyNameSet();
 238  10
                 nameSet.add( DavPropertyName.create( DavConstants.PROPERTY_DISPLAYNAME ) );
 239  
 
 240  10
                 method = new PropFindMethod( url, nameSet, DavConstants.DEPTH_1 );
 241  10
                 int status = execute( method );
 242  10
                 if ( method.succeeded() )
 243  
                 {
 244  10
                     ArrayList dirs = new ArrayList();
 245  10
                     MultiStatus multiStatus = method.getResponseBodyAsMultiStatus();
 246  
 
 247  46
                     for ( int i = 0; i < multiStatus.getResponses().length; i++ )
 248  
                     {
 249  
 
 250  36
                         MultiStatusResponse response = multiStatus.getResponses()[i];
 251  
 
 252  36
                         String entryUrl =  response.getHref();
 253  36
                         String fileName = PathUtils.filename( URLDecoder.decode( entryUrl ) );
 254  36
                         if ( entryUrl.endsWith( "/" ) )
 255  
                         {
 256  18
                             if ( i == 0 )
 257  
                             {
 258  
                                 //by design jackrabbit webdav sticks parent directory as the first entry
 259  
                                 // so we need to ignore this entry
 260  
                                 // http://www.nabble.com/Extra-entry-in-get-file-list-with-jackrabbit-webdav-td21262786.html
 261  
                                 // http://www.webdav.org/specs/rfc4918.html#rfc.section.9.1
 262  10
                                 continue;
 263  
                             }
 264  
 
 265  
                             //extract "dir/" part of "path.to.dir/"
 266  8
                             fileName = PathUtils.filename( PathUtils.dirname( URLDecoder.decode( entryUrl ) ) ) + "/";
 267  
                         }
 268  
 
 269  26
                         if ( !StringUtils.isEmpty( fileName ) )
 270  
                         {
 271  26
                             dirs.add( fileName );
 272  
                         }
 273  
                     }
 274  10
                     return dirs;
 275  
                 }
 276  
 
 277  0
                 if ( status == HttpStatus.SC_NOT_FOUND )
 278  
                 {
 279  0
                     throw new ResourceDoesNotExistException( "Destination directory does not exist: " + url );
 280  
                 }
 281  
             }
 282  
         }
 283  0
         catch ( DavException e )
 284  
         {
 285  0
             throw new TransferFailedException( e.getMessage(), e );
 286  
         }
 287  0
         catch ( IOException e )
 288  
         {
 289  0
             throw new TransferFailedException( e.getMessage(), e );
 290  
         }
 291  
         finally
 292  
         {
 293  14
             if ( method != null )
 294  
             {
 295  10
                 method.releaseConnection();
 296  
             }
 297  
         }
 298  4
         throw new ResourceDoesNotExistException( "Destination path exists but is not a "
 299  
                         + "WebDAV collection (directory): " + url );
 300  
     }
 301  
 
 302  
     public String getURL( Repository repository )
 303  
     {
 304  138
         String url = repository.getUrl();
 305  
 
 306  
         // Process mappings first.
 307  797
         for ( int i = 0; i < protocolMap.length; i++ )
 308  
         {
 309  757
             String protocol = protocolMap[i][0];
 310  757
             if ( url.startsWith( protocol ) )
 311  
             {
 312  98
                 return protocolMap[i][1] + url.substring( protocol.length() );
 313  
             }
 314  
         }
 315  
 
 316  
         // No mapping trigger? then just return as-is.
 317  40
         return url;
 318  
     }
 319  
 }