001package org.apache.maven.wagon.providers.webdav;
002
003/*
004 * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
005 * agreements. See the NOTICE file distributed with this work for additional information regarding
006 * copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance with the License. You may obtain a
008 * copy of the License at
009 * 
010 * http://www.apache.org/licenses/LICENSE-2.0
011 * 
012 * Unless required by applicable law or agreed to in writing, software distributed under the License
013 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
014 * or implied. See the License for the specific language governing permissions and limitations under
015 * the License.
016 */
017
018import it.could.webdav.DAVServlet;
019import org.apache.commons.httpclient.HttpException;
020import org.apache.commons.httpclient.HttpMethod;
021import org.apache.maven.wagon.ResourceDoesNotExistException;
022import org.apache.maven.wagon.StreamingWagon;
023import org.apache.maven.wagon.TransferFailedException;
024import org.apache.maven.wagon.Wagon;
025import org.apache.maven.wagon.http.HttpWagonTestCase;
026import org.apache.maven.wagon.repository.Repository;
027import org.apache.maven.wagon.resource.Resource;
028import org.mortbay.jetty.Server;
029import org.mortbay.jetty.servlet.Context;
030import org.mortbay.jetty.servlet.ServletHolder;
031
032import java.io.File;
033import java.io.IOException;
034import java.net.SocketTimeoutException;
035import java.util.List;
036import java.util.Properties;
037
038/*
039 * WebDAV Wagon Test
040 * 
041 * @author <a href="mailto:joakim@erdfelt.com">Joakim Erdfelt</a>
042 * 
043 * @author <a href="mailto:carlos@apache.org">Carlos Sanchez</a>
044 */
045public class WebDavWagonTest
046    extends HttpWagonTestCase
047{
048    protected String getTestRepositoryUrl()
049        throws IOException
050    {
051        return getProtocol() + "://localhost:" + getTestRepositoryPort() + "/newfolder/folder2/";
052    }
053
054    protected String getProtocol()
055    {
056        return "dav";
057    }
058
059    protected void createContext( Server server, File repositoryDirectory )
060        throws IOException
061    {
062        Context dav = new Context( server, "/", Context.SESSIONS );
063        ServletHolder davServletHolder = new ServletHolder( new DAVServlet() );
064        davServletHolder.setInitParameter( "rootPath", repositoryDirectory.getAbsolutePath() );
065        davServletHolder.setInitParameter( "xmlOnly", "false" );
066        dav.addServlet( davServletHolder, "/*" );
067    }
068
069    protected long getExpectedLastModifiedOnGet( Repository repository, Resource resource )
070    {
071        File file = new File( getDavRepository(), resource.getName() );
072        return ( file.lastModified() / 1000 ) * 1000;
073    }
074
075
076    private File getDavRepository()
077    {
078        return getTestFile( "target/test-output/http-repository/newfolder/folder2" );
079    }
080
081    private void assertURL( String userUrl, String expectedUrl )
082    {
083        Repository repo = new Repository( "test-geturl", userUrl );
084        String actualUrl = ( new WebDavWagon() ).getURL( repo );
085        assertEquals( "WebDavWagon.getURL(" + userUrl + ")", expectedUrl, actualUrl );
086    }
087
088    /**
089     * Tests the maven 2.0.x way to define a webdav URL without SSL.
090     */
091    public void testGetURLDavHttp()
092    {
093        assertURL( "dav:http://localhost:" + getTestRepositoryPort() + "/dav/",
094                   "http://localhost:" + getTestRepositoryPort() + "/dav/" );
095    }
096
097    /**
098     * Tests the maven 2.0.x way to define a webdav URL with SSL.
099     */
100    public void testGetURLDavHttps()
101    {
102        assertURL( "dav:https://localhost:" + getTestRepositoryPort() + "/dav/",
103                   "https://localhost:" + getTestRepositoryPort() + "/dav/" );
104    }
105
106    /**
107     * Tests the URI spec way of defining a webdav URL without SSL.
108     */
109    public void testGetURLDavUri()
110    {
111        assertURL( "dav://localhost:" + getTestRepositoryPort() + "/dav/",
112                   "http://localhost:" + getTestRepositoryPort() + "/dav/" );
113    }
114
115    /**
116     * Tests the URI spec way of defining a webdav URL with SSL.
117     */
118    public void testGetURLDavUriWithSsl()
119    {
120        assertURL( "davs://localhost:" + getTestRepositoryPort() + "/dav/",
121                   "https://localhost:" + getTestRepositoryPort() + "/dav/" );
122    }
123
124    /**
125     * Tests the URI spec way of defining a webdav URL without SSL.
126     */
127    public void testGetURLDavPlusHttp()
128    {
129        assertURL( "dav+https://localhost:" + getTestRepositoryPort() + "/dav/",
130                   "https://localhost:" + getTestRepositoryPort() + "/dav/" );
131    }
132
133    /**
134     * Tests the URI spec way of defining a webdav URL with SSL.
135     */
136    public void testGetURLDavPlusHttps()
137    {
138        assertURL( "dav+https://localhost:" + getTestRepositoryPort() + "/dav/",
139                   "https://localhost:" + getTestRepositoryPort() + "/dav/" );
140    }
141
142    public void testMkdirs()
143        throws Exception
144    {
145        setupRepositories();
146
147        setupWagonTestingFixtures();
148
149        WebDavWagon wagon = (WebDavWagon) getWagon();
150        wagon.connect( testRepository, getAuthInfo() );
151
152        try
153        {
154            File dir = getRepositoryDirectory();
155
156            // check basedir also doesn't exist and will need to be created
157            dir = new File( dir, testRepository.getBasedir() );
158            assertFalse( dir.exists() );
159
160            // test leading /
161            assertFalse( new File( dir, "foo" ).exists() );
162            wagon.mkdirs( "/foo" );
163            assertTrue( new File( dir, "foo" ).exists() );
164
165            // test trailing /
166            assertFalse( new File( dir, "bar" ).exists() );
167            wagon.mkdirs( "bar/" );
168            assertTrue( new File( dir, "bar" ).exists() );
169
170            // test when already exists
171            wagon.mkdirs( "bar" );
172
173            // test several parts
174            assertFalse( new File( dir, "1/2/3/4" ).exists() );
175            wagon.mkdirs( "1/2/3/4" );
176            assertTrue( new File( dir, "1/2/3/4" ).exists() );
177
178            // test additional part and trailing /
179            assertFalse( new File( dir, "1/2/3/4/5" ).exists() );
180            wagon.mkdirs( "1/2/3/4/5/" );
181            assertTrue( new File( dir, "1/2/3/4" ).exists() );
182        }
183        finally
184        {
185            wagon.disconnect();
186
187            tearDownWagonTestingFixtures();
188        }
189    }
190
191    public void testMkdirsWithNoBasedir()
192        throws Exception
193    {
194        // WAGON-244
195        setupRepositories();
196
197        setupWagonTestingFixtures();
198
199        // reconstruct with no basedir
200        testRepository.setUrl(
201            testRepository.getProtocol() + "://" + testRepository.getHost() + ":" + testRepository.getPort() );
202
203        WebDavWagon wagon = (WebDavWagon) getWagon();
204        wagon.connect( testRepository, getAuthInfo() );
205
206        try
207        {
208            File dir = getRepositoryDirectory();
209
210            // check basedir also doesn't exist and will need to be created
211            dir = new File( dir, testRepository.getBasedir() );
212            assertTrue( dir.exists() );
213
214            // test leading /
215            assertFalse( new File( dir, "foo" ).exists() );
216            wagon.mkdirs( "/foo" );
217            assertTrue( new File( dir, "foo" ).exists() );
218        }
219        finally
220        {
221            wagon.disconnect();
222
223            tearDownWagonTestingFixtures();
224        }
225    }
226
227    protected void setHttpHeaders( StreamingWagon wagon, Properties properties )
228    {
229        ( (WebDavWagon) wagon ).setHttpHeaders( properties );
230    }
231
232    /**
233     * Make sure wagon webdav can detect remote directory
234     *
235     * @throws Exception
236     */
237    public void testWagonWebDavGetFileList()
238        throws Exception
239    {
240        setupRepositories();
241
242        setupWagonTestingFixtures();
243
244        String dirName = "file-list";
245
246        String filenames[] =
247            new String[]{ "test-resource.txt", "test-resource.pom", "test-resource b.txt", "more-resources.dat" };
248
249        for ( int i = 0; i < filenames.length; i++ )
250        {
251            putFile( dirName + "/" + filenames[i], dirName + "/" + filenames[i], filenames[i] + "\n" );
252        }
253
254        String dirnames[] = new String[]{ "test-dir1", "test-dir2" };
255
256        for ( int i = 0; i < dirnames.length; i++ )
257        {
258            new File( getDavRepository(), dirName + "/" + dirnames[i] ).mkdirs();
259        }
260
261        Wagon wagon = getWagon();
262
263        wagon.connect( testRepository, getAuthInfo() );
264
265        List<String> list = wagon.getFileList( dirName );
266
267        assertNotNull( "file list should not be null.", list );
268        assertEquals( "file list should contain 6 items", 6, list.size() );
269
270        for ( int i = 0; i < filenames.length; i++ )
271        {
272            assertTrue( "Filename '" + filenames[i] + "' should be in list.", list.contains( filenames[i] ) );
273        }
274
275        for ( int i = 0; i < dirnames.length; i++ )
276        {
277            assertTrue( "Directory '" + dirnames[i] + "' should be in list.", list.contains( dirnames[i] + "/" ) );
278        }
279
280        ///////////////////////////////////////////////////////////////////////////
281        list = wagon.getFileList( "" );
282        assertNotNull( "file list should not be null.", list );
283        assertEquals( "file list should contain 1 items", 1, list.size() );
284
285        ///////////////////////////////////////////////////////////////////////////
286        list = wagon.getFileList( dirName + "/test-dir1" );
287        assertNotNull( "file list should not be null.", list );
288        assertEquals( "file list should contain 0 items", 0, list.size() );
289
290        /////////////////////////////////////////////////////////////////////////////
291        try
292        {
293            list = wagon.getFileList( dirName + "/test-dir-bogus" );
294            fail( "Exception expected" );
295        }
296        catch ( ResourceDoesNotExistException e )
297        {
298
299        }
300
301        wagon.disconnect();
302
303        tearDownWagonTestingFixtures();
304    }
305
306
307    public void testWagonFailsOnPutFailureByDefault()
308        throws Exception
309    {
310        setupRepositories();
311
312        setupWagonTestingFixtures();
313
314        File testFile = getTempFile();
315
316        System.clearProperty( WebDavWagon.CONTINUE_ON_FAILURE_PROPERTY );
317
318        WebDavWagon wagon = new TimeoutSimulatingWagon();
319        wagon.connect( testRepository, getAuthInfo() );
320
321        try
322        {
323            String filename = TimeoutSimulatingWagon.TIMEOUT_TRIGGER + ".txt";
324
325            try
326            {
327                wagon.put( testFile, filename );
328                fail( "Exception expected" );
329            }
330            catch ( TransferFailedException e )
331            {
332
333            }
334        }
335        finally
336        {
337            wagon.disconnect();
338
339            tearDownWagonTestingFixtures();
340        }
341    }
342
343    private File getTempFile()
344        throws IOException
345    {
346        File inputFile = File.createTempFile( "test-resource", ".txt" );
347        inputFile.deleteOnExit();
348        return inputFile;
349    }
350
351    private static class TimeoutSimulatingWagon
352        extends WebDavWagon
353    {
354        private static final String TIMEOUT_TRIGGER = "timeout";
355
356        protected int execute( HttpMethod httpMethod )
357            throws HttpException, IOException
358        {
359            if ( httpMethod.getPath().contains( TIMEOUT_TRIGGER ) )
360            {
361                throw new SocketTimeoutException( "Timeout triggered by request for '" + httpMethod.getPath() + "'" );
362            }
363            else
364            {
365                return super.execute( httpMethod );
366            }
367        }
368    }
369
370    public void testWagonContinuesOnPutFailureIfPropertySet()
371        throws Exception
372    {
373        setupRepositories();
374
375        setupWagonTestingFixtures();
376
377        File testFile = getTempFile();
378
379        String continueOnFailureProperty = WebDavWagon.CONTINUE_ON_FAILURE_PROPERTY;
380        System.setProperty( continueOnFailureProperty, "true" );
381
382        WebDavWagon wagon = new TimeoutSimulatingWagon();
383        wagon.connect( testRepository, getAuthInfo() );
384
385        try
386        {
387            String filename = TimeoutSimulatingWagon.TIMEOUT_TRIGGER + ".txt";
388
389            wagon.put( testFile, filename );
390        }
391        finally
392        {
393            wagon.disconnect();
394
395            System.clearProperty( continueOnFailureProperty );
396
397            tearDownWagonTestingFixtures();
398        }
399    }
400
401    @Override
402    protected boolean supportPreemptiveAuthenticationPut()
403    {
404        return true;
405    }
406
407    @Override
408    protected boolean supportPreemptiveAuthenticationGet()
409    {
410        return false;
411    }
412
413    @Override
414    protected boolean supportProxyPreemptiveAuthentication()
415    {
416        return false;
417    }
418
419    protected void testPreemptiveAuthenticationGet( TestSecurityHandler sh, boolean preemptive )
420    {
421        if ( preemptive )
422        {
423            assertEquals( "testPreemptiveAuthenticationGet preemptive=true: expected 1 request, got "
424                + sh.handlerRequestResponses, 1, sh.handlerRequestResponses.size() );
425            assertEquals( 200, sh.handlerRequestResponses.get( 0 ).responseCode );
426        }
427        else
428        {
429            assertEquals( "testPreemptiveAuthenticationGet preemptive=false: expected 2 requests (401,200), got "
430                + sh.handlerRequestResponses, 2, sh.handlerRequestResponses.size() );
431            assertEquals( 401, sh.handlerRequestResponses.get( 0 ).responseCode );
432            assertEquals( 200, sh.handlerRequestResponses.get( 1 ).responseCode );
433        }
434    }
435
436    protected void testPreemptiveAuthenticationPut( TestSecurityHandler sh, boolean preemptive )
437    {
438        if ( preemptive )
439        {
440            assertEquals( "testPreemptiveAuthenticationPut preemptive=true: expected 2 requests (200,.), got "
441                + sh.handlerRequestResponses, 2, sh.handlerRequestResponses.size() );
442            assertEquals( 200, sh.handlerRequestResponses.get( 0 ).responseCode );
443        }
444        else
445        {
446            assertEquals( "testPreemptiveAuthenticationPut preemptive=false: expected 3 requests (401,200,.), got "
447                + sh.handlerRequestResponses, 3, sh.handlerRequestResponses.size() );
448            assertEquals( 401, sh.handlerRequestResponses.get( 0 ).responseCode );
449            assertEquals( 200, sh.handlerRequestResponses.get( 1 ).responseCode );
450        }
451    }
452
453
454    @Override
455    protected void testPreemptiveAuthentication( TestSecurityHandler sh, boolean preemptive )
456    {
457        if ( preemptive )
458        {
459            assertEquals( "testPreemptiveAuthentication preemptive=false: expected 2 requests (200,.), got "
460                + sh.handlerRequestResponses, 2, sh.handlerRequestResponses.size() );
461            assertEquals( 200, sh.handlerRequestResponses.get( 0 ).responseCode );
462        }
463        else
464        {
465            assertEquals( "testPreemptiveAuthentication preemptive=false: expected 3 requests (401,200,200), got "
466                + sh.handlerRequestResponses, 3, sh.handlerRequestResponses.size() );
467            assertEquals( 401, sh.handlerRequestResponses.get( 0 ).responseCode );
468            assertEquals( 200, sh.handlerRequestResponses.get( 1 ).responseCode );
469            assertEquals( 200, sh.handlerRequestResponses.get( 2 ).responseCode );
470
471        }
472    }
473
474    protected void checkRequestResponseForRedirectPutFromStreamWithFullUrl( PutHandler putHandler,
475                                                                            RedirectHandler redirectHandler )
476    {
477        assertEquals( "found:" + putHandler.handlerRequestResponses, 1, putHandler.handlerRequestResponses.size() );
478        assertEquals( "found:" + putHandler.handlerRequestResponses, 201,
479                      putHandler.handlerRequestResponses.get( 0 ).responseCode );
480        assertEquals( "found:" + redirectHandler.handlerRequestResponses, 3,
481                      redirectHandler.handlerRequestResponses.size() );
482        assertEquals( "found:" + redirectHandler.handlerRequestResponses, 302,
483                      redirectHandler.handlerRequestResponses.get( 0 ).responseCode );
484    }
485
486    protected void checkRequestResponseForRedirectPutFromStreamWithRelativeUrl( PutHandler putHandler,
487                                                                                RedirectHandler redirectHandler )
488    {
489        assertEquals( "found:" + putHandler.handlerRequestResponses, 0, putHandler.handlerRequestResponses.size() );
490
491        assertEquals( "found:" + redirectHandler.handlerRequestResponses, 4,
492                      redirectHandler.handlerRequestResponses.size() );
493        assertEquals( "found:" + redirectHandler.handlerRequestResponses, 302,
494                      redirectHandler.handlerRequestResponses.get( 0 ).responseCode );
495        assertEquals( "found:" + redirectHandler.handlerRequestResponses, 302,
496                      redirectHandler.handlerRequestResponses.get( 1 ).responseCode );
497        assertEquals( "found:" + redirectHandler.handlerRequestResponses, 201,
498                      redirectHandler.handlerRequestResponses.get( 3 ).responseCode );
499
500    }
501
502}