I am calling one action for csv download in IE9 using OAF framework.
After processing gets completed, browser is showing the csv to Save or Download.
But, problem is my browser gerts hanged with processing mouse sign,for which I have to press F5 to kill the session.
Then browser is allowing me to Open/Download the CSV file.
The prob is ,I am able to close the session at browser level as I have been using OAF and no javascripts codes are being used in my code.
My code is as below:
HttpServletResponse response =
(HttpServletResponse)pageContext.getRenderingContext().getServletResponse();
response.setContentType("text/csv");
response.setHeader("Content-Disposition",
"attachment; filename=" + file_name + ".csv");
ServletOutputStream pw = null;
The important part is how you handle HttpServletResponse after your work is done.
Try the below code along with your try-catch block:
InputStream in = null;
finally
{
try
{
pw.flush();
pw.close();
if (in != null)
{
in.close();
}
}catch (Exception e)
{
e.printStackTrace();
}
}
Related
I have an ssh manager to execute (bash) scripts on a server. It contains a commandWithContinousRead(String command, Consumer<String> consumer). Whenever an echo is called in the bash script it is consumed by the consumer. I want to extend this with Spring Boot and an HTTP call. When a client sends a request, the server streams the data when it's ready from a bash script and the client can print it out.
I know Server-Sent Events, however, I feel like that is mostly for events and usually uses multiple resources on an API.
Additionally, I tried searching for streaming topics, but had no success. I did find StreamingResponseBody from Spring, but it collects all the data and then sends it all at once.
I used Postman for testing, maybe it cannot handle streaming?
However, how do I test this?
Example:
#/bin/bash
# Scriptname: stream-this.sh
echo "Starting line"
sleep 4
echo "Middle line"
sleep 4
echo "End line"
Request with commandWithContinousRead, but prints everything at once after eight seconds.
#RequestMapping(value = "/stream-this", method = RequestMethod.POST,
produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public ???? streamScript() {
StreamingResponseBody stream = out -> {
sshManager.commandWithContinousRead("bash /scripts/stream-this.sh", echo -> {
try {
byte[] bytes = echo.getBytes(StandardCharsets.UTF_8);
out.write(bytes);
System.out.println(echo);
} catch (IOException e) {
e.printStackTrace();
}
});
};
return new ResponseEntity<>(stream, HttpStatus.OK);
}
Implementation of commandWithContinousRead function.
public void commandWithContinousRead(String command, Consumer<String> consumer) {
SSHClient client = buildClient();
try (Session session = client.startSession()) {
Session.Command cmd = session.exec(command);
BufferedReader br = new BufferedReader(new InputStreamReader(cmd.getInputStream(), StandardCharsets.UTF_8));
String line;
while ((line = br.readLine()) != null) {
consumer.accept(line);
}
br.close();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
client.disconnect();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Now that you have posted the commandWithContinuousRead method, everything looks correct. Also, you've just now stated that you're testing with Postman, and that's definitely a problem -- postman doesn't support streaming responses
https://github.com/postmanlabs/postman-app-support/issues/5040
It's always a good idea to programmatically unit and integration test your code. A simple unit test doesn't even need to use Spring, or a real SSH connection (run the bash script local to the test). The unit test would just be testing the logic of your Consumer and would let you know that the reading of the output, and the bash script itself aren't blocking. Ideally, you would use junit, but here's a simple test class that I put together that shows what I mean.
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;
import java.lang.Process;
import java.nio.charset.StandardCharsets;
import java.util.function.Consumer;
public class Test {
// This would be a #Test instead of a main
public static void main(String... args) {
commandWithContinousRead("bash stream-this.sh", echo -> {
byte[] bytes = echo.getBytes(StandardCharsets.UTF_8);
// assert statements go here
System.out.println("In main -- " + echo);
});
}
public static void commandWithContinousRead(String command, Consumer<String> consumer) {
try {
Process process = Runtime.getRuntime().exec(command);
BufferedReader br = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line;
while ((line = br.readLine()) != null) {
consumer.accept(line);
}
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
An integration test would actually setup Spring, and would go through the endpoint, thereby testing in the same manner that the client/browser would. Commonly, this is done using #WebMvcTest and mockMvc async. You could choose to either mock the SSH client, or to have a server setup explicitly so your actual SSH client can connect to it. (The second option would expose/eliminate issues related to the ssh connection). This kind of test would expose issues with the spring setup/streaming response. You would need to set an artificial timeout on your mock mvc after say, 5 seconds and using a new mock mvc, after 9 seconds That would allow you to see that after 5 seconds, you've received the first echo, and after 9, you have the whole expected response. A good starting point for you would be to look at https://www.tabnine.com/code/java/methods/org.springframework.test.web.servlet.result.RequestResultMatchers/asyncStarted
Having passed those two levels of tests, then you would begin to suspect the client, which in this case, is Postman. If possible, try to use the actual browser(s) or clients that will be running your code. It may turn out that streaming might not be an option for you.
Please post the implementation of commandWithContinousRead
It could be a fundamental problem where the script that is echoing and sleeping is running on the same thread as the code that is supposed to read the echo and print them out. I.e., you're blocking while you wait for the bash script itself to run which would explain the 8 second delay before getting any output. Also, what type does commandWithContinousRead return? Depending, on how you're "reading" the echos in that method, you could be blocking there too. It's hard to say with 100% certainty without seeing the code for commandWithContinousRead.
Your return type will be a ResponseEntity<StreamingResponseBody> (to fill in the ????)
Okay, I came up with a solution that worked. As Pickled Brain mentioned, the main problem was Postman not working with streaming. Also, I went back to try SSE in a single call and I did by running the bash script in another thread. Additionally, I created an SSE client in Nodejs for testing purposes and it worked flawlessly.
Function to run the script, and place it in another thread.
private SseEmitter runScript() {
SseEmitter emitter = new SseEmitter(-1L); // -1L = no timeout
ExecutorService sseMvcExecutor = Executors.newSingleThreadExecutor();
sseMvcExecutor.execute(() -> {
try {
shellManager.commandWithContinousRead("bash scriptname"), s -> {
SseEmitter.SseEventBuilder event = SseEmitter.event().name("message").data(s);
try {
emitter.send(event);
System.out.println(s);
} catch (IOException e) {
e.printStackTrace();
}
});
emitter.send(SseEmitter.event().name("close").data(""));
} catch (IOException e) {
e.printStackTrace();
}
emitter.complete();
});
return emitter;
}
SSE Client:
const EventSource = require('eventsource'); // npm install eventsource
const url = 'yoururl';
var es = new EventSource(url);
es.onopen = function(ev) {
console.log("OPEN");
console.log(ev);
};
es.onmessage = function(ev) {
console.log("MESSAGE");
console.log(ev.data);
};
es.addEventListener('close', function() {
es.close();
console.log('closing!');
});
es.onerror = function(ev) {
console.log("ERROR");
console.log(ev);
es.close();
};
process.on('SIGINT', () => {
es.close();
console.log(es.CLOSED);
});
I'm trying to do repeat payments with Form Integration in Sagepay (now Opayo).
From an earlier problem posted on here, I get that the securitykey is needed but is not returned in the Form call, so an additional call needs to be made to the getTransactionDetails command.
I have the securitykey and can now make a call to https://test.sagepay.com/gateway/service/repeat.vsp to initiate the repeat payment. However, the documentation does not say where the response to that call goes. I assume therefore, that it would go to the NotificationURL that is set up with a payment when using the Server or Direct integrations. Since I'm using Form, this is not set.
The question is, is there any way of capturing the response to the https://test.sagepay.com/gateway/service/repeat.vsp call if the initial payment was created using Form integration?
I suppose the second question is, has anybody successfully made repeat payments work with Sagepay Form integration?
Not sure if this helps you and we didn't do repeat payments; but we are looking at releasing deferred payments and I think it is a similar approach.
How do you make the call to 'https://test.sagepay.com/gateway/service/repeat.vsp'?
Could you use a 'HttpWebRequest' to make the call then capture the direct response in 'HttpWebResponse'?
EG:
private static void DeferredSharedApiCall(Dictionary<string, string> data, string type, string url)
{
string postData = string.Join("&", data.Select(x => $"{x.Key}={HttpUtility.UrlEncode(x.Value)}"));
HttpWebRequest request = WebRequest.CreateHttp(url);
request.Method = "POST";
request.ContentType = "application/x-www-form-urlencoded";
using (TextWriter tw = new StreamWriter(request.GetRequestStream()))
{
tw.Write(postData);
}
HttpWebResponse response = null;
try
{
response = request.GetResponse() as HttpWebResponse;
}
catch (WebException ex)
{
//log.Error($"{type} Error, data: {postData}", ex);
}
catch (Exception ex)
{
//log.Error($"{type} Error, data: {postData}", ex);
}
if (response != null)
{
using (TextReader tr = new StreamReader(response.GetResponseStream()))
{
string result = tr.ReadToEnd();
//log.Info($"{type} Response: {Environment.NewLine}{result}");
}
}
}
How can I get a pdf located in a file in a server's directory structure to load in a browser for users of a Spring MVC application?
I have googled this and found postings about how to generate PDFs, but their answers do not work in this situation. For example, this other posting is not relevant because res.setContentType("application/pdf"); in my code below does not solve the problem. Also, this other posting describes how to do it from a database but does not show full working controller code. Other postings had similar problems that caused them not to work in this case.
I need to simply serve up a file (not from a database) and have it been viewable by a user in their browser. The best I have come up with is the code below, which asks the user to download the PDF or to view it in a separate application outside the browser. What specific changes can I make to the specific code below so that the user automatically sees the PDF content inside their browser when they click on the link instead of being prompted to download it?
#RequestMapping(value = "/test-pdf")
public void generatePdf(HttpServletRequest req,HttpServletResponse res){
res.setContentType("application/pdf");
res.setHeader("Content-Disposition", "attachment;filename=report.pdf");
ServletOutputStream outStream=null;
try {
BufferedInputStream bis = new BufferedInputStream(
new FileInputStream(new File("/path/to", "nameOfThe.pdf")));
/*ServletOutputStream*/ outStream = res.getOutputStream();
//to make it easier to change to 8 or 16 KBs
int FILE_CHUNK_SIZE = 1024 * 4;
byte[] chunk = new byte[FILE_CHUNK_SIZE];
int bytesRead = 0;
while ((bytesRead = bis.read(chunk)) != -1) {outStream.write(chunk, 0, bytesRead);}
bis.close();
outStream.flush();
outStream.close();
}
catch (Exception e) {e.printStackTrace();}
}
Change
res.setHeader("Content-Disposition", "attachment;filename=report.pdf");
To
res.setHeader("Content-Disposition", "inline;filename=report.pdf");
You should also set the Content Length
FileCopyUtils is handy:
#Controller
public class FileController {
#RequestMapping("/report")
void getFile(HttpServletResponse response) throws IOException {
String fileName = "report.pdf";
String path = "/path/to/" + fileName;
File file = new File(path);
FileInputStream inputStream = new FileInputStream(file);
response.setContentType("application/pdf");
response.setContentLength((int) file.length());
response.setHeader("Content-Disposition", "inline;filename=\"" + fileName + "\"");
FileCopyUtils.copy(inputStream, response.getOutputStream());
}
}
I'm using JSF 2.1.7 + Prime Faces + Prime Faces Mobile.
I have a page that references a image:
<img src="/charts/chart1.png"/>
There's a separate background process that runs to update chart1.png every 2 minutes. (It gets the image from a particular online url). Here is the code I use to save the image:
public static String saveImage(final String strurl, final String imageFileName){
//assume the url for the image is valid
try {
URL url = new URL(strurl);
HttpURLConnection conn = (HttpURLConnection )url.openConnection();
conn.setConnectTimeout(1000); //1 sec timeout
conn.connect();
File file = new File(imageFileName);
BufferedImage image = ImageIO.read(conn.getInputStream());
ImageIO.write(image, "png", file);
return file.getAbsolutePath();
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
ImageIO.write will delete the existing image file and write out a new one. I believe the above will result in a few milliseconds where the image file is not available, and hence the web user will not be able to see the image.
My questions are:
1) Is it true that there will be a time where the image is temporarily unavailable?
2) And if that is the case, what is a possible solution to fix this, so that user will always have a available image?
Thanks in advance. =)
Heres what i would do
1) First instead of replacing the image, i create a new image chart2.png
2)Then I would change the url in the IMG tag to point to a Servlet,
say
and in that servlet which will be mapped to /charts/showchart
,
And in the servlet i would check for latest available PNG , and stream it as response.
This way you customize how or what image you show.
To start off with, this might be tagged as a duplicate of the following thread:
Wait for HttpWebRequest.BeginGetResponse to finish in Windows Phone 7, however, the responses in that thread did not help me get over my problem.
To begin with, I am collecting user data on the UI Thread in order to process application registration, where I also have an instance of ManualResetEvent:
private static ManualResetEvent registrationEvent = new ManualResetEvent(false);
I have another thread which handles the registration process (and includes the HttpWebRequest.BeginGetResponse() and its corresponding callback method.)
Thread t = new Thread(() => RegistrationHandler.sendRegistrationData(url));
t.Start();
Right after this call, I block the current (UI) thread with a call to
registrationEvent.WaitOne();
//Process the response, update some UI elements and navigate to a different page.
httpSessionCompleted(response);
Once the thread handling the registration process starts, I am instantiating HttpWebRequest and invoking the BeginGetResponse() method on it.
try
{
HttpWebRequest request = HttpWebRequest.CreateHttp(url);
request.Method = "POST";
request.ContentType = mimeType;
request.BeginGetResponse(new AsyncCallback(GetRequestCallback), request);
}
catch (Exception ex)
{
Console.WriteLine("Exception caught in sendData(): {0}", ex.Message);
}
Now the issue is that the callback method (code below) is never invoked, and the application just freezes. There also doesn't seem to be any exception(s) thrown either.
try
{
HttpWebRequest request = (HttpWebRequest)asyncResult.AsyncState;
if (request != null)
{
using (HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(asyncResult))
{
using (StreamReader reader = new StreamReader(response.GetResponseStream()))
{
String result = reader.ReadToEnd();
Globals.HostResponse = result;
//Signalling the calling thread to continue execution
RegistrationPage.RegistrationEvent.Set();
}
}
}
}
catch (Exception ex)
{
Console.WriteLine("Exception caught in GetRequestCallback(): {0}", ex.Message);
}
I ideally want my application to continue from httpSessionCompleted() after the callback method finishes execution. Can someone please help me with some guidance/suggestions?
Sorry for being verbose. Thanks!
You should not block UI thread, use callback pattern instead. Look at this: Windows Phone 7 - wait for Webclient to complete . Hope this helps