Skip to content

Commit 3f095c3

Browse files
committed
[WAGON-583] WebDavWagon and tests now adhere to RFC 4918
- Handle 409 Conflict status when parent collections don't exist - Handle redirects on MKCOL operations - Accept 200 OK status for already-existing collections - Fix tests to use WebDAV PROPFIND instead of filesystem checks
1 parent 85a276d commit 3f095c3

2 files changed

Lines changed: 67 additions & 24 deletions

File tree

wagon-providers/wagon-webdav-jackrabbit/src/main/java/org/apache/maven/wagon/providers/webdav/WebDavWagon.java

Lines changed: 48 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -122,16 +122,24 @@ protected void mkdirs(String dir) throws IOException {
122122
do {
123123
String url = baseUrl + "/" + navigator.getPath();
124124
status = doMkCol(url);
125-
if (status == HttpStatus.SC_CREATED || status == HttpStatus.SC_METHOD_NOT_ALLOWED) {
125+
// RFC 4918: Accept 201 Created, 200 OK (already exists), 405 Method Not Allowed (already exists)
126+
if (status == HttpStatus.SC_CREATED
127+
|| status == HttpStatus.SC_OK
128+
|| status == HttpStatus.SC_METHOD_NOT_ALLOWED) {
126129
break;
127130
}
131+
// RFC 4918: 409 Conflict means intermediate collection is missing, continue traversing backwards
132+
if (status == HttpStatus.SC_CONFLICT) {
133+
continue;
134+
}
128135
} while (navigator.backward());
129136

130137
// traverse forward creating missing directories
131138
while (navigator.forward()) {
132139
String url = baseUrl + "/" + navigator.getPath();
133140
status = doMkCol(url);
134-
if (status != HttpStatus.SC_CREATED) {
141+
// RFC 4918: Accept 201 Created or 200 OK (if collection already exists from another request)
142+
if (status != HttpStatus.SC_CREATED && status != HttpStatus.SC_OK) {
135143
throw new IOException("Unable to create collection: " + url + "; status code = " + status);
136144
}
137145
}
@@ -140,7 +148,21 @@ protected void mkdirs(String dir) throws IOException {
140148
private int doMkCol(String url) throws IOException {
141149
HttpMkcol method = new HttpMkcol(url);
142150
try (CloseableHttpResponse closeableHttpResponse = execute(method)) {
143-
return closeableHttpResponse.getStatusLine().getStatusCode();
151+
int statusCode = closeableHttpResponse.getStatusLine().getStatusCode();
152+
153+
// RFC 4918: Handle redirects for MKCOL
154+
// 3xx redirects should be followed to the new location
155+
if (statusCode >= HttpStatus.SC_MULTIPLE_CHOICES && statusCode < HttpStatus.SC_BAD_REQUEST) {
156+
org.apache.http.Header locationHeader = closeableHttpResponse.getFirstHeader("Location");
157+
if (locationHeader != null) {
158+
String redirectUrl = locationHeader.getValue();
159+
// Recursive call to handle redirect - execute() will handle the redirect automatically
160+
// but we need to return the final status
161+
return doMkCol(redirectUrl);
162+
}
163+
}
164+
165+
return statusCode;
144166
} catch (HttpException e) {
145167
throw new IOException(e.getMessage(), e);
146168
} finally {
@@ -172,6 +194,29 @@ public void putDirectory(File sourceDirectory, String destinationDirectory)
172194
}
173195
}
174196

197+
protected boolean collectionExists(String url) throws IOException {
198+
DavPropertyNameSet nameSet = new DavPropertyNameSet();
199+
nameSet.add(DavPropertyName.create(DavConstants.PROPERTY_RESOURCETYPE));
200+
201+
CloseableHttpResponse closeableHttpResponse = null;
202+
HttpPropfind method = null;
203+
try {
204+
method = new HttpPropfind(url, nameSet, DavConstants.DEPTH_0);
205+
closeableHttpResponse = execute(method);
206+
int statusCode = closeableHttpResponse.getStatusLine().getStatusCode();
207+
return statusCode == HttpStatus.SC_OK || statusCode == HttpStatus.SC_MULTI_STATUS;
208+
} catch (HttpException e) {
209+
throw new IOException(e.getMessage(), e);
210+
} finally {
211+
if (method != null) {
212+
method.releaseConnection();
213+
}
214+
if (closeableHttpResponse != null) {
215+
closeableHttpResponse.close();
216+
}
217+
}
218+
}
219+
175220
private boolean isDirectory(String url) throws IOException, DavException {
176221
DavPropertyNameSet nameSet = new DavPropertyNameSet();
177222
nameSet.add(DavPropertyName.create(DavConstants.PROPERTY_RESOURCETYPE));

wagon-providers/wagon-webdav-jackrabbit/src/test/java/org/apache/maven/wagon/providers/webdav/WebDavWagonTest.java

Lines changed: 19 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -137,34 +137,35 @@ public void testMkdirs() throws Exception {
137137
wagon.connect(testRepository, getAuthInfo());
138138

139139
try {
140-
File dir = getRepositoryDirectory();
141-
142-
// check basedir also doesn't exist and will need to be created
143-
dir = new File(dir, testRepository.getBasedir());
144-
assertFalse(dir.exists());
140+
String repositoryUrl = testRepository.getUrl();
145141

146142
// test leading /
147-
assertFalse(new File(dir, "foo").exists());
143+
String fooUrl = repositoryUrl + (repositoryUrl.endsWith("/") ? "" : "/") + "foo";
144+
assertFalse("Collection should not exist before creation", wagon.collectionExists(fooUrl));
148145
wagon.mkdirs("/foo");
149-
assertTrue(new File(dir, "foo").exists());
146+
assertTrue("Collection should exist after creation", wagon.collectionExists(fooUrl));
150147

151148
// test trailing /
152-
assertFalse(new File(dir, "bar").exists());
149+
String barUrl = repositoryUrl + (repositoryUrl.endsWith("/") ? "" : "/") + "bar";
150+
assertFalse("Collection should not exist before creation", wagon.collectionExists(barUrl));
153151
wagon.mkdirs("bar/");
154-
assertTrue(new File(dir, "bar").exists());
152+
assertTrue("Collection should exist after creation", wagon.collectionExists(barUrl));
155153

156-
// test when already exists
154+
// test when already exists (should not fail)
157155
wagon.mkdirs("bar");
156+
assertTrue("Collection should still exist", wagon.collectionExists(barUrl));
158157

159158
// test several parts
160-
assertFalse(new File(dir, "1/2/3/4").exists());
159+
String deepUrl = repositoryUrl + (repositoryUrl.endsWith("/") ? "" : "/") + "1/2/3/4";
160+
assertFalse("Deep collection should not exist before creation", wagon.collectionExists(deepUrl));
161161
wagon.mkdirs("1/2/3/4");
162-
assertTrue(new File(dir, "1/2/3/4").exists());
162+
assertTrue("Deep collection should exist after creation", wagon.collectionExists(deepUrl));
163163

164164
// test additional part and trailing /
165-
assertFalse(new File(dir, "1/2/3/4/5").exists());
165+
String deeperUrl = repositoryUrl + (repositoryUrl.endsWith("/") ? "" : "/") + "1/2/3/4/5";
166+
assertFalse("Deeper collection should not exist before creation", wagon.collectionExists(deeperUrl));
166167
wagon.mkdirs("1/2/3/4/5/");
167-
assertTrue(new File(dir, "1/2/3/4").exists());
168+
assertTrue("Deeper collection should exist after creation", wagon.collectionExists(deeperUrl));
168169
} finally {
169170
wagon.disconnect();
170171

@@ -186,16 +187,13 @@ public void testMkdirsWithNoBasedir() throws Exception {
186187
wagon.connect(testRepository, getAuthInfo());
187188

188189
try {
189-
File dir = getRepositoryDirectory();
190-
191-
// check basedir also doesn't exist and will need to be created
192-
dir = new File(dir, testRepository.getBasedir());
193-
assertTrue(dir.exists());
190+
String repositoryUrl = testRepository.getUrl();
194191

195192
// test leading /
196-
assertFalse(new File(dir, "foo").exists());
193+
String fooUrl = repositoryUrl + (repositoryUrl.endsWith("/") ? "" : "/") + "foo";
194+
assertFalse("Collection should not exist before creation", wagon.collectionExists(fooUrl));
197195
wagon.mkdirs("/foo");
198-
assertTrue(new File(dir, "foo").exists());
196+
assertTrue("Collection should exist after creation", wagon.collectionExists(fooUrl));
199197
} finally {
200198
wagon.disconnect();
201199

0 commit comments

Comments
 (0)