Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

ObjectUploadListener.java 5.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. /*
  2. * Copyright (C) 2015, Matthias Sohn <matthias.sohn@sap.com> and others
  3. *
  4. * This program and the accompanying materials are made available under the
  5. * terms of the Eclipse Distribution License v. 1.0 which is available at
  6. * https://www.eclipse.org/org/documents/edl-v10.php.
  7. *
  8. * SPDX-License-Identifier: BSD-3-Clause
  9. */
  10. package org.eclipse.jgit.lfs.server.fs;
  11. import java.io.FileNotFoundException;
  12. import java.io.IOException;
  13. import java.nio.ByteBuffer;
  14. import java.nio.channels.Channels;
  15. import java.nio.channels.ReadableByteChannel;
  16. import java.nio.channels.WritableByteChannel;
  17. import java.nio.file.Path;
  18. import java.util.logging.Level;
  19. import java.util.logging.Logger;
  20. import javax.servlet.AsyncContext;
  21. import javax.servlet.ReadListener;
  22. import javax.servlet.ServletInputStream;
  23. import javax.servlet.http.HttpServletRequest;
  24. import javax.servlet.http.HttpServletResponse;
  25. import org.apache.http.HttpStatus;
  26. import org.eclipse.jgit.lfs.errors.CorruptLongObjectException;
  27. import org.eclipse.jgit.lfs.internal.AtomicObjectOutputStream;
  28. import org.eclipse.jgit.lfs.lib.AnyLongObjectId;
  29. import org.eclipse.jgit.lfs.lib.Constants;
  30. /**
  31. * Handle asynchronous object upload.
  32. *
  33. * @since 4.6
  34. */
  35. public class ObjectUploadListener implements ReadListener {
  36. private static final Logger LOG = Logger
  37. .getLogger(ObjectUploadListener.class.getName());
  38. private final AsyncContext context;
  39. private final HttpServletResponse response;
  40. private final ServletInputStream in;
  41. private final ReadableByteChannel inChannel;
  42. private final AtomicObjectOutputStream out;
  43. private WritableByteChannel channel;
  44. private final ByteBuffer buffer = ByteBuffer.allocateDirect(8192);
  45. private final Path path;
  46. private long uploaded;
  47. private Callback callback;
  48. /**
  49. * Callback invoked after object upload completed.
  50. *
  51. * @since 5.1.7
  52. */
  53. public interface Callback {
  54. /**
  55. * Notified after object upload completed.
  56. *
  57. * @param path
  58. * path to the object on the backend
  59. * @param size
  60. * uploaded size in bytes
  61. */
  62. void uploadCompleted(String path, long size);
  63. }
  64. /**
  65. * Constructor for ObjectUploadListener.
  66. *
  67. * @param repository
  68. * the repository storing large objects
  69. * @param context
  70. * a {@link javax.servlet.AsyncContext} object.
  71. * @param request
  72. * a {@link javax.servlet.http.HttpServletRequest} object.
  73. * @param response
  74. * a {@link javax.servlet.http.HttpServletResponse} object.
  75. * @param id
  76. * a {@link org.eclipse.jgit.lfs.lib.AnyLongObjectId} object.
  77. * @throws java.io.FileNotFoundException
  78. * @throws java.io.IOException
  79. */
  80. public ObjectUploadListener(FileLfsRepository repository,
  81. AsyncContext context, HttpServletRequest request,
  82. HttpServletResponse response, AnyLongObjectId id)
  83. throws FileNotFoundException, IOException {
  84. this.context = context;
  85. this.response = response;
  86. this.in = request.getInputStream();
  87. this.inChannel = Channels.newChannel(in);
  88. this.out = repository.getOutputStream(id);
  89. this.channel = Channels.newChannel(out);
  90. this.path = repository.getPath(id);
  91. this.uploaded = 0L;
  92. response.setContentType(Constants.CONTENT_TYPE_GIT_LFS_JSON);
  93. }
  94. /**
  95. * Set the callback to invoke after upload completed.
  96. *
  97. * @param callback
  98. * the callback
  99. * @return {@code this}.
  100. * @since 5.1.7
  101. */
  102. public ObjectUploadListener setCallback(Callback callback) {
  103. this.callback = callback;
  104. return this;
  105. }
  106. /**
  107. * {@inheritDoc}
  108. *
  109. * Writes all the received data to the output channel
  110. */
  111. @Override
  112. public void onDataAvailable() throws IOException {
  113. while (in.isReady()) {
  114. if (inChannel.read(buffer) > 0) {
  115. buffer.flip();
  116. uploaded += Integer.valueOf(channel.write(buffer)).longValue();
  117. buffer.compact();
  118. } else {
  119. buffer.flip();
  120. while (buffer.hasRemaining()) {
  121. uploaded += Integer.valueOf(channel.write(buffer))
  122. .longValue();
  123. }
  124. close();
  125. return;
  126. }
  127. }
  128. }
  129. /** {@inheritDoc} */
  130. @Override
  131. public void onAllDataRead() throws IOException {
  132. close();
  133. }
  134. /**
  135. * Close resources held by this listener
  136. *
  137. * @throws java.io.IOException
  138. */
  139. protected void close() throws IOException {
  140. try {
  141. inChannel.close();
  142. channel.close();
  143. // TODO check if status 200 is ok for PUT request, HTTP foresees 204
  144. // for successful PUT without response body
  145. if (!response.isCommitted()) {
  146. response.setStatus(HttpServletResponse.SC_OK);
  147. }
  148. if (callback != null) {
  149. callback.uploadCompleted(path.toString(), uploaded);
  150. }
  151. } finally {
  152. context.complete();
  153. }
  154. }
  155. /** {@inheritDoc} */
  156. @Override
  157. public void onError(Throwable e) {
  158. try {
  159. out.abort();
  160. inChannel.close();
  161. channel.close();
  162. int status;
  163. if (e instanceof CorruptLongObjectException) {
  164. status = HttpStatus.SC_BAD_REQUEST;
  165. LOG.log(Level.WARNING, e.getMessage(), e);
  166. } else {
  167. status = HttpStatus.SC_INTERNAL_SERVER_ERROR;
  168. LOG.log(Level.SEVERE, e.getMessage(), e);
  169. }
  170. FileLfsServlet.sendError(response, status, e.getMessage());
  171. } catch (IOException ex) {
  172. LOG.log(Level.SEVERE, ex.getMessage(), ex);
  173. }
  174. }
  175. }