View Javadoc
1   /*
2    * Copyright 2014 Jin Kwon.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
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   * @author Jin Kwon <jinahya_at_gmail.com>
56   */
57  //@Path("/paths")
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 : () -> { // _not_usd_!!!
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      * A file back injected.
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