1
2
3
4
5
6
7
8
9
10 package org.eclipse.jgit.lfs.server.fs;
11
12 import java.io.FileNotFoundException;
13 import java.io.IOException;
14 import java.nio.ByteBuffer;
15 import java.nio.channels.Channels;
16 import java.nio.channels.ReadableByteChannel;
17 import java.nio.channels.WritableByteChannel;
18 import java.nio.file.Path;
19 import java.util.logging.Level;
20 import java.util.logging.Logger;
21
22 import javax.servlet.AsyncContext;
23 import javax.servlet.ReadListener;
24 import javax.servlet.ServletInputStream;
25 import javax.servlet.http.HttpServletRequest;
26 import javax.servlet.http.HttpServletResponse;
27
28 import org.apache.http.HttpStatus;
29 import org.eclipse.jgit.lfs.errors.CorruptLongObjectException;
30 import org.eclipse.jgit.lfs.internal.AtomicObjectOutputStream;
31 import org.eclipse.jgit.lfs.lib.AnyLongObjectId;
32 import org.eclipse.jgit.lfs.lib.Constants;
33
34
35
36
37
38
39 public class ObjectUploadListener implements ReadListener {
40
41 private static final Logger LOG = Logger
42 .getLogger(ObjectUploadListener.class.getName());
43
44 private final AsyncContext context;
45
46 private final HttpServletResponse response;
47
48 private final ServletInputStream in;
49
50 private final ReadableByteChannel inChannel;
51
52 private final AtomicObjectOutputStream out;
53
54 private WritableByteChannel channel;
55
56 private final ByteBuffer buffer = ByteBuffer.allocateDirect(8192);
57
58 private final Path path;
59
60 private long uploaded;
61
62 private Callback callback;
63
64
65
66
67
68
69 public interface Callback {
70
71
72
73
74
75
76
77
78 void uploadCompleted(String path, long size);
79 }
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97 public ObjectUploadListener(FileLfsRepository repository,
98 AsyncContext context, HttpServletRequest request,
99 HttpServletResponse response, AnyLongObjectId id)
100 throws FileNotFoundException, IOException {
101 this.context = context;
102 this.response = response;
103 this.in = request.getInputStream();
104 this.inChannel = Channels.newChannel(in);
105 this.out = repository.getOutputStream(id);
106 this.channel = Channels.newChannel(out);
107 this.path = repository.getPath(id);
108 this.uploaded = 0L;
109 response.setContentType(Constants.CONTENT_TYPE_GIT_LFS_JSON);
110 }
111
112
113
114
115
116
117
118
119
120 public ObjectUploadListener setCallback(Callback callback) {
121 this.callback = callback;
122 return this;
123 }
124
125
126
127
128
129
130 @Override
131 public void onDataAvailable() throws IOException {
132 while (in.isReady()) {
133 if (inChannel.read(buffer) > 0) {
134 buffer.flip();
135 uploaded += Integer.valueOf(channel.write(buffer)).longValue();
136 buffer.compact();
137 } else {
138 buffer.flip();
139 while (buffer.hasRemaining()) {
140 uploaded += Integer.valueOf(channel.write(buffer))
141 .longValue();
142 }
143 close();
144 return;
145 }
146 }
147 }
148
149
150 @Override
151 public void onAllDataRead() throws IOException {
152 close();
153 }
154
155
156
157
158
159
160 protected void close() throws IOException {
161 try {
162 inChannel.close();
163 channel.close();
164
165
166 if (!response.isCommitted()) {
167 response.setStatus(HttpServletResponse.SC_OK);
168 }
169 if (callback != null) {
170 callback.uploadCompleted(path.toString(), uploaded);
171 }
172 } finally {
173 context.complete();
174 }
175 }
176
177
178 @Override
179 public void onError(Throwable e) {
180 try {
181 out.abort();
182 inChannel.close();
183 channel.close();
184 int status;
185 if (e instanceof CorruptLongObjectException) {
186 status = HttpStatus.SC_BAD_REQUEST;
187 LOG.log(Level.WARNING, e.getMessage(), e);
188 } else {
189 status = HttpStatus.SC_INTERNAL_SERVER_ERROR;
190 LOG.log(Level.SEVERE, e.getMessage(), e);
191 }
192 FileLfsServlet.sendError(response, status, e.getMessage());
193 } catch (IOException ex) {
194 LOG.log(Level.SEVERE, ex.getMessage(), ex);
195 }
196 }
197 }