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 static java.lang.invoke.MethodHandles.lookup;
28 import java.nio.channels.Channels;
29 import java.nio.channels.FileChannel;
30 import java.nio.file.Files;
31 import java.nio.file.StandardCopyOption;
32 import java.nio.file.StandardOpenOption;
33 import static java.util.Optional.ofNullable;
34 import javax.annotation.PostConstruct;
35 import javax.annotation.PreDestroy;
36 import javax.inject.Inject;
37 import javax.ws.rs.GET;
38 import javax.ws.rs.HeaderParam;
39 import javax.ws.rs.NotFoundException;
40 import javax.ws.rs.Path;
41 import javax.ws.rs.PathParam;
42 import javax.ws.rs.Produces;
43 import javax.ws.rs.WebApplicationException;
44 import javax.ws.rs.core.Context;
45 import javax.ws.rs.core.MediaType;
46 import javax.ws.rs.core.Response;
47 import javax.ws.rs.core.StreamingOutput;
48 import javax.ws.rs.core.UriInfo;
49 import org.slf4j.Logger;
50 import static org.slf4j.LoggerFactory.getLogger;
51
52
53
54
55
56
57
58 public abstract class AbstractPathsResource {
59
60
61 public static final String PREFERRED_PATH_VALUE = "/paths";
62
63
64 @PostConstruct
65 private void constructed() {
66 }
67
68
69 @PreDestroy
70 private void destroying() {
71
72 if (tempPath != null) {
73 try {
74 final boolean deleted = Files.deleteIfExists(tempPath);
75 logger.trace("temp path deleted: {}", deleted);
76 } catch (final IOException ioe) {
77 logger.error("failed to delete temp path: " + tempPath, ioe);
78 }
79 }
80 }
81
82
83 @Produces(MediaType.WILDCARD)
84 @GET
85 @Path("/{path: .+}")
86 public Response readSingle(@PathParam("path") final String path)
87 throws IOException, FileBackException {
88
89 logger.trace("path: {}", path);
90
91 tempPath = Files.createTempFile("prefix", "suffix");
92
93 final FileContext fileContext = new DefaultFileContext();
94
95 fileContext.fileOperationSupplier(() -> FileOperation.READ);
96
97 fileContext.pathNameSupplier(() -> path);
98
99 final Object[] sourceObject_ = new Object[1];
100 fileContext.sourceObjectConsumer(sourceObject -> {
101 logger.trace("consuming source object: {}", sourceObject);
102 sourceObject_[0] = sourceObject;
103 });
104
105 final Long[] sourceCopied_ = new Long[1];
106 fileContext.sourceCopiedConsumer(sourceCopied -> {
107 logger.trace("consuming source copied: {}", sourceCopied);
108 sourceCopied_[0] = sourceCopied;
109 });
110
111 final Long[] targetCopied_ = new Long[1];
112 fileContext.targetCopiedConsumer(targetCopied -> {
113 logger.trace("consuming target copied: {}", targetCopied);
114 targetCopied_[0] = targetCopied;
115 });
116
117 fileContext.sourceChannelConsumer(sourceChannel -> {
118 logger.trace("consuming source channel : {}", sourceChannel);
119 try {
120 final long sourceCopied = Files.copy(
121 Channels.newInputStream(sourceChannel), tempPath,
122 StandardCopyOption.REPLACE_EXISTING);
123 logger.trace("source copied: {}", sourceCopied);
124 sourceCopied_[0] = sourceCopied;
125 } catch (final IOException ioe) {
126 final String message
127 = "failed from source channel to temp path";
128 logger.error(message, ioe);
129 throw new WebApplicationException(message, ioe);
130 }
131 });
132
133 final FileChannel[] targetChannel_ = new FileChannel[1];
134 fileContext.targetChannelSupplier(true ? null : () -> {
135 try {
136 targetChannel_[0] = FileChannel.open(
137 tempPath, StandardOpenOption.CREATE_NEW,
138 StandardOpenOption.WRITE);
139 logger.trace("suppling target channel: {}", targetChannel_[0]);
140 return targetChannel_[0];
141 } catch (final IOException ioe) {
142 final String message
143 = "failed to open temp path for writing";
144 logger.error(message, ioe);
145 throw new WebApplicationException(message, ioe);
146 }
147 });
148
149 fileBack.operate(fileContext);
150
151 ofNullable(targetChannel_[0]).ifPresent(v -> {
152 try {
153 v.close();
154 } catch (final IOException ioe) {
155 logger.error("failed to close target channel", ioe);
156 }
157 });
158
159 if (sourceCopied_[0] == null && targetCopied_[0] == null) {
160 throw new NotFoundException("no file for path: " + path);
161 }
162
163 return Response
164 .ok((StreamingOutput) output -> Files.copy(tempPath, output))
165 .header(FileFrontConstants.HEADER_SOURCE_COPIED, sourceCopied_[0])
166 .header(FileFrontConstants.HEADER_TARGET_COPIED, targetCopied_[0])
167 .build();
168 }
169
170
171 private transient final Logger logger = getLogger(lookup().lookupClass());
172
173
174 private transient java.nio.file.Path tempPath;
175
176
177
178
179
180 @Inject
181 @Backing
182 private FileBack fileBack;
183
184
185 @Context
186 private UriInfo uriInfo;
187
188
189 @HeaderParam("Content-Type")
190 private MediaType contentType;
191
192
193 }
194