Bạn có chắc chắn rằng lời khuyên giao dịch đang hoạt động không? By default, Mùa xuân sử dụng chế độ lời khuyên "proxy". Lời khuyên giao dịch sẽ chỉ chạy nếu bạn đã đăng ký trường hợp tài nguyên của bạn với JAX-RS Application
hoặc nếu bạn đang sử dụng chế độ dệt "aspectj" thay vì chế độ lời khuyên "proxy" mặc định.
Giả sử rằng giao dịch physical không được sử dụng lại do việc tuyên truyền giao dịch, sử dụng phương thức tải xuống()là không chính xác nói chung.
Nếu lời khuyên giao dịch thực sự đang chạy, giao dịch kết thúc khi trở về từ phương thức tải xuống(). Blob
Javadoc nói: "Đối tượng A Blob
hợp lệ trong thời gian giao dịch được tạo." Tuy nhiên, §16.3.7 của đặc tả JDBC 4.2 cho biết: "Blob
, Clob
và NClob
đối tượng vẫn còn hiệu lực trong ít nhất thời lượng của giao dịch mà chúng được tạo." Do đó, InputStream
được trả về bởi getBinaryStream() không được đảm bảo là hợp lệ để phục vụ phản hồi; hiệu lực sẽ phụ thuộc vào bất kỳ bảo đảm nào được cung cấp bởi trình điều khiển JDBC. Để có tính di động tối đa, bạn nên dựa vào số Blob
chỉ hợp lệ trong thời gian giao dịch.
Bất kể lời khuyên giao dịch có đang chạy hay không, bạn có thể có điều kiện chủng tộc vì kết nối JDBC cơ bản được sử dụng để truy xuất Blob
có thể được sử dụng lại theo cách làm mất hiệu lực Blob
.
EDIT: Testing Jersey 2.17, có vẻ như hành vi xây dựng Response
từ một InputStream
tùy thuộc vào loại MIME phản hồi được chỉ định. Trong một số trường hợp, InputStream
được đọc hoàn toàn vào bộ nhớ trước khi gửi phản hồi. Trong các trường hợp khác, InputStream
được phát lại.
Đây là trường hợp thử nghiệm của tôi:
@Path("test")
public class MyResource {
@GET
public Response getIt() {
return Response.ok(new InputStream() {
@Override
public int read() throws IOException {
return 97; // 'a'
}
}).build();
}
}
Nếu getIt() phương pháp được chú thích với @Produces(MediaType.TEXT_PLAIN)
hoặc không @Produces
chú thích, sau đó Jersey cố gắng để đọc toàn bộ (vô hạn) InputStream
vào bộ nhớ và máy chủ ứng dụng cuối cùng gặp sự cố khi hết bộ nhớ. Nếu phương thức getIt() được chú thích bằng @Produces(MediaType.APPLICATION_OCTET_STREAM)
, thì phản hồi sẽ được phát lại.
Vì vậy, phương thức tải xuống() của bạn có thể hoạt động đơn giản vì đốm màu là không phải là đang được phát lại. Jersey có thể đang đọc toàn bộ đốm màu trong bộ nhớ.
liên quan: How to stream an endless InputStream with JAX-RS
EDIT2: Tôi đã tạo ra một dự án trình diễn sử dụng Spring Boot và Apache CXF:
https://github.com/dtrebbien/so30356840-cxf
Nếu bạn chạy dự án và thực hiện trên dòng lệnh:
curl 'http://localhost:8080/myapp/test/data/1' >/dev/null
Sau đó, bạn sẽ thấy kết xuất nhật ký như sau:
2015-06-01 15:58:14.573 DEBUG 9362 --- [nio-8080-exec-1] org.apache.cxf.transport.http.Headers : Request Headers: {Accept=[*/*], Content-Type=[null], host=[localhost:8080], user-agent=[curl/7.37.1]}
2015-06-01 15:58:14.584 DEBUG 9362 --- [nio-8080-exec-1] org.apache.cxf.jaxrs.utils.JAXRSUtils : Trying to select a resource class, request path : /test/data/1
2015-06-01 15:58:14.585 DEBUG 9362 --- [nio-8080-exec-1] org.apache.cxf.jaxrs.utils.JAXRSUtils : Trying to select a resource operation on the resource class com.sample.resource.MyResource
2015-06-01 15:58:14.585 DEBUG 9362 --- [nio-8080-exec-1] org.apache.cxf.jaxrs.utils.JAXRSUtils : Resource operation getIt may get selected
2015-06-01 15:58:14.585 DEBUG 9362 --- [nio-8080-exec-1] org.apache.cxf.jaxrs.utils.JAXRSUtils : Resource operation getIt on the resource class com.sample.resource.MyResource has been selected
2015-06-01 15:58:14.585 DEBUG 9362 --- [nio-8080-exec-1] o.a.c.j.interceptor.JAXRSInInterceptor : Request path is: /test/data/1
2015-06-01 15:58:14.585 DEBUG 9362 --- [nio-8080-exec-1] o.a.c.j.interceptor.JAXRSInInterceptor : Request HTTP method is: GET
2015-06-01 15:58:14.585 DEBUG 9362 --- [nio-8080-exec-1] o.a.c.j.interceptor.JAXRSInInterceptor : Request contentType is: */*
2015-06-01 15:58:14.585 DEBUG 9362 --- [nio-8080-exec-1] o.a.c.j.interceptor.JAXRSInInterceptor : Accept contentType is: */*
2015-06-01 15:58:14.585 DEBUG 9362 --- [nio-8080-exec-1] o.a.c.j.interceptor.JAXRSInInterceptor : Found operation: getIt
2015-06-01 15:58:14.595 DEBUG 9362 --- [nio-8080-exec-1] o.s.j.d.DataSourceTransactionManager : Creating new transaction with name [com.sample.resource.MyResource.getIt]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''
2015-06-01 15:58:14.595 DEBUG 9362 --- [nio-8080-exec-1] o.s.j.d.DataSourceTransactionManager : Acquired Connection [ProxyConnection[PooledConnection[[email protected]]]] for JDBC transaction
2015-06-01 15:58:14.596 DEBUG 9362 --- [nio-8080-exec-1] o.s.j.d.DataSourceTransactionManager : Switching JDBC Connection [ProxyConnection[PooledConnection[[email protected]]]] to manual commit
2015-06-01 15:58:14.602 DEBUG 9362 --- [nio-8080-exec-1] o.s.jdbc.core.JdbcTemplate : Executing prepared SQL query
2015-06-01 15:58:14.603 DEBUG 9362 --- [nio-8080-exec-1] o.s.jdbc.core.JdbcTemplate : Executing prepared SQL statement [SELECT data FROM images WHERE id = ?]
2015-06-01 15:58:14.620 DEBUG 9362 --- [nio-8080-exec-1] o.s.j.d.DataSourceTransactionManager : Initiating transaction commit
2015-06-01 15:58:14.620 DEBUG 9362 --- [nio-8080-exec-1] o.s.j.d.DataSourceTransactionManager : Committing JDBC transaction on Connection [ProxyConnection[PooledConnection[[email protected]]]]
2015-06-01 15:58:14.621 DEBUG 9362 --- [nio-8080-exec-1] o.s.j.d.DataSourceTransactionManager : Releasing JDBC Connection [ProxyConnection[PooledConnection[[email protected]]]] after transaction
2015-06-01 15:58:14.621 DEBUG 9362 --- [nio-8080-exec-1] o.s.jdbc.datasource.DataSourceUtils : Returning JDBC Connection to DataSource
2015-06-01 15:58:14.621 DEBUG 9362 --- [nio-8080-exec-1] o.a.cxf.phase.PhaseInterceptorChain : Invoking handleMessage on interceptor [email protected]
2015-06-01 15:58:14.622 DEBUG 9362 --- [nio-8080-exec-1] o.a.cxf.phase.PhaseInterceptorChain : Adding interceptor [email protected] to phase prepare-send
2015-06-01 15:58:14.622 DEBUG 9362 --- [nio-8080-exec-1] o.a.cxf.phase.PhaseInterceptorChain : Adding interceptor [email protected] to phase marshal
2015-06-01 15:58:14.622 DEBUG 9362 --- [nio-8080-exec-1] o.a.cxf.phase.PhaseInterceptorChain : Chain [email protected] was created. Current flow:
prepare-send [MessageSenderInterceptor]
marshal [JAXRSOutInterceptor]
2015-06-01 15:58:14.623 DEBUG 9362 --- [nio-8080-exec-1] o.a.cxf.phase.PhaseInterceptorChain : Invoking handleMessage on interceptor [email protected]
2015-06-01 15:58:14.623 DEBUG 9362 --- [nio-8080-exec-1] o.a.cxf.phase.PhaseInterceptorChain : Adding interceptor org.apache.cxf.inte[email protected]6129236d to phase prepare-send-ending
2015-06-01 15:58:14.623 DEBUG 9362 --- [nio-8080-exec-1] o.a.cxf.phase.PhaseInterceptorChain : Chain [email protected] was modified. Current flow:
prepare-send [MessageSenderInterceptor]
marshal [JAXRSOutInterceptor]
prepare-send-ending [MessageSenderEndingInterceptor]
2015-06-01 15:58:14.623 DEBUG 9362 --- [nio-8080-exec-1] o.a.cxf.phase.PhaseInterceptorChain : Invoking handleMessage on interceptor [email protected]
2015-06-01 15:58:14.627 DEBUG 9362 --- [nio-8080-exec-1] o.a.c.j.interceptor.JAXRSOutInterceptor : Response content type is: application/octet-stream
2015-06-01 15:58:14.631 DEBUG 9362 --- [nio-8080-exec-1] o.apache.cxf.ws.addressing.ContextUtils : retrieving MAPs from context property javax.xml.ws.addressing.context.inbound
2015-06-01 15:58:14.631 DEBUG 9362 --- [nio-8080-exec-1] o.apache.cxf.ws.addressing.ContextUtils : WS-Addressing - failed to retrieve Message Addressing Properties from context
2015-06-01 15:58:14.636 DEBUG 9362 --- [nio-8080-exec-1] o.a.cxf.phase.PhaseInterceptorChain : Invoking handleMessage on interceptor org.apache.cxf.inte[email protected]6129236d
2015-06-01 15:58:14.639 DEBUG 9362 --- [nio-8080-exec-1] o.a.c.t.http.AbstractHTTPDestination : Finished servicing http request on thread: Thread[http-nio-8080-exec-1,5,main]
2015-06-01 15:58:14.639 DEBUG 9362 --- [nio-8080-exec-1] o.a.c.t.servlet.ServletController : Finished servicing http request on thread: Thread[http-nio-8080-exec-1,5,main]
Tôi đã cắt đầu ra nhật ký để dễ đọc. Điều quan trọng cần lưu ý là giao dịch được cam kết và kết nối JDBC được trả về trước phản hồi được gửi. Do đó, InputStream
được trả lại bởi blob.getBinaryStream()
không nhất thiết phải hợp lệ và getIt() resource method có thể đang gọi hành vi không xác định.
EDIT3: Thực tiễn được khuyến nghị để sử dụng chú thích @Transactional
của Spring là chú thích phương thức dịch vụ (xem Spring @Transactional Annotation Best Practice). Bạn có thể có một phương pháp dịch vụ tìm thấy đốm màu và chuyển dữ liệu blob đến phản hồi OutputStream
. Phương thức dịch vụ có thể được chú thích bằng @Transactional
để giao dịch trong đó Blob
được tạo sẽ vẫn mở trong suốt thời gian chuyển. Tuy nhiên, có vẻ như với tôi rằng cách tiếp cận này có thể giới thiệu một lỗ hổng dịch vụ từ chối theo cách của "slow read" attack. Bởi vì giao dịch nên được giữ mở trong suốt thời gian chuyển giao cho tính di động tối đa, nhiều độc giả chậm có thể khóa các bảng cơ sở dữ liệu của bạn bằng cách giữ các giao dịch mở.
Một cách tiếp cận có thể là để lưu đốm màu vào tệp tạm thời và phát lại tệp. Xem How do I use Java to read from a file that is actively being written? để biết một số ý tưởng về việc đọc tệp trong khi nó đang được viết đồng thời, mặc dù trường hợp này đơn giản hơn vì độ dài của đốm màu có thể được xác định bằng cách gọi phương thức Blob#length().
§16.3.7 của đặc tả JDBC 4.2 xác nhận giải thích của bạn rằng 'Blob' có thể hợp lệ bên ngoài giao dịch (tôi đã cập nhật câu trả lời của mình cho phù hợp).Tuy nhiên, đọc [Hướng dẫn của nhà phát triển JDBC] của Oracle (http://docs.oracle.com/database/121/JJDBC/toc.htm), tôi không thấy các bảo đảm bổ sung về tính hợp lệ của 'Blob' bên ngoài giao dịch trong được tạo/tạo. Tôi sẽ có nhiều câu hỏi như điều gì xảy ra khi kết nối được sử dụng lại và dữ liệu LOB được sửa đổi? Điều gì sẽ xảy ra khi LOB bị xóa? Điều này chỉ hoạt động trong phạm vi tìm nạp trước LOB không? Vv –