1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 package org.waarp.http.protocol;
22
23 import org.waarp.common.command.exception.CommandAbstractException;
24 import org.waarp.common.digest.FilesystemBasedDigest;
25 import org.waarp.common.digest.FilesystemBasedDigest.DigestAlgo;
26 import org.waarp.common.logging.WaarpLogger;
27 import org.waarp.common.logging.WaarpLoggerFactory;
28 import org.waarp.common.lru.ConcurrentUtility;
29 import org.waarp.common.utility.ParametersChecker;
30 import org.waarp.common.utility.WaarpStringUtils;
31 import org.waarp.common.utility.WaarpSystemUtil;
32 import org.waarp.http.protocol.servlet.HttpAuthent;
33 import org.waarp.openr66.context.R66BusinessInterface;
34 import org.waarp.openr66.context.filesystem.R66Dir;
35 import org.waarp.openr66.context.filesystem.R66File;
36 import org.waarp.openr66.context.task.exception.OpenR66RunnerErrorException;
37 import org.waarp.openr66.database.data.DbTaskRunner;
38
39 import java.io.File;
40 import java.io.IOException;
41 import java.io.InputStream;
42 import java.io.RandomAccessFile;
43 import java.util.Set;
44 import java.util.UUID;
45
46 import static org.waarp.openr66.context.R66FiniteDualStates.*;
47
48
49
50
51 public class HttpResumableSession extends HttpSessionAbstract {
52 private static final WaarpLogger logger =
53 WaarpLoggerFactory.getLogger(HttpResumableSession.class);
54
55 private final Set<HttpResumableChunkNumber> uploadedChunks =
56 ConcurrentUtility.newConcurrentSet();
57 private final HttpResumableInfo httpResumableInfo;
58 private long size = 0L;
59
60
61
62
63
64
65
66
67
68
69
70 HttpResumableSession(final HttpResumableInfo resumableInfo,
71 final String rulename, final String comment,
72 final HttpAuthent authent)
73 throws IllegalArgumentException {
74 super(authent);
75 this.httpResumableInfo = resumableInfo;
76 if (!WaarpSystemUtil.isJunit()) {
77 final R66BusinessInterface business =
78 checkAuthentR66Business(this, session, authent);
79 final DbTaskRunner runner =
80 getDbTaskRunner(authent.getUserId(), rulename, comment, business);
81 preTasks(business, runner);
82 }
83 }
84
85
86
87
88
89
90
91
92
93
94
95 private DbTaskRunner getDbTaskRunner(final String user, final String rulename,
96 final String comment,
97 final R66BusinessInterface business) {
98 final long uuid = UUID.nameUUIDFromBytes(
99 httpResumableInfo.getIdentifier().getBytes(WaarpStringUtils.UTF8))
100 .getMostSignificantBits();
101 return getDbTaskRunner(user, httpResumableInfo.getFilename(), rulename,
102 uuid, comment, httpResumableInfo.getChunkSize(),
103 business, true);
104 }
105
106
107
108
109 public HttpResumableInfo getHttpResumableInfo() {
110 return httpResumableInfo;
111 }
112
113
114
115
116
117
118
119
120
121
122
123 public final boolean tryWrite(final HttpResumableInfo resumableInfo,
124 final InputStream stream) throws IOException {
125 if (!session.isAuthenticated() || !session.isReady()) {
126 logger.error("Not authenticated or not Ready");
127 return false;
128 }
129 if (!valid(resumableInfo)) {
130 return false;
131 }
132 final HttpResumableChunkNumber chunkNumber =
133 new HttpResumableChunkNumber(resumableInfo.getChunkNumber());
134 if (uploadedChunks.contains(chunkNumber)) {
135 return false;
136 }
137 write(resumableInfo, stream);
138 uploadedChunks.add(chunkNumber);
139 session.getRunner().incrementRank();
140 return true;
141 }
142
143
144
145
146
147
148
149
150 public final boolean valid(final HttpResumableInfo resumableInfo) {
151 return (resumableInfo.getChunkSize() > 0 &&
152 resumableInfo.getTotalSize() > 0 &&
153 resumableInfo.getChunkNumber() > 0 &&
154 ParametersChecker.isNotEmpty(resumableInfo.getIdentifier()) &&
155 ParametersChecker.isNotEmpty(resumableInfo.getFilename()) &&
156 ParametersChecker.isNotEmpty(resumableInfo.getRelativePath()) &&
157 httpResumableInfo.isCompatible(resumableInfo));
158 }
159
160
161
162
163
164
165
166
167
168 private void write(final HttpResumableInfo info, final InputStream stream)
169 throws IOException {
170 final File file = session.getFile().getTrueFile();
171 RandomAccessFile raf = null;
172 try {
173 raf = new RandomAccessFile(file, "rw");
174
175 raf.seek((info.getChunkNumber() - 1) * (long) info.getChunkSize());
176 final byte[] bytes = new byte[info.getChunkSize()];
177 while (true) {
178 final int r = stream.read(bytes);
179 if (r < 0) {
180 break;
181 }
182 raf.write(bytes, 0, r);
183 size += r;
184 }
185 } finally {
186 if (raf != null) {
187 raf.close();
188 }
189 stream.close();
190 }
191
192 }
193
194
195
196
197
198
199
200
201 public final boolean contains(final HttpResumableInfo resumableInfo) {
202 final HttpResumableChunkNumber chunkNumber =
203 new HttpResumableChunkNumber(resumableInfo.getChunkNumber());
204 return uploadedChunks.contains(chunkNumber);
205 }
206
207
208
209
210
211
212
213
214 public final boolean checkIfUploadFinished(final String sha256) {
215 logger.debug("Write until now: {} for {}", size,
216 httpResumableInfo.getTotalSize());
217
218 if (size != httpResumableInfo.getTotalSize()) {
219 return false;
220 }
221 if (session.getState() == CLOSEDCHANNEL) {
222 return true;
223 }
224 logger.debug("Final block received! {}", session);
225 session.newState(ENDTRANSFERS);
226
227 final R66File r66File = session.getFile();
228 final File file = r66File.getTrueFile();
229 if (file.isFile()) {
230
231 if (ParametersChecker.isNotEmpty(sha256)) {
232 try {
233 final byte[] bin =
234 FilesystemBasedDigest.getHash(file, false, DigestAlgo.SHA256);
235 if (!FilesystemBasedDigest.digestEquals(sha256, bin)) {
236 logger.error("Digests differs: {} {}", sha256,
237 FilesystemBasedDigest.getHex(bin));
238 error(new OpenR66RunnerErrorException("Digest differs"),
239 session.getBusinessObject());
240 }
241 logger.info("Digest OK");
242 } catch (final IOException ignore) {
243 logger.warn(ignore);
244 }
245 } else {
246 logger.info("NO DIGEST given");
247 }
248 final DbTaskRunner runner = session.getRunner();
249 try {
250 final String finalpath = R66Dir.getFinalUniqueFilename(r66File);
251 logger.debug("File to move from {} to {}",
252 r66File.getTrueFile().getAbsolutePath(), finalpath);
253 if (!r66File.renameTo(runner.getRule().setRecvPath(finalpath))) {
254 logger.error("Cannot move file to final position {}",
255 runner.getRule().setRecvPath(finalpath));
256 }
257 runner.setFilename(r66File.getFile());
258 runner.saveStatus();
259 runPostTask();
260 authent.finalizeTransfer(this, session);
261 } catch (final OpenR66RunnerErrorException e) {
262 error(e, session.getBusinessObject());
263 } catch (final CommandAbstractException e) {
264 error(e, session.getBusinessObject());
265 }
266 return true;
267 }
268 return true;
269 }
270
271 @Override
272 public String toString() {
273 return "RS: {" + uploadedChunks.toString() + ", " + session.toString() +
274 ", " + httpResumableInfo + "}";
275 }
276 }