You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

ObjectDownloadListener.java 3.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  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.IOException;
  12. import java.nio.ByteBuffer;
  13. import java.nio.channels.Channels;
  14. import java.nio.channels.ReadableByteChannel;
  15. import java.nio.channels.WritableByteChannel;
  16. import java.util.logging.Level;
  17. import java.util.logging.Logger;
  18. import javax.servlet.AsyncContext;
  19. import javax.servlet.ServletOutputStream;
  20. import javax.servlet.WriteListener;
  21. import javax.servlet.http.HttpServletResponse;
  22. import org.apache.http.HttpStatus;
  23. import org.eclipse.jgit.lfs.lib.AnyLongObjectId;
  24. import org.eclipse.jgit.lfs.lib.Constants;
  25. import org.eclipse.jgit.util.HttpSupport;
  26. /**
  27. * Handle asynchronous large object download.
  28. *
  29. * @since 4.7
  30. */
  31. public class ObjectDownloadListener implements WriteListener {
  32. private static final Logger LOG = Logger
  33. .getLogger(ObjectDownloadListener.class.getName());
  34. private final AsyncContext context;
  35. private final HttpServletResponse response;
  36. private final ServletOutputStream out;
  37. private final ReadableByteChannel in;
  38. private final WritableByteChannel outChannel;
  39. private ByteBuffer buffer = ByteBuffer.allocateDirect(8192);
  40. /**
  41. * <p>Constructor for ObjectDownloadListener.</p>
  42. *
  43. * @param repository
  44. * the repository storing large objects
  45. * @param context
  46. * the servlet asynchronous context
  47. * @param response
  48. * the servlet response
  49. * @param id
  50. * id of the object to be downloaded
  51. * @throws java.io.IOException
  52. */
  53. public ObjectDownloadListener(FileLfsRepository repository,
  54. AsyncContext context, HttpServletResponse response,
  55. AnyLongObjectId id) throws IOException {
  56. this.context = context;
  57. this.response = response;
  58. this.in = repository.getReadChannel(id);
  59. this.out = response.getOutputStream();
  60. this.outChannel = Channels.newChannel(out);
  61. response.addHeader(HttpSupport.HDR_CONTENT_LENGTH,
  62. String.valueOf(repository.getSize(id)));
  63. response.setContentType(Constants.HDR_APPLICATION_OCTET_STREAM);
  64. }
  65. /**
  66. * {@inheritDoc}
  67. *
  68. * Write file content
  69. */
  70. @Override
  71. public void onWritePossible() throws IOException {
  72. while (out.isReady()) {
  73. try {
  74. buffer.clear();
  75. if (in.read(buffer) < 0) {
  76. buffer = null;
  77. } else {
  78. buffer.flip();
  79. }
  80. } catch (Throwable t) {
  81. LOG.log(Level.SEVERE, t.getMessage(), t);
  82. buffer = null;
  83. } finally {
  84. if (buffer != null) {
  85. outChannel.write(buffer);
  86. } else {
  87. try {
  88. in.close();
  89. } catch (IOException e) {
  90. LOG.log(Level.SEVERE, e.getMessage(), e);
  91. }
  92. try {
  93. out.close();
  94. } finally {
  95. context.complete();
  96. }
  97. // This is need to avoid endless loop in recent Jetty versions.
  98. // That's because out.isReady() is returning true for already
  99. // closed streams and because out.close() doesn't throw any
  100. // exception any more when trying to close already closed stream.
  101. return;
  102. }
  103. }
  104. }
  105. }
  106. /**
  107. * {@inheritDoc}
  108. *
  109. * Handle errors
  110. */
  111. @Override
  112. public void onError(Throwable e) {
  113. try {
  114. FileLfsServlet.sendError(response,
  115. HttpStatus.SC_INTERNAL_SERVER_ERROR, e.getMessage());
  116. context.complete();
  117. in.close();
  118. } catch (IOException ex) {
  119. LOG.log(Level.SEVERE, ex.getMessage(), ex);
  120. }
  121. }
  122. }