Situation:
I have a Java (Spring) web application, where a user can upload a file. This file will then be analyzed and the results are presented to the user on a new JSP.
The simplified controller method looks like this:
#RequestMapping(method = RequestMethod.POST)
public String fileUploaded(Model model, File file, BindingResult result, HttpServletRequest request, HttpServletResponse response) {
/** Do very long calculations. */
model.addAttribute("map", /** very large map */);
return "result_page";
}
Problem:
The code works fine on my local Netbeans/Tomcat configuration. But on the server (after deploying the *.war), the application does not work for large files. The program runs till the return "result_page"; statement and then the page is not displayed. Without any error in the tomcat Log (catalina.out). The previous upload-JSP remains present. Note, that the large files are working on my local Tomcat.
Tried Solutions:
I thought the problem could be the session-timeout. On my local config, the large files took 20 minutes until the return is reached. On the server, it takes 40 minutes. Thus, the default 30 minutes timeout could be an issue. I tried to change the timeout in the web.xml. But setting the session-timeout to 120 minutes did not solve the issue. Also setting the timeout to 1 minute didn't reproduce the error on my local configuration.
Question:
What am I missing? Is it the timeout I just configured wrong, or what could be the reason? Are there other Log-Files I can search for Error-Messages?
Related
I have a Spring Boot app using Spring Session and Spring Security. Java # config. That's all working. I can set my timeout dynamically as I need.
What I need to do is check the remaining session time from the browser. I guess I could check the server with an ajax request every X minutes and have a servlet do a session.getLastAccessedTime() and a session.getMaxInactiveInterval() and do the math to see what time is remaining and return that. (not sure if that would refresh the lastAccessedTime though..)
But as with everything Spring-related, my first instinct is usually wrong. Ha. Plus if the browser is sitting idle for 30 minutes, seems silly to ping the server every ~minute when nothing has changed.
I'm seeing if anyone has a better solution for this. I'd think that maybe I could set a cookie with a epoch time (~1602002425) when the session will timeout. Then I can watch that value with a javascript function. But I don't see a way to do that in Spring. I tried making a CookieSerializer bean creating a custom cookie name, but there is not a way I can see to SET that value. (looks like a random base64 string value gets created for me) This is probably Spring preventing me from doing something the "wrong way". Then this cookie would need to be updated whenever the Session is refreshed as well.
I've spend a couple days looking from an answer online and all I see are ways to set that Session timeout value servers-side (which I can already so) or JavaScript solutions front-end that are not Spring specific.
End goal is to get a ~"session is about to expire.. refresh?" button to popup when client is getting close to timeout. Does anyone have an approach they've use that works for this? I feel like someone has to have solved this, but I am not seeing any posts that address this. Thanks!
=======
EDIT
I figured out I can use this code to set a cookie and return the lastAccessed time in the response body..
#RequestMapping("/api/timeout")
#ResponseBody
public String add(HttpServletRequest request, HttpServletResponse response) throws Exception
{
return getLastAccessed(request, response);
}
String getLastAccessed(HttpServletRequest request, HttpServletResponse response)
{
ServletRequestAttributes attr = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
HttpSession session = attr.getRequest().getSession(false);
long lastAccessed = session.getLastAccessedTime();
final String cookieName = "timeout";
final String cookieValue = String.valueOf(lastAccessed);
final Boolean useSecureCookie = false;
final int expiryTime = 60 * 60 * 24; // 24h in seconds
final String cookiePath = "/";
Cookie cookie = new Cookie(cookieName, cookieValue);
cookie.setSecure(useSecureCookie);
cookie.setMaxAge(expiryTime);
cookie.setPath(cookiePath);
response.addCookie(cookie);
return String.valueOf(lastAccessed);
}
But as I feared, the lastAccessed timestamp gets updated every time I check for it. I need to find a way to check that from the client browser without updating it, or another approach that I can use where the cookie either gets update with any 'other' request, or some other "Spring way" of doing this that I am unaware of currently.
Not quite an answer, and this is a bit late for your question, but i am currently trying to solve this problem by looking at using websockets to contact the client pre-emptivly .
My current approach is to have a thread that checks the timeout every n minutes, and is reset whenever a endpoint is called (detecting endpoint calls as per this thread)
https://stackoverflow.com/a/22371901
when timer gets close to 0, use websockets to let the client know
Good overview here https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API
We are using spring boot and angular, so this tutorial may also help, but it does have some angular specific stuff.
https://www.javaguides.net/2019/06/spring-boot-angular-8-websocket-example-tutorial.html
good luck if you are still trying at this.
I have the following controller method:
#Transactional
#PostMapping(path = "/add")
#Secured({Constants.ADMIN, Constants.DATAMANAGER})
public DeferredResult<ResponseEntity> addNewCitizens(
#RequestBody List<Citizen> citizens) {
// Time out in 30 minutes
DeferredResult<ResponseEntity> output = new DeferredResult<>((long) (1000 * 60 * 30));
ForkJoinPool.commonPool().submit(() -> {
// Long operation...
output.setResult(new ResponseEntity(HttpStatus.OK));
}
return output;
}
The method usually receives a lot of data and uses it to update my database. For the data I'm testing with right now it takes 3-4 minutes.
For some reason, when it takes a while for it to process the request, it starts all over when it has finished running once, as if it received another request. The Angular front end application from which i sent the request never receives a response in this case. For fast requests it works perfectly. Also, I checked the network tab in Chrome and I can only see one request getting sent to the endpoint in there.
What could be causing this, and what can I do to fix it? Thanks!
Additional info: It only happens when I deploy the service to a remote server - not when I test on my local machine...
I was wondering if anyone would know if I could use the watch service in a FileInboundChannelAdapter along with a LastModifiedFileListFilter?
The sample code below is giving me fairly inconsistent results. Sometimes the file just sits in the folder and remains unprocessed.
I suspect that the watch service might be incompatible with the LastModifiedFileListFilter. For e.g.
If the LastModifiedFileListfilter is set to look for files at least 5
seconds old, and the poller is set to poll every 10 seconds.
At the 9th second, a file could be created in the watched folder.
At 10 seconds the poller queries the watch service to find out what
changed in the past 10 seconds.
It finds the newly created file.
The newly created has a last modified time of -1 second, so it
does not process it.
At 20 seconds, the poller queries the watch
service a second time, this time it does not see the unprocessed
file as it was created more than 10 seconds ago.
Would anyone else have any experience with this? Would there be a recommended way to get around this issue and allow me to verify that the file has been fully written before proceeding?
#Bean
public IntegrationFlow ftpInputFileWatcher()
{
return IntegrationFlows.from(ftpInboundFolder(), filePoller())
.handle()
/*abbreviated*/
.get();
}
private FileInboundChannelAdapterSpec ftpInboundFolder() {
LastModifiedFileListFilter lastModifiedFileListFilter = new LastModifiedFileListFilter();
lastModifiedFileListFilter.setAge(5);
return Files.inboundAdapter(inboundFolder)
.preventDuplicates(false)
.useWatchService(true)
.filter(fileAgeFilterToPreventPrematurePickup());
}
protected Consumer<SourcePollingChannelAdapterSpec> filePoller(){
return poller -> poller.poller((Function<PollerFactory, PollerSpec>) p -> p.fixedRate(2000));
}
Thanks!
Yeah, that's good catch!
Right they are not compatible. The WatchService is event-based and store files from the events into the internal queue. When the poller triggers its action, it polls files from that queue and applies its filters. Since LastModifiedFileListFilter discards the file and there is no any events for it any more, we won't see that file again.
Please, raise a JIRA on the matter and we'll think how to be .
Meanwhile as a workaround do not use WatchService for this kind of logic.
How to get loading time of each component of a page in CQ5 from server side.
Here as per my implementation we are getting longest time taking to load page from request.log file. But i need to get each component loading time of page from server side.
I found this link but this will work from client side:
http://www.wemblog.com/2014/05/how-to-find-component-load-time-on-page.html
You have to include logger for every component tag class call and provide stopwatch for entry and exit of the call.
Logger LOG = LoggerFactory.getLogger(classname.class);
StopWatch stopWatch = new StopWatch("new");
stopWatch.start();
stopWatch.stop();
Once you included this in your tag class you could able to find the time taken for each component in a particular page.
You can use putty for accessing your server log.
Starting from version AEM 6.0, there is OOTB feature to measure rendering time for each component on a page.
It is accessible through TouchUI, Developer mode.
However, it won't work if AEM is installed with run mode 'nosamplecontent'.
You can use the RequestProgressTracker, as explained in the Sling documentation
It's obtainable from SlingHttpServletRequest#getRequestProgressTracker. In order to get the timing stats for your components, you can use a Servlet Filter to execute the code on every request.
Whenever the filter is called:
Get the RequestProgressTracker from the request object
Call getMessages to obtain an iterator over a collection of request progress messages for the current request
Analyze the messages to find the resource types and timing information. Unfortunately, every message is only available as a String so you'll need to parse it to get the data.
Let's have a look at some example messages from the docs:
The last message is the kind we're looking for:
TIMER_END{103,/libs/sling/servlet/default/explorer/node.esp#0}
The number 103 is the number of milliseconds the script took to execute. The value after the comma is the script. You can tailor a regular expression to extract both values from every such message.
One of the projects I recently worked on used this approach to report on component performance. We had a neat dashboard in NewRelic with live stats on every component we built.
I have an init method on my #ViewScoped mananged bean. In the post construct i load data from the db. I have a custom ExceptionHandlerWrapper to catch all excptions and send to an error pages. However when #PostConstuct throws an exception i recieve an IllegalStateException and am not redirected to the error page. I have tried many combinations.....
Ive tried this inside my ExcpetionHandler
externalContext.getRequestMap().put(ERROR_BEAN_ID, ERROR_TEXT);
externalContext.dispatch(ERROR_PAGE);
fc.responseComplete();
This line below is what i originally had. It also doent work
externalContext.getFlash().put(ERROR_BEAN_ID, ERROR_TEXT);
nav.handleNavigation(fc, null, ERROR_PAGE);
fc.renderResponse();
These all cause IllegalStateExceptions. I also called redirect with the same result.
Can you globally catch errors thrown from #PostConstruct?
These all cause IllegalStateExceptions.
With the message "Response already committed", I assume? Well, that's a point of no return. A part of the response has already been sent to the client (the webbrowser). It's not possible to take the already sent bytes back. The server will end up with this exception in the logs and the client will end up with a halfbaked response.
What can you do?
Most straightforward way would be to enlarge the response buffer size to the size of the largest page. For example, 64KB:
<context-param>
<param-name>javax.faces.FACELETS_BUFFER_SIZE</param-name>
<param-value>65536</param-value>
</context-param>
It defaults to ~2KB depending on the server configuration. You only need to keep in mind that this may be memory hogging when your server has to process relatively a lot of requests/responses. Profile and measure properly.
Another way is to reference the bean before the response is to be rendered/committed so that it's (post)construction is triggered before that point. Perhaps the bean in question is referenced for the first time at the very bottom of the view, long beyond the ~2KB response size border. You could take over the job of #PostConstruct with a <f:event type="preRenderView"> somewhere in the top of the view. E.g.
<f:event type="preRenderView" listener="#{bean.init}" />
with
public void init() {
if (!FacesContext.getCurrentInstance().isPostback()) {
// Do here your original #PostConstruct job.
}
}