1 package org.apache.archiva.webdav;
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
12 * http://www.apache.org/licenses/LICENSE-2.0
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
22 import junit.framework.TestCase;
23 import org.apache.archiva.admin.model.RepositoryAdminException;
24 import org.apache.archiva.admin.model.beans.ManagedRepository;
25 import org.apache.archiva.admin.model.beans.RepositoryGroup;
26 import org.apache.archiva.admin.repository.group.DefaultRepositoryGroupAdmin;
27 import org.apache.archiva.admin.repository.managed.DefaultManagedRepositoryAdmin;
28 import org.apache.archiva.common.plexusbridge.PlexusSisuBridge;
29 import org.apache.archiva.common.plexusbridge.PlexusSisuBridgeException;
30 import org.apache.commons.io.FileUtils;
31 import org.apache.jackrabbit.webdav.DavException;
32 import org.apache.jackrabbit.webdav.DavResourceLocator;
33 import org.apache.jackrabbit.webdav.DavServletRequest;
34 import org.apache.jackrabbit.webdav.DavServletResponse;
35 import org.apache.archiva.configuration.ArchivaConfiguration;
36 import org.apache.archiva.configuration.Configuration;
37 import org.apache.archiva.configuration.RepositoryGroupConfiguration;
38 import org.apache.archiva.proxy.DefaultRepositoryProxyConnectors;
39 import org.apache.archiva.repository.ManagedRepositoryContent;
40 import org.apache.archiva.repository.RepositoryContentFactory;
41 import org.apache.archiva.repository.content.LegacyPathParser;
42 import org.apache.archiva.repository.content.ManagedDefaultRepositoryContent;
43 import org.apache.archiva.repository.content.RepositoryRequest;
44 import org.easymock.MockControl;
45 import org.easymock.classextension.MockClassControl;
46 import org.junit.After;
47 import org.junit.Before;
48 import org.junit.Test;
49 import org.junit.runner.RunWith;
50 import org.springframework.context.ApplicationContext;
51 import org.springframework.test.context.ContextConfiguration;
52 import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
54 import javax.inject.Inject;
56 import java.io.IOException;
57 import java.util.ArrayList;
58 import java.util.List;
61 * ArchivaDavResourceFactoryTest
63 @RunWith( SpringJUnit4ClassRunner.class )
64 @ContextConfiguration( locations = { "classpath*:/META-INF/spring-context.xml", "classpath*:/spring-context.xml" } )
65 public class ArchivaDavResourceFactoryTest
68 private static final String RELEASES_REPO = "releases";
70 private static final String INTERNAL_REPO = "internal";
72 private static final String LOCAL_MIRROR_REPO = "local-mirror";
74 private static final String LEGACY_REPO = "legacy-repo";
76 private static final String LOCAL_REPO_GROUP = "local";
78 private OverridingArchivaDavResourceFactory resourceFactory;
80 private MockControl requestControl;
82 private DavServletRequest request;
84 private MockControl repoRequestControl;
86 private RepositoryRequest repoRequest;
88 private MockControl responseControl;
90 private DavServletResponse response;
92 private MockControl archivaConfigurationControl;
94 private ArchivaConfiguration archivaConfiguration;
96 private Configuration config;
98 private MockControl repoContentFactoryControl;
100 private RepositoryContentFactory repoFactory;
103 ApplicationContext applicationContext;
106 PlexusSisuBridge plexusSisuBridge;
109 DefaultManagedRepositoryAdmin defaultManagedRepositoryAdmin;
112 DefaultRepositoryGroupAdmin defaultRepositoryGroupAdmin;
121 requestControl = MockControl.createControl( DavServletRequest.class );
122 request = (DavServletRequest) requestControl.getMock();
124 responseControl = MockControl.createControl( DavServletResponse.class );
125 response = (DavServletResponse) responseControl.getMock();
126 responseControl.setDefaultMatcher( MockControl.ALWAYS_MATCHER );
128 archivaConfigurationControl = MockControl.createControl( ArchivaConfiguration.class );
129 archivaConfiguration = (ArchivaConfiguration) archivaConfigurationControl.getMock();
131 config = new Configuration();
132 archivaConfiguration.getConfiguration();
133 archivaConfigurationControl.setReturnValue( config, 5, 20 );
134 archivaConfiguration.save( config );
135 archivaConfigurationControl.setVoidCallable( 1, 4 );
136 archivaConfigurationControl.replay();
138 defaultManagedRepositoryAdmin.setArchivaConfiguration( archivaConfiguration );
139 defaultManagedRepositoryAdmin.getRepositoryCommonValidator().setArchivaConfiguration( archivaConfiguration );
140 if ( defaultManagedRepositoryAdmin.getManagedRepository( RELEASES_REPO ) == null )
142 defaultManagedRepositoryAdmin.addManagedRepository(
143 createManagedRepository( RELEASES_REPO, new File( "target/test-classes/" + RELEASES_REPO ).getPath(),
144 "default" ), false, null );
146 if ( defaultManagedRepositoryAdmin.getManagedRepository( INTERNAL_REPO ) == null )
148 defaultManagedRepositoryAdmin.addManagedRepository(
149 createManagedRepository( INTERNAL_REPO, new File( "target/test-classes/" + INTERNAL_REPO ).getPath(),
150 "default" ), false, null );
152 RepositoryGroup repoGroupConfig = new RepositoryGroup();
153 repoGroupConfig.setId( LOCAL_REPO_GROUP );
154 repoGroupConfig.addRepository( RELEASES_REPO );
155 repoGroupConfig.addRepository( INTERNAL_REPO );
157 defaultRepositoryGroupAdmin.setArchivaConfiguration( archivaConfiguration );
158 if ( defaultManagedRepositoryAdmin.getManagedRepository( LOCAL_REPO_GROUP ) == null )
160 defaultRepositoryGroupAdmin.addRepositoryGroup( repoGroupConfig, null );
163 repoContentFactoryControl = MockClassControl.createControl( RepositoryContentFactory.class );
164 repoFactory = (RepositoryContentFactory) repoContentFactoryControl.getMock();
166 repoRequestControl = MockClassControl.createControl( RepositoryRequest.class );
167 repoRequest = (RepositoryRequest) repoRequestControl.getMock();
170 new OverridingArchivaDavResourceFactory( applicationContext, plexusSisuBridge, archivaConfiguration );
171 resourceFactory.setArchivaConfiguration( archivaConfiguration );
172 resourceFactory.setRepositoryFactory( repoFactory );
173 resourceFactory.setRepositoryRequest( repoRequest );
174 resourceFactory.setConnectors( new OverridingRepositoryProxyConnectors() );
177 private ManagedRepository createManagedRepository( String id, String location, String layout )
179 ManagedRepository repoConfig = new ManagedRepository();
180 repoConfig.setId( id );
181 repoConfig.setName( id );
182 repoConfig.setLocation( location );
183 repoConfig.setLayout( layout );
188 private ManagedRepositoryContent createManagedRepositoryContent( String repoId )
189 throws RepositoryAdminException
191 ManagedRepositoryContent repoContent = new ManagedDefaultRepositoryContent();
192 repoContent.setRepository( defaultManagedRepositoryAdmin.getManagedRepository( repoId ) );
198 public void tearDown()
204 // MRM-1232 - Unable to get artifacts from repositories which requires Repository Manager role using repository group
206 public void testRepositoryGroupFirstRepositoryRequiresAuthentication()
209 DavResourceLocator locator = new ArchivaDavResourceLocator( "", "/repository/" + LOCAL_REPO_GROUP
210 + "/org/apache/archiva/archiva/1.2-SNAPSHOT/archiva-1.2-SNAPSHOT.jar", LOCAL_REPO_GROUP,
211 new ArchivaDavLocatorFactory() );
213 ManagedRepositoryContent internalRepo = createManagedRepositoryContent( INTERNAL_REPO );
214 ManagedRepositoryContent releasesRepo = createManagedRepositoryContent( RELEASES_REPO );
218 archivaConfigurationControl.reset();
219 archivaConfigurationControl.expectAndReturn( archivaConfiguration.getConfiguration(), config );
220 requestControl.expectAndReturn( request.getMethod(), "GET", 2 );
221 repoContentFactoryControl.expectAndReturn( repoFactory.getManagedRepositoryContent( RELEASES_REPO ),
223 requestControl.expectAndReturn( request.getRemoteAddr(), "http://localhost:8080", 2 );
224 requestControl.expectAndReturn( request.getDavSession(), new ArchivaDavSession(), 2 );
225 repoRequestControl.expectAndReturn(
226 repoRequest.isSupportFile( "org/apache/archiva/archiva/1.2-SNAPSHOT/archiva-1.2-SNAPSHOT.jar" ),
228 repoRequestControl.expectAndReturn(
229 repoRequest.isDefault( "org/apache/archiva/archiva/1.2-SNAPSHOT/archiva-1.2-SNAPSHOT.jar" ), false );
230 repoRequestControl.expectAndReturn(
231 repoRequest.toArtifactReference( "org/apache/archiva/archiva/1.2-SNAPSHOT/archiva-1.2-SNAPSHOT.jar" ),
233 repoRequestControl.expectAndReturn(
234 repoRequest.toNativePath( "org/apache/archiva/archiva/1.2-SNAPSHOT/archiva-1.2-SNAPSHOT.jar",
236 new File( config.findManagedRepositoryById( INTERNAL_REPO ).getLocation(),
237 "target/test-classes/internal/org/apache/archiva/archiva/1.2-SNAPSHOT/archiva-1.2-SNAPSHOT.jar" ).getPath() );
238 repoContentFactoryControl.expectAndReturn( repoFactory.getManagedRepositoryContent( INTERNAL_REPO ),
241 archivaConfigurationControl.replay();
242 requestControl.replay();
243 repoContentFactoryControl.replay();
244 repoRequestControl.replay();
246 resourceFactory.createResource( locator, request, response );
248 archivaConfigurationControl.verify();
249 requestControl.verify();
250 repoContentFactoryControl.verify();
251 repoRequestControl.verify();
253 fail( "A DavException with 401 error code should have been thrown." );
255 catch ( DavException e )
257 assertEquals( 401, e.getErrorCode() );
262 public void testRepositoryGroupLastRepositoryRequiresAuthentication()
265 DavResourceLocator locator = new ArchivaDavResourceLocator( "", "/repository/" + LOCAL_REPO_GROUP
266 + "/org/apache/archiva/archiva/1.2-SNAPSHOT/archiva-1.2-SNAPSHOT.jar", LOCAL_REPO_GROUP,
267 new ArchivaDavLocatorFactory() );
269 List<RepositoryGroupConfiguration> repoGroups = new ArrayList<RepositoryGroupConfiguration>();
270 RepositoryGroupConfiguration repoGroup = new RepositoryGroupConfiguration();
271 repoGroup.setId( LOCAL_REPO_GROUP );
272 repoGroup.addRepository( INTERNAL_REPO );
273 repoGroup.addRepository( RELEASES_REPO );
275 repoGroups.add( repoGroup );
277 config.setRepositoryGroups( repoGroups );
279 ManagedRepositoryContent internalRepo = createManagedRepositoryContent( INTERNAL_REPO );
281 ManagedRepositoryContent releasesRepo = createManagedRepositoryContent( RELEASES_REPO );
285 archivaConfigurationControl.reset();
286 archivaConfigurationControl.expectAndReturn( archivaConfiguration.getConfiguration(), config );
287 requestControl.expectAndReturn( request.getMethod(), "GET", 2 );
288 repoContentFactoryControl.expectAndReturn( repoFactory.getManagedRepositoryContent( INTERNAL_REPO ),
290 repoContentFactoryControl.expectAndReturn( repoFactory.getManagedRepositoryContent( RELEASES_REPO ),
292 requestControl.expectAndReturn( request.getRemoteAddr(), "http://localhost:8080", 2 );
293 requestControl.expectAndReturn( request.getDavSession(), new ArchivaDavSession(), 2 );
294 repoRequestControl.expectAndReturn(
295 repoRequest.isSupportFile( "org/apache/archiva/archiva/1.2-SNAPSHOT/archiva-1.2-SNAPSHOT.jar" ),
297 repoRequestControl.expectAndReturn(
298 repoRequest.isDefault( "org/apache/archiva/archiva/1.2-SNAPSHOT/archiva-1.2-SNAPSHOT.jar" ), false );
299 repoRequestControl.expectAndReturn(
300 repoRequest.toArtifactReference( "org/apache/archiva/archiva/1.2-SNAPSHOT/archiva-1.2-SNAPSHOT.jar" ),
302 repoRequestControl.expectAndReturn(
303 repoRequest.toNativePath( "org/apache/archiva/archiva/1.2-SNAPSHOT/archiva-1.2-SNAPSHOT.jar",
305 new File( config.findManagedRepositoryById( INTERNAL_REPO ).getLocation(),
306 "target/test-classes/internal/org/apache/archiva/archiva/1.2-SNAPSHOT/archiva-1.2-SNAPSHOT.jar" ).getPath() );
308 archivaConfigurationControl.replay();
309 requestControl.replay();
310 repoContentFactoryControl.replay();
311 repoRequestControl.replay();
313 resourceFactory.createResource( locator, request, response );
315 archivaConfigurationControl.verify();
316 requestControl.verify();
317 repoContentFactoryControl.verify();
318 repoRequestControl.verify();
320 fail( "A DavException with 401 error code should have been thrown." );
322 catch ( DavException e )
324 assertEquals( 401, e.getErrorCode() );
329 public void testRepositoryGroupArtifactDoesNotExistInAnyOfTheReposAuthenticationDisabled()
332 DavResourceLocator locator = new ArchivaDavResourceLocator( "", "/repository/" + LOCAL_REPO_GROUP
333 + "/org/apache/archiva/archiva/1.2-SNAPSHOT/archiva-1.2-SNAPSHOT.jar", LOCAL_REPO_GROUP,
334 new ArchivaDavLocatorFactory() );
336 defaultManagedRepositoryAdmin.addManagedRepository(
337 createManagedRepository( LOCAL_MIRROR_REPO, new File( "target/test-classes/local-mirror" ).getPath(),
338 "default" ), false, null );
340 List<RepositoryGroupConfiguration> repoGroups = new ArrayList<RepositoryGroupConfiguration>();
341 RepositoryGroupConfiguration repoGroup = new RepositoryGroupConfiguration();
342 repoGroup.setId( LOCAL_REPO_GROUP );
343 repoGroup.addRepository( INTERNAL_REPO );
344 repoGroup.addRepository( LOCAL_MIRROR_REPO );
346 repoGroups.add( repoGroup );
348 config.setRepositoryGroups( repoGroups );
350 ManagedRepositoryContent internalRepo = createManagedRepositoryContent( INTERNAL_REPO );
351 ManagedRepositoryContent localMirrorRepo = createManagedRepositoryContent( LOCAL_MIRROR_REPO );
355 archivaConfigurationControl.reset();
356 archivaConfigurationControl.expectAndReturn( archivaConfiguration.getConfiguration(), config );
357 requestControl.expectAndReturn( request.getMethod(), "GET", 4 );
358 repoContentFactoryControl.expectAndReturn( repoFactory.getManagedRepositoryContent( INTERNAL_REPO ),
360 repoContentFactoryControl.expectAndReturn( repoFactory.getManagedRepositoryContent( LOCAL_MIRROR_REPO ),
362 requestControl.expectAndReturn( request.getRemoteAddr(), "http://localhost:8080", 4 );
363 requestControl.expectAndReturn( request.getDavSession(), new ArchivaDavSession(), 4 );
364 repoRequestControl.expectAndReturn(
365 repoRequest.isSupportFile( "org/apache/archiva/archiva/1.2-SNAPSHOT/archiva-1.2-SNAPSHOT.jar" ), false,
367 repoRequestControl.expectAndReturn(
368 repoRequest.isDefault( "org/apache/archiva/archiva/1.2-SNAPSHOT/archiva-1.2-SNAPSHOT.jar" ), false, 2 );
369 repoRequestControl.expectAndReturn(
370 repoRequest.toArtifactReference( "org/apache/archiva/archiva/1.2-SNAPSHOT/archiva-1.2-SNAPSHOT.jar" ),
372 repoRequestControl.expectAndReturn(
373 repoRequest.toNativePath( "org/apache/archiva/archiva/1.2-SNAPSHOT/archiva-1.2-SNAPSHOT.jar",
375 new File( config.findManagedRepositoryById( INTERNAL_REPO ).getLocation(),
376 "target/test-classes/internal/org/apache/archiva/archiva/1.2-SNAPSHOT/archiva-1.2-SNAPSHOT.jar" ).getPath() );
378 repoRequestControl.expectAndReturn(
379 repoRequest.toNativePath( "org/apache/archiva/archiva/1.2-SNAPSHOT/archiva-1.2-SNAPSHOT.jar",
381 new File( config.findManagedRepositoryById( LOCAL_MIRROR_REPO ).getLocation(),
382 "target/test-classes/internal/org/apache/archiva/archiva/1.2-SNAPSHOT/archiva-1.2-SNAPSHOT.jar" ).getPath() );
384 archivaConfigurationControl.replay();
385 requestControl.replay();
386 repoContentFactoryControl.replay();
387 repoRequestControl.replay();
389 resourceFactory.createResource( locator, request, response );
391 archivaConfigurationControl.verify();
392 requestControl.verify();
393 repoContentFactoryControl.verify();
394 repoRequestControl.verify();
396 fail( "A DavException with 404 error code should have been thrown." );
398 catch ( DavException e )
400 assertEquals( 404, e.getErrorCode() );
406 public void testRequestArtifactMetadataThreePartsRepoHasDefaultLayout()
409 // should fetch metadata
410 DavResourceLocator locator =
411 new ArchivaDavResourceLocator( "", "/repository/" + INTERNAL_REPO + "/eclipse/jdtcore/maven-metadata.xml",
412 INTERNAL_REPO, new ArchivaDavLocatorFactory() );
414 ManagedRepositoryContent internalRepo = createManagedRepositoryContent( INTERNAL_REPO );
416 // use actual object (this performs the isMetadata, isDefault and isSupportFile check!)
417 RepositoryRequest repoRequest = new RepositoryRequest( new LegacyPathParser( this.archivaConfiguration ) );
418 resourceFactory.setRepositoryRequest( repoRequest );
422 archivaConfigurationControl.reset();
423 archivaConfigurationControl.expectAndReturn( archivaConfiguration.getConfiguration(), config );
424 repoContentFactoryControl.expectAndReturn( repoFactory.getManagedRepositoryContent( INTERNAL_REPO ),
426 requestControl.expectAndReturn( request.getMethod(), "GET", 3 );
427 requestControl.expectAndReturn( request.getRemoteAddr(), "http://localhost:8080", 3 );
428 requestControl.expectAndReturn( request.getDavSession(), new ArchivaDavSession(), 2 );
429 requestControl.expectAndReturn( request.getRequestURI(),
430 "http://localhost:8080/archiva/repository/" + INTERNAL_REPO
431 + "/eclipse/jdtcore/maven-metadata.xml" );
432 response.addHeader( "Pragma", "no-cache" );
433 responseControl.setVoidCallable();
435 response.addHeader( "Cache-Control", "no-cache" );
436 responseControl.setVoidCallable();
438 long date = 2039842134;
439 response.addDateHeader( "last-modified", date );
440 responseControl.setVoidCallable();
442 archivaConfigurationControl.replay();
443 repoContentFactoryControl.replay();
444 requestControl.replay();
445 responseControl.replay();
447 resourceFactory.createResource( locator, request, response );
449 archivaConfigurationControl.verify();
450 repoContentFactoryControl.verify();
451 requestControl.verify();
452 responseControl.verify();
454 catch ( DavException e )
456 fail( "A DavException should not have been thrown!" );
461 public void testRequestArtifactMetadataTwoPartsRepoHasDefaultLayout()
464 // should not fetch metadata
465 DavResourceLocator locator =
466 new ArchivaDavResourceLocator( "", "/repository/" + INTERNAL_REPO + "/eclipse/maven-metadata.xml",
467 INTERNAL_REPO, new ArchivaDavLocatorFactory() );
469 ManagedRepositoryContent internalRepo = createManagedRepositoryContent( INTERNAL_REPO );
471 // use actual object (this performs the isMetadata, isDefault and isSupportFile check!)
472 RepositoryRequest repoRequest = new RepositoryRequest( new LegacyPathParser( this.archivaConfiguration ) );
473 resourceFactory.setRepositoryRequest( repoRequest );
477 archivaConfigurationControl.reset();
478 archivaConfigurationControl.expectAndReturn( archivaConfiguration.getConfiguration(), config );
479 repoContentFactoryControl.expectAndReturn( repoFactory.getManagedRepositoryContent( INTERNAL_REPO ),
481 requestControl.expectAndReturn( request.getMethod(), "GET", 2 );
482 requestControl.expectAndReturn( request.getRemoteAddr(), "http://localhost:8080", 2 );
483 requestControl.expectAndReturn( request.getDavSession(), new ArchivaDavSession(), 2 );
485 archivaConfigurationControl.replay();
486 repoContentFactoryControl.replay();
487 requestControl.replay();
489 resourceFactory.createResource( locator, request, response );
491 archivaConfigurationControl.verify();
492 repoContentFactoryControl.verify();
493 requestControl.verify();
495 fail( "A 404 error should have been thrown!" );
497 catch ( DavException e )
499 assertEquals( 404, e.getErrorCode() );
504 public void testRequestMetadataRepoIsLegacy()
507 defaultManagedRepositoryAdmin.addManagedRepository(
508 createManagedRepository( LEGACY_REPO, new File( "target/test-classes/" + LEGACY_REPO ).getPath(),
509 "legacy" ), false, null );
510 DavResourceLocator locator =
511 new ArchivaDavResourceLocator( "", "/repository/" + LEGACY_REPO + "/eclipse/maven-metadata.xml",
512 LEGACY_REPO, new ArchivaDavLocatorFactory() );
514 ManagedRepositoryContent legacyRepo = createManagedRepositoryContent( LEGACY_REPO );
516 // use actual object (this performs the isMetadata, isDefault and isSupportFile check!)
517 RepositoryRequest repoRequest = new RepositoryRequest( new LegacyPathParser( this.archivaConfiguration ) );
518 resourceFactory.setRepositoryRequest( repoRequest );
522 archivaConfigurationControl.reset();
523 archivaConfigurationControl.expectAndReturn( archivaConfiguration.getConfiguration(), config );
524 repoContentFactoryControl.expectAndReturn( repoFactory.getManagedRepositoryContent( LEGACY_REPO ),
526 requestControl.expectAndReturn( request.getMethod(), "GET", 2 );
527 requestControl.expectAndReturn( request.getRemoteAddr(), "http://localhost:8080", 2 );
528 requestControl.expectAndReturn( request.getDavSession(), new ArchivaDavSession(), 2 );
530 archivaConfigurationControl.replay();
531 repoContentFactoryControl.replay();
532 requestControl.replay();
534 resourceFactory.createResource( locator, request, response );
536 archivaConfigurationControl.verify();
537 repoContentFactoryControl.verify();
538 requestControl.verify();
540 fail( "A 404 error should have been thrown!" );
542 catch ( DavException e )
544 assertEquals( 404, e.getErrorCode() );
548 class OverridingArchivaDavResourceFactory
549 extends ArchivaDavResourceFactory
552 OverridingArchivaDavResourceFactory( ApplicationContext applicationContext, PlexusSisuBridge plexusSisuBridge,
553 ArchivaConfiguration archivaConfiguration )
554 throws PlexusSisuBridgeException
556 super( applicationContext, plexusSisuBridge, archivaConfiguration );
559 protected boolean isAuthorized( DavServletRequest request, String repositoryId )
562 if ( RELEASES_REPO.equals( repositoryId ) )
564 throw new UnauthorizedDavException( repositoryId,
565 "You are not authenticated and authorized to access any repository." );
573 protected String getActivePrincipal( DavServletRequest request )
579 class OverridingRepositoryProxyConnectors
580 extends DefaultRepositoryProxyConnectors
582 public File fetchMetatadaFromProxies( ManagedRepositoryContent repository, String logicalPath )
584 File target = new File( repository.getRepoRoot(), logicalPath );
587 FileUtils.copyFile( new File( "target/test-classes/maven-metadata.xml" ), target );
589 catch ( IOException e )