Skip to content

Commit a7fd719

Browse files
authored
Merge pull request #128 from a-schild/fix/87-connector-lifecycle
Reference-count connector lifecycle (#87)
2 parents 3d4371a + 98d57eb commit a7fd719

3 files changed

Lines changed: 91 additions & 5 deletions

File tree

Changelog.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
# Changelog for nextcloud api
22

33
## Version 14.2.0
4+
- Fix connector lifecycle: closing a `NextcloudConnector` now shuts down the
5+
shared HTTP client only once the last open connector is closed, so closing
6+
one connector no longer breaks others still in use (issue #87). Use
7+
`shutdown()` to force an immediate teardown.
48
- Add system tags support: list, create and delete system tags, and assign or
59
remove tags on a file via the new `SystemTags` connector and
610
`NextcloudConnector` methods (issue #110)

src/main/java/org/aarboard/nextcloud/api/NextcloudConnector.java

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,15 @@
4949

5050
public class NextcloudConnector implements AutoCloseable {
5151

52+
/**
53+
* Number of open connectors sharing the static HTTP client, so it is only
54+
* shut down once the last connector is closed (see issue #87).
55+
*/
56+
private static final java.util.concurrent.atomic.AtomicInteger OPEN_INSTANCES =
57+
new java.util.concurrent.atomic.AtomicInteger(0);
58+
59+
private volatile boolean closed = false;
60+
5261
private final ServerConfig serverConfig;
5362
private final ProvisionConnector pc;
5463
private final FilesharingConnector fc;
@@ -121,6 +130,7 @@ public NextcloudConnector(String originalServiceUrl, AuthenticationConfig authen
121130
fl = new Files(this.serverConfig);
122131
gf = new GroupFolders(this.serverConfig);
123132
st = new SystemTags(this.serverConfig);
133+
OPEN_INSTANCES.incrementAndGet();
124134

125135
} catch (MalformedURLException e) {
126136
throw new IllegalArgumentException(e);
@@ -144,6 +154,7 @@ public NextcloudConnector(String serverName, boolean useHTTPS, int port,
144154
fl = new Files(this.serverConfig);
145155
gf = new GroupFolders(this.serverConfig);
146156
st = new SystemTags(this.serverConfig);
157+
OPEN_INSTANCES.incrementAndGet();
147158
}
148159

149160
/**
@@ -193,8 +204,11 @@ public void setWebDavPathResolverAsType(final WebDavPathResolverBuilder.TYPE typ
193204
}
194205

195206
/**
196-
* Close the HTTP client. Perform this to cleanly shut down this
197-
* application.
207+
* Immediately shuts down the shared HTTP client, regardless of how many
208+
* other {@link NextcloudConnector} instances are still open. Prefer
209+
* {@link #close()} (e.g. via try-with-resources), which only shuts the
210+
* shared client down once the last connector is closed. Use this only when
211+
* you explicitly want to tear everything down at once.
198212
*
199213
* @throws IOException In case of IO errors
200214
*/
@@ -401,13 +415,22 @@ public void setGroupFolderQuota(int groupFolderId, long quota) {
401415
}
402416

403417
/**
404-
* Close the HTTP client. Perform this to cleanly shut down this
405-
* application.
418+
* Closes this connector. The shared HTTP client is only shut down once the
419+
* last open {@link NextcloudConnector} has been closed, so closing one
420+
* connector no longer breaks others that are still in use (see issue #87).
421+
* Idempotent: closing an already-closed connector does nothing.
406422
*
407423
* @throws Exception In case of errors
408424
*/
425+
@Override
409426
public void close() throws Exception {
410-
shutdown();
427+
if (closed) {
428+
return;
429+
}
430+
closed = true;
431+
if (OPEN_INSTANCES.decrementAndGet() <= 0) {
432+
shutdown();
433+
}
411434
}
412435

413436
/**
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/*
2+
* Copyright (C) 2026 a.schild
3+
*
4+
* This program is free software: you can redistribute it and/or modify
5+
* it under the terms of the GNU General Public License as published by
6+
* the Free Software Foundation, either version 3 of the License, or
7+
* (at your option) any later version.
8+
*
9+
* This program is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
* GNU General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU General Public License
15+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
16+
*/
17+
package org.aarboard.nextcloud.api;
18+
19+
import static org.junit.Assert.assertNotNull;
20+
21+
import org.junit.Test;
22+
23+
/**
24+
* Verifies the shared-HTTP-client lifecycle: closing one connector must not
25+
* shut the shared client down while another connector is still in use
26+
* (issue #87).
27+
*
28+
* @author a.schild
29+
*/
30+
public class TestConnectorLifecycle {
31+
32+
@Test
33+
public void testClosingOneConnectorDoesNotBreakAnother() throws Exception {
34+
TestHelper th = new TestHelper();
35+
String serverName = th.getServerName();
36+
if (serverName == null) {
37+
return;
38+
}
39+
NextcloudConnector first = newConnector(th);
40+
NextcloudConnector second = newConnector(th);
41+
try {
42+
assertNotNull(first.getShares());
43+
assertNotNull(second.getShares());
44+
45+
// Closing the first connector must not tear down the shared client
46+
// that the second one still relies on.
47+
first.close();
48+
49+
assertNotNull(second.getShares());
50+
} finally {
51+
second.close();
52+
}
53+
}
54+
55+
private NextcloudConnector newConnector(TestHelper th) {
56+
return new NextcloudConnector(th.getServerName(), th.getServerPort() == 443,
57+
th.getServerPort(), th.getUserName(), th.getPassword());
58+
}
59+
}

0 commit comments

Comments
 (0)