• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

tomdesair / tus-java-server / 28847441985

07 Jul 2026 06:52AM UTC coverage: 95.749% (+0.9%) from 94.813%
28847441985

Pull #105

github

web-flow
Merge 5d520af31 into 4b2f45149
Pull Request #105: WIP feat: Implement IETF Resumable Uploads for HTTP (RUFH) Protocol

1089 of 1190 branches covered (91.51%)

Branch coverage included in aggregate %.

748 of 759 new or added lines in 37 files covered. (98.55%)

24 existing lines in 3 files now uncovered.

2560 of 2621 relevant lines covered (97.67%)

6.29 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

92.59
/src/main/java/me/desair/tus/server/upload/cache/ThreadLocalCachedStorageAndLockingService.java
1
package me.desair.tus.server.upload.cache;
2

3
import java.io.IOException;
4
import java.io.InputStream;
5
import java.io.OutputStream;
6
import java.lang.ref.WeakReference;
7
import java.util.Objects;
8
import me.desair.tus.server.checksum.ChecksumAlgorithm;
9
import me.desair.tus.server.exception.TusException;
10
import me.desair.tus.server.exception.UploadNotFoundException;
11
import me.desair.tus.server.upload.UploadId;
12
import me.desair.tus.server.upload.UploadIdFactory;
13
import me.desair.tus.server.upload.UploadInfo;
14
import me.desair.tus.server.upload.UploadLock;
15
import me.desair.tus.server.upload.UploadLockingService;
16
import me.desair.tus.server.upload.UploadStorageService;
17
import me.desair.tus.server.upload.concatenation.UploadConcatenationService;
18

19
/**
20
 * Combined implementation of {@link UploadStorageService} and {@link UploadLockingService}. Uses
21
 * both of them as delegates but allowing to reduce disk operations during a request processing by
22
 * caching UploadInfo in the memory. UploadLockingService service is used as a delegate to cleanup
23
 * cached data on releasing a lock.
24
 */
25
public class ThreadLocalCachedStorageAndLockingService
26
    implements UploadLockingService, UploadStorageService {
27

28
  private final ThreadLocal<WeakReference<UploadInfo>> uploadInfoCache = new ThreadLocal<>();
10✔
29
  private final UploadLockingService lockingServiceDelegate;
30
  private final UploadStorageService storageServiceDelegate;
31
  private UploadIdFactory idFactory;
32

33
  /** Constructor of ThreadLocalCachedStorageAndLockingService. */
34
  public ThreadLocalCachedStorageAndLockingService(
35
      UploadStorageService storageServiceDelegate, UploadLockingService lockingServiceDelegate) {
4✔
36
    if (storageServiceDelegate.getClass() == ThreadLocalCachedStorageAndLockingService.class) {
8✔
37
      this.storageServiceDelegate =
12✔
38
          ((ThreadLocalCachedStorageAndLockingService) storageServiceDelegate)
39
              .storageServiceDelegate;
40
    } else {
41
      this.storageServiceDelegate = storageServiceDelegate;
6✔
42
    }
43
    if (lockingServiceDelegate.getClass() == ThreadLocalCachedStorageAndLockingService.class) {
8✔
44
      this.lockingServiceDelegate =
12✔
45
          ((ThreadLocalCachedStorageAndLockingService) lockingServiceDelegate)
46
              .lockingServiceDelegate;
47
    } else {
48
      this.lockingServiceDelegate = lockingServiceDelegate;
6✔
49
    }
50
  }
2✔
51

52
  @Override
53
  public UploadInfo getUploadInfo(UploadId id) throws IOException {
54
    UploadInfo uploadInfo;
55
    WeakReference<UploadInfo> ref = uploadInfoCache.get();
10✔
56
    if (ref == null || (uploadInfo = ref.get()) == null || !id.equals(uploadInfo.getId())) {
26✔
57
      uploadInfo = storageServiceDelegate.getUploadInfo(id);
10✔
58
      uploadInfoCache.set(new WeakReference<>(uploadInfo));
14✔
59
    }
60
    return uploadInfo;
4✔
61
  }
62

63
  @Override
64
  public UploadInfo getUploadInfo(String uploadUrl, String ownerKey) throws IOException {
65
    UploadInfo uploadInfo = getUploadInfo(idFactory.readUploadId(uploadUrl));
14✔
66
    if (uploadInfo == null || !Objects.equals(uploadInfo.getOwnerKey(), ownerKey)) {
14✔
67
      uploadInfo = storageServiceDelegate.getUploadInfo(uploadUrl, ownerKey);
12✔
68
      uploadInfoCache.set(new WeakReference<>(uploadInfo));
14✔
69
    }
70
    return uploadInfo;
4✔
71
  }
72

73
  @Override
74
  public void update(UploadInfo uploadInfo) throws IOException, UploadNotFoundException {
75
    storageServiceDelegate.update(uploadInfo);
8✔
76
    uploadInfoCache.set(new WeakReference<>(uploadInfo));
14✔
77
  }
2✔
78

79
  @Override
80
  public void setIdFactory(UploadIdFactory idFactory) {
81
    this.idFactory = idFactory;
6✔
82
    this.storageServiceDelegate.setIdFactory(idFactory);
8✔
83
    this.lockingServiceDelegate.setIdFactory(idFactory);
8✔
84
  }
2✔
85

86
  @Override
87
  public String getUploadUri() {
88
    return storageServiceDelegate.getUploadUri();
8✔
89
  }
90

91
  @Override
92
  public UploadInfo append(UploadInfo upload, InputStream inputStream)
93
      throws IOException, TusException {
94
    UploadInfo info = storageServiceDelegate.append(upload, inputStream);
12✔
95
    uploadInfoCache.set(new WeakReference<>(info));
14✔
96
    return info;
4✔
97
  }
98

99
  @Override
100
  public void setMaxUploadSize(Long maxUploadSize) {
101
    storageServiceDelegate.setMaxUploadSize(maxUploadSize);
8✔
102
  }
2✔
103

104
  @Override
105
  public long getMaxUploadSize() {
106
    return storageServiceDelegate.getMaxUploadSize();
8✔
107
  }
108

109
  @Override
110
  public UploadInfo create(UploadInfo info, String ownerKey) throws IOException {
111
    UploadInfo uploadInfo = storageServiceDelegate.create(info, ownerKey);
12✔
112
    uploadInfoCache.set(new WeakReference<>(uploadInfo));
14✔
113
    return uploadInfo;
4✔
114
  }
115

116
  @Override
117
  public InputStream getUploadedBytes(String uploadUri, String ownerKey)
118
      throws IOException, UploadNotFoundException {
119
    return storageServiceDelegate.getUploadedBytes(uploadUri, ownerKey);
12✔
120
  }
121

122
  @Override
123
  public InputStream getUploadedBytes(UploadId id) throws IOException, UploadNotFoundException {
124
    return storageServiceDelegate.getUploadedBytes(id);
10✔
125
  }
126

127
  @Override
128
  public void copyUploadTo(UploadInfo info, OutputStream outputStream)
129
      throws UploadNotFoundException, IOException {
130
    storageServiceDelegate.copyUploadTo(info, outputStream);
10✔
131
    uploadInfoCache.set(new WeakReference<>(info));
14✔
132
  }
2✔
133

134
  @Override
135
  public void cleanupExpiredUploads(UploadLockingService uploadLockingService) throws IOException {
136
    storageServiceDelegate.cleanupExpiredUploads(uploadLockingService);
8✔
137
    // Since any cached uploads was potentially removed by the storage service
138
    // we clean the cache to prevent any stale state
139
    cleanupCache();
4✔
140
  }
2✔
141

142
  @Override
143
  public void removeLastNumberOfBytes(UploadInfo uploadInfo, long byteCount)
144
      throws UploadNotFoundException, IOException {
145
    storageServiceDelegate.removeLastNumberOfBytes(uploadInfo, byteCount);
10✔
146
    uploadInfoCache.set(new WeakReference<>(uploadInfo));
14✔
147
  }
2✔
148

149
  @Override
150
  public void terminateUpload(UploadInfo uploadInfo) throws UploadNotFoundException, IOException {
151
    storageServiceDelegate.terminateUpload(uploadInfo);
8✔
152
    // Since the upload is terminated and potentially removed by the storage service
153
    // we clean the cache to prevent any stale state
154
    cleanupCache();
4✔
155
  }
2✔
156

157
  @Override
158
  public Long getUploadExpirationPeriod() {
159
    return storageServiceDelegate.getUploadExpirationPeriod();
8✔
160
  }
161

162
  @Override
163
  public void setUploadExpirationPeriod(Long uploadExpirationPeriod) {
164
    storageServiceDelegate.setUploadExpirationPeriod(uploadExpirationPeriod);
8✔
165
  }
2✔
166

167
  @Override
168
  public void setUploadConcatenationService(UploadConcatenationService concatenationService) {
169
    storageServiceDelegate.setUploadConcatenationService(concatenationService);
8✔
170
  }
2✔
171

172
  @Override
173
  public UploadConcatenationService getUploadConcatenationService() {
174
    return storageServiceDelegate.getUploadConcatenationService();
8✔
175
  }
176

177
  @Override
178
  public void setUploadDeduplicationEnabled(boolean enabled) {
179
    storageServiceDelegate.setUploadDeduplicationEnabled(enabled);
8✔
180
  }
2✔
181

182
  @Override
183
  public boolean isUploadDeduplicationEnabled() {
184
    return storageServiceDelegate.isUploadDeduplicationEnabled();
8✔
185
  }
186

187
  @Override
188
  public UploadInfo getUploadInfoByChecksum(String checksum, ChecksumAlgorithm algorithm)
189
      throws IOException {
190
    return storageServiceDelegate.getUploadInfoByChecksum(checksum, algorithm);
12✔
191
  }
192

193
  @Override
194
  public UploadLock lockUploadByUri(String requestUri) throws TusException, IOException {
195
    UploadLock uploadLock = lockingServiceDelegate.lockUploadByUri(requestUri);
10✔
196
    return new CachedLock(uploadLock);
12✔
197
  }
198

199
  @Override
200
  public void cleanupStaleLocks() throws IOException {
201
    lockingServiceDelegate.cleanupStaleLocks();
6✔
202
    cleanupCache();
4✔
203
  }
2✔
204

205
  @Override
206
  public boolean isLocked(UploadId id) {
207
    return lockingServiceDelegate.isLocked(id);
10✔
208
  }
209

210
  @Override
211
  public void registerInputStream(String requestUri, InputStream inputStream) {
212
    lockingServiceDelegate.registerInputStream(requestUri, inputStream);
10✔
213
  }
2✔
214

215
  @Override
216
  public void requestLockRelease(String requestUri) {
217
    lockingServiceDelegate.requestLockRelease(requestUri);
8✔
218
  }
2✔
219

220
  private void cleanupCache() {
221
    WeakReference<UploadInfo> ref = uploadInfoCache.get();
10✔
222
    if (ref != null) {
4✔
223
      uploadInfoCache.remove();
6✔
224
      ref.clear();
4✔
225
    }
226
  }
2✔
227

228
  class CachedLock implements UploadLock {
229

230
    private final UploadLock delegate;
231

232
    CachedLock(UploadLock delegate) {
10✔
233
      this.delegate = delegate;
6✔
234
    }
2✔
235

236
    @Override
237
    public String getUploadUri() {
UNCOV
238
      return delegate != null ? delegate.getUploadUri() : null;
×
239
    }
240

241
    @Override
242
    public void release() {
UNCOV
243
      if (delegate != null) {
×
UNCOV
244
        delegate.release();
×
245
      }
UNCOV
246
    }
×
247

248
    @Override
249
    public void close() throws IOException {
250
      if (delegate != null) {
6✔
251
        delegate.close();
6✔
252
      }
253
      cleanupCache();
6✔
254
    }
2✔
255
  }
256
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc