1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package com.github.jinahya.simple.file.front;
19
20
21 import com.github.jinahya.simple.file.back.DefaultFileContext;
22 import com.github.jinahya.simple.file.back.FileBack;
23 import com.github.jinahya.simple.file.back.FileBack.FileOperation;
24 import com.github.jinahya.simple.file.back.FileBackException;
25 import com.github.jinahya.simple.file.back.FileContext;
26 import java.io.IOException;
27 import java.io.InputStream;
28 import static java.lang.invoke.MethodHandles.lookup;
29 import java.net.URI;
30 import java.nio.ByteBuffer;
31 import java.nio.channels.Channels;
32 import java.nio.channels.FileChannel;
33 import java.nio.charset.StandardCharsets;
34 import java.nio.file.Files;
35 import java.nio.file.StandardCopyOption;
36 import java.nio.file.StandardOpenOption;
37 import java.util.ArrayList;
38 import java.util.List;
39 import static java.util.Optional.ofNullable;
40 import java.util.concurrent.ExecutionException;
41 import java.util.concurrent.Future;
42 import javax.annotation.PostConstruct;
43 import javax.annotation.PreDestroy;
44 import javax.inject.Inject;
45 import javax.ws.rs.Consumes;
46 import javax.ws.rs.DELETE;
47 import javax.ws.rs.DefaultValue;
48 import javax.ws.rs.GET;
49 import javax.ws.rs.HeaderParam;
50 import javax.ws.rs.NotFoundException;
51 import javax.ws.rs.POST;
52 import javax.ws.rs.PUT;
53 import javax.ws.rs.Path;
54 import javax.ws.rs.PathParam;
55 import javax.ws.rs.ProcessingException;
56 import javax.ws.rs.Produces;
57 import javax.ws.rs.QueryParam;
58 import javax.ws.rs.WebApplicationException;
59 import javax.ws.rs.client.Client;
60 import javax.ws.rs.client.ClientBuilder;
61 import javax.ws.rs.client.Entity;
62 import javax.ws.rs.client.WebTarget;
63 import javax.ws.rs.core.Context;
64 import javax.ws.rs.core.MediaType;
65 import javax.ws.rs.core.Response;
66 import javax.ws.rs.core.StreamingOutput;
67 import javax.ws.rs.core.UriInfo;
68 import org.glassfish.jersey.client.ClientProperties;
69 import org.slf4j.Logger;
70 import static org.slf4j.LoggerFactory.getLogger;
71
72
73
74
75
76
77 public abstract class AbstractLocatorsResource {
78
79
80 public static final String PREFERRED_PATH_VALUE = "locators";
81
82
83 protected static ByteBuffer key(final String locator) {
84
85 final Logger logger = getLogger(lookup().lookupClass());
86
87 logger.trace("key({})", locator);
88
89 return ByteBuffer.wrap(locator.getBytes(StandardCharsets.UTF_8));
90 }
91
92
93 @PostConstruct
94 private void constructed() {
95
96 logger.trace("fileBack: {}", fileBack);
97 logger.trace("fileFronts: {}", fileFronts);
98 logger.trace("Header.Accept: {}", accept);
99
100 try {
101 tempPath = Files.createTempFile("prefix", "suffix");
102 logger.trace("temp path created: {}", tempPath);
103 } catch (final IOException ioe) {
104 logger.error("failed to create temp path", ioe);
105 }
106 }
107
108
109 @PreDestroy
110 private void destroying() {
111
112 if (tempPath != null) {
113 try {
114 Files.deleteIfExists(tempPath);
115 } catch (final IOException ioe) {
116 logger.error("failed to delete temp path: " + tempPath, ioe);
117 }
118 }
119 }
120
121
122 protected Response copySingle(final FileContext fileContext,
123 final String sourceLocator,
124 final String targetLocator,
125 final boolean distributeFlag)
126 throws IOException, FileBackException {
127
128 logger.trace("copySingle({}, {}, {}, {})", fileContext, sourceLocator,
129 targetLocator, distributeFlag);
130
131 fileContext.fileOperationSupplier(() -> FileOperation.COPY);
132
133 fileContext.sourceKeySupplier(
134 ofNullable(fileContext.sourceKeySupplier()).orElse(
135 () -> key(sourceLocator)));
136 fileContext.targetKeySupplier(
137 ofNullable(fileContext.targetKeySupplier()).orElse(
138 () -> key(targetLocator)));
139
140 final Object[] sourceObject_ = new Object[1];
141 fileContext.sourceObjectConsumer(
142 ofNullable(fileContext.sourceObjectConsumer()).orElse(
143 sourceObject -> {
144 logger.trace("consuming source object: {}", sourceObject);
145 sourceObject_[0] = sourceObject;
146 }));
147
148 final Long[] sourceCopied_ = new Long[1];
149 fileContext.sourceCopiedConsumer(
150 ofNullable(fileContext.sourceCopiedConsumer()).orElse(
151 sourceCopied -> {
152 logger.trace("consuming source copied: {}", sourceCopied);
153 sourceCopied_[0] = sourceCopied;
154 }));
155
156 final Object[] targetObject_ = new Object[1];
157 fileContext.targetObjectConsumer(
158 ofNullable(fileContext.targetObjectConsumer()).orElse(
159 targetObject -> {
160 logger.trace("consuming target object: {}", targetObject);
161 targetObject_[0] = targetObject;
162 }));
163
164 final Long[] targetCopied_ = new Long[1];
165 fileContext.targetCopiedConsumer(
166 ofNullable(fileContext.targetCopiedConsumer()).orElse(
167 targetCopied -> {
168 logger.trace("consuming target copied: {}", targetCopied);
169 targetCopied_[0] = targetCopied;
170 }));
171
172 final String[] pathName_ = new String[1];
173 fileContext.pathNameConsumer(
174 ofNullable(fileContext.pathNameConsumer()).orElse(
175 pathName -> {
176 logger.trace("consuming path name: {}", pathName);
177 pathName_[0] = pathName;
178 }));
179
180 try {
181 fileBack.operate(fileContext);
182 } catch (IOException | FileBackException e) {
183 final String message = "failed to operate file back";
184 logger.error(message, e);
185 throw new WebApplicationException(message, e);
186 }
187
188 if (distributeFlag) {
189 final URI baseUri = uriInfo.getBaseUri();
190 logger.trace("uriInfo.baseUri: {}", baseUri);
191 final String path = uriInfo.getPath();
192 logger.trace("uriInfo.path: {}", path);
193 final List<Future<Response>> futures = new ArrayList<>();
194 for (final URI fileFront : fileFronts) {
195 logger.trace("fileFront: {}", fileFront);
196 if (baseUri.equals(fileFront)) {
197 logger.trace("skipping self: " + fileFront);
198 continue;
199 }
200 if (!fileFront.isAbsolute()) {
201 logger.warn("not an absolute uri: {}", fileFront);
202 continue;
203 }
204 final Client client = ClientBuilder.newClient()
205 .property(ClientProperties.CONNECT_TIMEOUT, 1000)
206 .property(ClientProperties.READ_TIMEOUT, 1000);
207 final WebTarget target = client.target(fileFront).path(path)
208 .queryParam("locator", targetLocator)
209 .queryParam("distribute", Boolean.FALSE.toString());
210 logger.trace("target.uri: {}", target.getUri().toString());
211 final Future<Response> future
212 = target.request().async().method("POST");
213 logger.trace("future: {}", future);
214 futures.add(future);
215 }
216 logger.trace("futures: {}", futures);
217 futures.forEach(future -> {
218 try {
219 final Response response = future.get();
220 logger.trace("response: {}", response);
221 logger.trace("response.statusInfo: {}",
222 response.getStatusInfo());
223 } catch (InterruptedException | ExecutionException e) {
224 logger.error("fail to get response", e);
225 }
226 });
227 }
228
229 return Response.noContent()
230 .header(FileFrontConstants.HEADER_PATH_NAME, pathName_[0])
231 .header(FileFrontConstants.HEADER_SOURCE_COPIED, sourceCopied_[0])
232 .header(FileFrontConstants.HEADER_TARGET_COPIED, targetCopied_[0])
233 .build();
234 }
235
236
237 @POST
238 @Path("/{locator: .+}/copy")
239 public Response copySingle(
240 @PathParam("locator") final String sourceLocator,
241 @QueryParam("locator") final String targetLocator,
242 @QueryParam("distribute") @DefaultValue("true")
243 final boolean distribute)
244 throws IOException, FileBackException {
245
246 logger.trace("copySingle({}, {}, {})", sourceLocator, targetLocator,
247 distribute);
248
249 final FileContext fileContext = new DefaultFileContext();
250
251 fileContext.fileOperationSupplier(() -> FileOperation.COPY);
252
253 fileContext.sourceKeySupplier(() -> ByteBuffer.wrap(
254 sourceLocator.getBytes(StandardCharsets.UTF_8)));
255 fileContext.targetKeySupplier(() -> ByteBuffer.wrap(
256 targetLocator.getBytes(StandardCharsets.UTF_8)));
257
258 final Object[] sourceObject_ = new Object[1];
259 fileContext.sourceObjectConsumer(sourceObject -> {
260 logger.trace("source object: {}", sourceObject);
261 sourceObject_[0] = sourceObject;
262 });
263
264 final Long[] sourceCopied_ = new Long[1];
265 fileContext.sourceCopiedConsumer(sourceCopied -> {
266 logger.trace("source copied: {}", sourceCopied);
267 sourceCopied_[0] = sourceCopied;
268 });
269
270 final Object[] targetObject_ = new Object[1];
271 fileContext.targetObjectConsumer(targetObject -> {
272 logger.trace("target object: {}", targetObject);
273 targetObject_[0] = targetObject;
274 });
275
276 final Long[] targetCopied_ = new Long[0];
277 fileContext.targetCopiedConsumer(targetCopied -> {
278 logger.trace("target copied: {}", targetCopied);
279 targetCopied_[0] = targetCopied;
280 });
281
282 final String[] pathName_ = new String[1];
283 fileContext.pathNameConsumer(pathName -> {
284 logger.trace("path name: {}", pathName);
285 pathName_[0] = pathName;
286 });
287
288 fileBack.operate(fileContext);
289
290 if (distribute) {
291 final URI baseUri = uriInfo.getBaseUri();
292 logger.trace("uriInfo.baseUri: {}", baseUri);
293 final String path = uriInfo.getPath();
294 logger.trace("uriInfo.path: {}", path);
295 final List<Future<Response>> futures = new ArrayList<>();
296 for (final URI fileFront : fileFronts) {
297 logger.trace("fileFront: {}", fileFront);
298 if (baseUri.equals(fileFront)) {
299 logger.trace("skipping self: " + fileFront);
300 continue;
301 }
302 if (!fileFront.isAbsolute()) {
303 logger.warn("not an absolute uri: {}", fileFront);
304 continue;
305 }
306 final Client client = ClientBuilder.newClient()
307 .property(ClientProperties.CONNECT_TIMEOUT, 1000)
308 .property(ClientProperties.READ_TIMEOUT, 1000);
309 final WebTarget target = client.target(fileFront).path(path)
310 .queryParam("source_locator", sourceLocator)
311 .queryParam("target_locator", targetLocator)
312 .queryParam("distribute", Boolean.FALSE.toString());
313 logger.trace("target.uri: {}", target.getUri().toString());
314 try {
315 final Future<Response> future
316 = target.request().async().method("POST");
317 logger.trace("future: {}", future);
318 futures.add(future);
319 } catch (final ProcessingException pe) {
320 logger.error("failed to distribute to " + fileFront, pe);
321 }
322 }
323 logger.trace("futures: {}", futures);
324 futures.forEach(future -> {
325 try {
326 final Response response = future.get();
327 logger.trace("response: {}", response);
328 logger.trace("response.statusInfo: {}",
329 response.getStatusInfo());
330 } catch (InterruptedException | ExecutionException e) {
331 logger.error("fail to get response", e);
332 }
333 });
334 }
335
336 return Response.noContent()
337 .header(FileFrontConstants.HEADER_PATH_NAME, pathName_[0])
338 .header(FileFrontConstants.HEADER_SOURCE_COPIED, sourceCopied_[0])
339 .header(FileFrontConstants.HEADER_TARGET_COPIED, targetCopied_[0])
340 .build();
341 }
342
343
344
345
346
347
348
349
350
351
352
353
354
355 @DELETE
356 @Path("{locator: .+}")
357 public Response deleteSingle(
358 @PathParam("locator") final String locator,
359 @QueryParam("distribute") @DefaultValue("true")
360 final boolean distribute)
361 throws IOException, FileBackException {
362
363 logger.trace("deleteSingle({}, {})", locator, distribute);
364
365 final FileContext fileContext = new DefaultFileContext();
366
367 fileContext.fileOperationSupplier(() -> FileOperation.DELETE);
368
369 fileContext.targetKeySupplier(
370 () -> ByteBuffer.wrap(locator.getBytes(StandardCharsets.UTF_8)));
371
372 final Object[] sourceObject_ = new Object[1];
373 fileContext.sourceObjectConsumer(sourceObject -> {
374 logger.trace("consuming source object: {}", sourceObject);
375 sourceObject_[0] = sourceObject;
376 });
377
378 final Long[] sourceCopied_ = new Long[1];
379 fileContext.sourceCopiedConsumer(sourceCopied -> {
380 logger.trace("consuming source copied: {}", sourceCopied);
381 sourceCopied_[0] = sourceCopied;
382 });
383
384 final Object[] targetObject_ = new Object[1];
385 fileContext.targetObjectConsumer(targetObject -> {
386 logger.trace("consuming target object: {}", targetObject);
387 targetObject_[0] = targetObject;
388 });
389
390 final Long[] targetCopied_ = new Long[1];
391 fileContext.targetCopiedConsumer(targetCopied -> {
392 logger.trace("consuming target copied: {}", targetCopied);
393 targetCopied_[0] = targetCopied;
394 });
395
396 final String[] pathName_ = new String[1];
397 fileContext.pathNameConsumer(pathName -> {
398 logger.trace("consuming path name: {}", pathName);
399 pathName_[0] = pathName;
400 });
401
402 fileBack.operate(fileContext);
403
404 if (distribute) {
405 final URI baseUri = uriInfo.getBaseUri();
406 logger.trace("uriInfo.baseUri: {}", baseUri);
407 final String path = uriInfo.getPath();
408 logger.trace("uriInfo.path: {}", path);
409 final List<Future<Response>> futures = new ArrayList<>();
410 for (final URI fileFront : fileFronts) {
411 logger.trace("fileFront: {}", fileFront);
412 if (baseUri.equals(fileFront)) {
413 logger.trace("skipping self: " + fileFront);
414 continue;
415 }
416 if (!fileFront.isAbsolute()) {
417 logger.warn("not an absolute uri: {}", fileFront);
418 continue;
419 }
420 final Client client = ClientBuilder.newClient()
421 .property(ClientProperties.CONNECT_TIMEOUT, 1000)
422 .property(ClientProperties.READ_TIMEOUT, 1000);
423 final WebTarget target = client.target(fileFront).path(path)
424 .queryParam("distribute", Boolean.FALSE.toString());
425 logger.trace("target: {}", target.getUri().toString());
426 try {
427 final Future<Response> future
428 = target.request().async().delete();
429 logger.trace("future: {}", future);
430 futures.add(future);
431 } catch (final ProcessingException pe) {
432 logger.error("failed to distribute to " + fileFront, pe);
433 }
434 }
435 logger.trace("futures: {}", futures);
436 futures.forEach(future -> {
437 try {
438 final Response response = future.get();
439 logger.trace("response: {}", response);
440 logger.trace("response.statusInfo: {}",
441 response.getStatusInfo());
442 } catch (InterruptedException | ExecutionException e) {
443 logger.error("fail to get from future", e);
444 }
445 });
446 }
447
448 return Response.noContent()
449 .header(FileFrontConstants.HEADER_PATH_NAME, pathName_[0])
450 .header(FileFrontConstants.HEADER_SOURCE_COPIED, sourceCopied_[0])
451 .header(FileFrontConstants.HEADER_TARGET_COPIED, targetCopied_[0])
452 .build();
453 }
454
455
456 protected Response readSingle(final FileContext fileContext,
457 final String sourceLocator)
458 throws IOException, FileBackException {
459
460 logger.trace("readSingle({}, {})", fileContext, sourceLocator);
461
462 fileContext.fileOperationSupplier(() -> FileOperation.READ);
463
464 fileContext.sourceKeySupplier(() -> key(sourceLocator));
465
466 final Object[] sourceObject_ = new Object[1];
467 fileContext.sourceObjectConsumer(sourceObject -> {
468 logger.trace("consuming source object: {}", sourceObject);
469 sourceObject_[0] = sourceObject;
470 });
471
472 final Long[] sourceCopied_ = new Long[1];
473 fileContext.sourceCopiedConsumer(sourceCopied -> {
474 logger.trace("consuming source copied: {}", sourceCopied);
475 sourceCopied_[0] = sourceCopied;
476 });
477
478 final Object[] targetObject_ = new Object[1];
479 fileContext.targetObjectConsumer(targetObject -> {
480 logger.trace("consuming target object: {}", targetObject);
481 targetObject_[0] = targetObject;
482 });
483
484 final Long[] targetCopied_ = new Long[1];
485 fileContext.targetCopiedConsumer(targetCopied -> {
486 logger.trace("consuming target copied: {}", targetCopied);
487 targetCopied_[0] = targetCopied;
488 });
489
490 final String[] pathName_ = new String[1];
491 fileContext.pathNameConsumer(pathName -> {
492 logger.trace("consuming path name: {}", pathName);
493 pathName_[0] = pathName;
494 });
495
496 fileContext.sourceChannelConsumer(sourceChannel -> {
497 logger.trace("consuming source channel : {}", sourceChannel);
498 try {
499 final long sourceCopied = Files.copy(
500 Channels.newInputStream(sourceChannel), tempPath,
501 StandardCopyOption.REPLACE_EXISTING);
502 sourceCopied_[0] = sourceCopied;
503 } catch (final IOException ioe) {
504 final String message
505 = "failed from source channel to temp path";
506 logger.error(message, ioe);
507 throw new WebApplicationException(message, ioe);
508 }
509 });
510
511 final FileChannel[] targetChannel_ = new FileChannel[1];
512 fileContext.targetChannelSupplier(true ? null : () -> {
513 try {
514 targetChannel_[0] = FileChannel.open(
515 tempPath, StandardOpenOption.CREATE_NEW,
516 StandardOpenOption.WRITE);
517 logger.trace("target channel: {}", targetChannel_[0]);
518 return targetChannel_[0];
519 } catch (final IOException ioe) {
520 final String message
521 = "failed to open temp path for writing";
522 logger.error(message, ioe);
523 throw new WebApplicationException(message, ioe);
524 }
525 });
526
527 fileBack.operate(fileContext);
528
529 if (sourceCopied_[0] == null) {
530 throw new NotFoundException(
531 "no file for locator: " + sourceLocator);
532 }
533
534 return Response
535 .ok((StreamingOutput) output -> Files.copy(tempPath, output))
536 .header(FileFrontConstants.HEADER_PATH_NAME, pathName_[0])
537 .header(FileFrontConstants.HEADER_SOURCE_COPIED, sourceCopied_[0])
538 .header(FileFrontConstants.HEADER_TARGET_COPIED, targetCopied_[0])
539 .build();
540 }
541
542
543
544
545
546
547
548
549
550
551
552 @Produces(MediaType.WILDCARD)
553 @GET
554 @Path("{locator: .+}")
555 public Response readSingle(@PathParam("locator") final String locator)
556 throws IOException, FileBackException {
557
558 logger.trace("readSingle({})", locator);
559
560 final FileContext fileContext = new DefaultFileContext();
561
562 return readSingle(fileContext, locator);
563 }
564
565
566 protected Response updateSingle(final FileContext fileContext,
567 final String targetLocator,
568 final InputStream sourceStream,
569 final boolean distributeFlag) {
570
571 logger.trace("updateSingle({}, {}, {}, {})", fileContext, targetLocator,
572 sourceStream, distributeFlag);
573
574 try {
575 Files.copy(sourceStream, tempPath,
576 StandardCopyOption.REPLACE_EXISTING);
577 logger.trace("source stream copied to temp path");
578 } catch (final IOException ioe) {
579 logger.error("failed to copy source stream to temp path", ioe);
580 throw new WebApplicationException(ioe);
581 }
582
583 fileContext.fileOperationSupplier(() -> FileOperation.WRITE);
584
585 fileContext.targetKeySupplier(() -> key(targetLocator));
586
587 final Object[] sourceObject_ = new Object[1];
588 fileContext.sourceObjectConsumer(
589 ofNullable(fileContext.sourceObjectConsumer()).orElse(
590 sourceObject -> {
591 logger.trace("consuming source object: {}", sourceObject);
592 sourceObject_[0] = sourceObject;
593 }));
594
595 final Long[] sourceCopied_ = new Long[1];
596 fileContext.sourceCopiedConsumer(
597 ofNullable(fileContext.sourceCopiedConsumer()).orElse(
598 sourceCopied -> {
599 logger.trace("consuming source copied: {}", sourceCopied);
600 sourceCopied_[0] = sourceCopied;
601 }));
602
603 final Object[] targetObject_ = new Object[1];
604 fileContext.targetObjectConsumer(
605 ofNullable(fileContext.targetObjectConsumer()).orElse(
606 targetObject -> {
607 logger.trace("consuming target object: {}", targetObject);
608 targetObject_[0] = targetObject;
609 }));
610
611 final Long[] targetCopied_ = new Long[1];
612 fileContext.targetCopiedConsumer(
613 ofNullable(fileContext.targetCopiedConsumer()).orElse(
614 targetCopied -> {
615 logger.trace("consuming target copied: {}", targetCopied);
616 targetCopied_[0] = targetCopied;
617 }));
618
619 final String[] pathName_ = new String[1];
620 fileContext.pathNameConsumer(
621 ofNullable(fileContext.pathNameConsumer()).orElse(
622 pathName -> {
623 logger.trace("consuming path name: {}", pathName);
624 pathName_[0] = pathName;
625 }));
626
627 fileContext.targetChannelConsumer(targetChannel -> {
628 logger.trace("consuming target channel : {}", targetChannel);
629 try {
630 final long targetCopied = Files.copy(
631 tempPath, Channels.newOutputStream(targetChannel));
632 logger.trace("target copied: {}", targetCopied);
633 targetCopied_[0] = targetCopied;
634 } catch (final IOException ioe) {
635 final String message
636 = "failed to copy from temp path to target channel";
637 logger.error(message, ioe);
638 throw new WebApplicationException(message, ioe);
639 }
640 });
641
642 final FileChannel[] sourceChannel_ = new FileChannel[1];
643 fileContext.sourceChannelSupplier(true ? null : () -> {
644 try {
645 sourceChannel_[0] = FileChannel.open(
646 tempPath, StandardOpenOption.READ);
647 logger.trace("suppling source channel: {}", sourceChannel_[0]);
648 return sourceChannel_[0];
649 } catch (final IOException ioe) {
650 final String message
651 = "failed to open temp path for reading";
652 logger.error(message, ioe);
653 throw new WebApplicationException(message, ioe);
654 }
655 });
656
657 try {
658 fileBack.operate(fileContext);
659 } catch (IOException | FileBackException e) {
660 final String message = "failed to operate file back";
661 logger.error(message, e);
662 throw new WebApplicationException(message, e);
663 }
664
665 ofNullable(sourceChannel_[0]).ifPresent(fileChannel -> {
666 try {
667 fileChannel.close();
668 logger.trace("file channel closed: {}", fileChannel);
669 } catch (final IOException ioe) {
670 final String message
671 = "failed to close file channel: " + fileChannel;
672 logger.error(message, ioe);
673 throw new WebApplicationException(message, ioe);
674 }
675 });
676
677 if (distributeFlag) {
678 logger.trace("distributing...");
679 final URI baseUri = uriInfo.getBaseUri();
680 logger.trace("uriInfo.baseUri: {}", baseUri);
681 final String path = uriInfo.getPath();
682 logger.trace("uriInfo.path: {}", path);
683 final List<Future<Response>> futures = new ArrayList<>();
684 logger.trace("fileFronts: {}", fileFronts);
685 for (final URI fileFront : fileFronts) {
686 logger.trace("fileFront: {}", fileFront);
687 if (!fileFront.isAbsolute()) {
688 logger.warn("not an absolute uri: {}", fileFront);
689 continue;
690 }
691 if (baseUri.equals(fileFront)) {
692 logger.trace("skipping self: " + fileFront);
693 continue;
694 }
695 final Client client = ClientBuilder.newClient()
696 .property(ClientProperties.CONNECT_TIMEOUT, 2000)
697 .property(ClientProperties.READ_TIMEOUT, 2000);
698 final WebTarget target = client.target(fileFront).path(path)
699 .queryParam("distribute", Boolean.FALSE.toString());
700 logger.trace("target: {}", target.getUri().toString());
701
702
703
704
705
706
707
708
709 final Future<Response> future = target.request().async().put(
710 Entity.entity(tempPath.toFile(), contentType));
711 logger.trace("future: {}", future);
712 futures.add(future);
713 }
714 logger.trace("futures: {}", futures);
715 futures.forEach(future -> {
716 try {
717 final Response response = future.get();
718 logger.trace("response: {}", response);
719 logger.trace("response.status: {}", response.getStatus());
720 } catch (InterruptedException | ExecutionException e) {
721 logger.error("fail to get response", e);
722 }
723 });
724 }
725
726 return Response.noContent()
727 .header(FileFrontConstants.HEADER_PATH_NAME, pathName_[0])
728 .header(FileFrontConstants.HEADER_SOURCE_COPIED, sourceCopied_[0])
729 .header(FileFrontConstants.HEADER_TARGET_COPIED, targetCopied_[0])
730 .build();
731 }
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746 @Consumes(MediaType.WILDCARD)
747 @PUT
748 @Path("{locator: .+}")
749 public Response updateSingle(
750 @PathParam("locator") final String locator,
751 @QueryParam("distribute") @DefaultValue("true")
752 final boolean distribute,
753 final InputStream entity)
754 throws IOException, FileBackException {
755
756 logger.trace("updateSingle({}, {}, {})", locator, distribute, entity);
757
758 if (true) {
759 return updateSingle(new DefaultFileContext(), locator, entity,
760 distribute);
761 }
762
763 try {
764 Files.copy(entity, tempPath, StandardCopyOption.REPLACE_EXISTING);
765 logger.trace("entity copied to temp path");
766 } catch (final IOException ioe) {
767 logger.error("failed to copy entity to temp path", ioe);
768 throw new WebApplicationException(ioe);
769 }
770
771 final FileContext fileContext = new DefaultFileContext();
772
773 fileContext.fileOperationSupplier(() -> FileOperation.WRITE);
774
775 fileContext.targetKeySupplier(
776 () -> ByteBuffer.wrap(locator.getBytes(StandardCharsets.UTF_8)));
777
778 final Object[] sourceObject_ = new Object[1];
779 fileContext.sourceObjectConsumer(sourceObject -> {
780 logger.trace("source object: {}", sourceObject);
781 sourceObject_[0] = sourceObject;
782 });
783
784 final Long[] sourceCopied_ = new Long[1];
785 fileContext.sourceCopiedConsumer(sourceCopied -> {
786 logger.trace("source copied: {}", sourceCopied);
787 sourceCopied_[0] = sourceCopied;
788 });
789
790 final Object[] targetObject_ = new Object[1];
791 fileContext.targetObjectConsumer(targetObject -> {
792 logger.trace("target object: {}", targetObject);
793 targetObject_[0] = targetObject;
794 });
795
796 final Long[] targetCopied_ = new Long[1];
797 fileContext.targetCopiedConsumer(targetCopied -> {
798 logger.trace("target copied: {}", targetCopied);
799 targetCopied_[0] = targetCopied;
800 });
801
802 final String[] pathName_ = new String[1];
803 fileContext.pathNameConsumer(pathName -> {
804 logger.trace("path name: {}", pathName);
805 pathName_[0] = pathName;
806 });
807
808 fileContext.targetChannelConsumer(targetChannel -> {
809 logger.trace("target channel : {}", targetChannel);
810 try {
811 final long targetCopied = Files.copy(
812 tempPath, Channels.newOutputStream(targetChannel));
813 logger.trace("target copied: {}", targetCopied);
814 targetCopied_[0] = targetCopied;
815 } catch (final IOException ioe) {
816 final String message
817 = "failed to copy from temp path to target channel";
818 logger.error(message, ioe);
819 throw new WebApplicationException(message, ioe);
820 }
821 });
822
823 final FileChannel[] sourceChannel_ = new FileChannel[1];
824 fileContext.sourceChannelSupplier(true ? null : () -> {
825 try {
826 sourceChannel_[0] = FileChannel.open(
827 tempPath, StandardOpenOption.READ);
828 logger.trace("source channel: {}", sourceChannel_[0]);
829 return sourceChannel_[0];
830 } catch (final IOException ioe) {
831 final String message
832 = "failed to open temp path for reading";
833 logger.error(message, ioe);
834 throw new WebApplicationException(message, ioe);
835 }
836 });
837
838 fileBack.operate(fileContext);
839
840
841
842 if (distribute) {
843 logger.trace("distributing...");
844 final URI baseUri = uriInfo.getBaseUri();
845 logger.trace("uriInfo.baseUri: {}", baseUri);
846 final String path = uriInfo.getPath();
847 logger.trace("uriInfo.path: {}", path);
848 final List<Future<Response>> futures = new ArrayList<>();
849 logger.trace("fileFronts: {}", fileFronts);
850 for (final URI fileFront : fileFronts) {
851 logger.trace("fileFront: {}", fileFront);
852 if (!fileFront.isAbsolute()) {
853 logger.warn("not an absolute uri: {}", fileFront);
854 continue;
855 }
856 if (baseUri.equals(fileFront)) {
857 logger.trace("skipping self: " + fileFront);
858 continue;
859 }
860 final Client client = ClientBuilder.newClient()
861 .property(ClientProperties.CONNECT_TIMEOUT, 2000)
862 .property(ClientProperties.READ_TIMEOUT, 2000);
863 final WebTarget target = client.target(fileFront).path(path)
864 .queryParam("distribute", Boolean.FALSE.toString());
865 logger.trace("target: {}", target.getUri().toString());
866
867
868
869
870
871
872
873
874 final Future<Response> future = target.request().async().put(
875 Entity.entity(tempPath.toFile(), contentType));
876 logger.trace("future: {]", future);
877 futures.add(future);
878 }
879 logger.trace("futures: {}", futures);
880 futures.forEach(future -> {
881 try {
882 final Response response = future.get();
883 logger.trace("response: {}", response);
884 logger.trace("response.status: {}", response.getStatus());
885 } catch (InterruptedException | ExecutionException e) {
886 logger.error("fail to get response", e);
887 } catch (final Exception e) {
888 logger.error("unhandled", e);
889 }
890 });
891 }
892
893 return Response.noContent()
894 .header(FileFrontConstants.HEADER_PATH_NAME, pathName_[0])
895 .header(FileFrontConstants.HEADER_SOURCE_COPIED, sourceCopied_[0])
896 .header(FileFrontConstants.HEADER_TARGET_COPIED, targetCopied_[0])
897 .build();
898 }
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059 protected FileBack getFileBack() {
1060
1061 return fileBack;
1062 }
1063
1064
1065
1066
1067
1068
1069
1070 protected List<URI> getFileFronts() {
1071
1072 return fileFronts;
1073 }
1074
1075
1076 private transient final Logger logger = getLogger(lookup().lookupClass());
1077
1078
1079 private transient java.nio.file.Path tempPath;
1080
1081
1082
1083
1084
1085 @Inject
1086 @Backing
1087 private FileBack fileBack;
1088
1089
1090
1091
1092
1093 @Inject
1094 @Siblings
1095 private List<URI> fileFronts;
1096
1097
1098 @Context
1099 private UriInfo uriInfo;
1100
1101
1102 @HeaderParam("Content-Type")
1103 private MediaType contentType = MediaType.WILDCARD_TYPE;
1104
1105
1106 @HeaderParam("Accept")
1107 private String accept;
1108
1109
1110 }
1111