I have a portlet. When the portlet loads, then before the first view is rendered, in some cases there is a need to call a repository which changes data in the database. I wouldn't go into more detail about why this is necessary and answers about this being a design flaw are not helpful. I am aware that it is a design flaw but I would still like to find out an alternative solution to the following problem:
The problem with this set-up is, that browsers send preloading requests. For example the URL of the page where the portlet resides is /test-portlet. Now when you type it in your address-bar then if you have it in your browser history, then the browser sends a GET request to the page already when it suggests it to you. If you press enter before the first GET request is resolved, then the browser sends a new GET request. This means that the portlet receives 2 separate requests which it starts to process parallelly. The first database procedure might work correctly but considering the nature of the database procedure, the second call usually gives an exception.
What would be a nice clean way to deal with the aforementioned problem from the Java application?
Sidenote: I am using Spring MVC.
A simple example of a possible controller:
#RequestMapping
public String index( Model model, RenderRequest request ){
String username = dummyRepository.changeSomeData(request.getAttribute("userId"));
model.add("userName", username);
return "view";
}
I would be interested in a solution to block the first execution altogether. For example somekind of a redirect to POST from controller which the browser wouldn't trigger. Not sure if it is achievable though.
Using locks I think you could solve it, making the secound request wait for the first to finish and then processing it. I don't have experience with locks in java but i found another stack exchange post about file locks in jave:
How can I lock a file using java (if possible)
Please refer to this answer, it might help you to detect and ignore some preloading requests. However you should also make sure the 'worst case' works, perhaps using the locking as suggested by #jpeg, but it could be as easy as using a synchronize block somewhere.
Since I don't see that chrome adds some specific header (or anyhow notifies the server about prerendering state) it is probably not possible to detect it on the server side... at least not directly. You can however simulate the detection on client side and later combine it with server call.
Notice that you can detect prerendering on the client side:
if (document.webkitVisibilityState == 'prerender' || document.visibilityState == 'prerender' || document.visibilityState[0] == 'prerender') {
// prerendering takes place
}
Now, you can break preloading on client side by showing alert box in case browser is in preloading state (or you can probably do the same with just some error in javascript, instead of using alert()):
if (document.webkitVisibilityState == 'prerender' || document.visibilityState == 'prerender' || document.visibilityState[0] == 'prerender') {
alert('this is alert during prerendering..')
}
Now when chrome prerenders the page it will fail because the javascript alert will prevent the browser to continue executing javascript.
If you type in chrome: chrome://net-internals/#prerender you can track when and for which pages chrome executes prerendering. In case of above example (with alert box during prerendering) you can see there:
Link Rel Prerender (cross
domain) http://some.url.which.is.preloaded Javascript
Alert 2015-06-07 19:26:18.758
The final state - Javascript Alret proves that chrome failed to preload the page (I have tested this).
Now how can this solve your issue? Well, you can combine this with asynchronous call (AJAX) and load some content (from another url) depending on wheater the page is actually prerendering or not.
Consider following code (which might be rendered by your portlet under url /test-portlet):
<html>
<body>
<div id="content"></div>
<script>
if (document.webkitVisibilityState == 'prerender' || document.visibilityState == 'prerender' || document.visibilityState[0] == 'prerender') {
// when chrome uses prerendering we block the request with alert
alert('this is alert during prerendering..');
} else {
// in case no prerendering takes place we load the actual content asynchronously
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
// when the content is loaded we place the html inside "content" div
document.getElementById('content').innerHTML = xhr.responseText;
}
}
xhr.open('GET', '/hidden-portlet', true); // we call the actual portlet
xhr.send(null);
}
</script>
</body>
</html>
As you see the /hidden-portlet is only loaded in case browser is loading the page normally (without preloading). The server side handler under url /hidden-portlet (which can be another portlet/servlet) contains actual code which should not be executed during prerendering. So it is the /hidden-portlet which executes
dummyRepository.changeSomeData(request.getAttribute("userId"));
This portlet can also return normal view (rendered html) which will be asynchronously placed on the page under url /test-portlet thanks to the trick on /test-portlet: document.getElementById('content').innerHTML = xhr.responseText;.
So to sumarize the portlet under address /test-portlet only returns html with a javascript code which triggers actual portlet.
If you have many fragile portlets, you can go with this even further, so you can parametrize you /test-portlet with request parameter like /test-portlet?actualUrl=hidden-portlet so that address of the actual portlet is taken from url (which can be read as request parameter on server side). Server will in this case dynamically render the url which should be loaded:
So instead of hardcoded:
xhr.open('GET', '/hidden-portlet', true);
you will have
xhr.open('GET', '/THIS_IS_DYNAMICALLY_REPLACED_EITHER_ON_SERVER_OR_CLIENT_SIDE_WITH_THE_ADDRES_FROM_URL', true);
Related
I am developing a web server and My home page takes request from user and /process servlet processes the request by making calls to other classes and interacting with the database.
/process servlet executes a long running process ( needs to run a perl script using ProcessBuilder, takes 3 mins) and return back to results.jsp?ID=something page.
Now I am doing the following,
Taking request at home.jsp --> /process servlet --> results.jsp
I am not using process.waitFor(), so that my response goes quickly to results.jsp.
And in results.jsp, I am checking for the files (named with ID) to be generated by the perl script and waiting for them to be generated and in meanwhile, I am showing status by refreshing the page every 15 seconds. Whenever the files are generated I am processing them in results.jsp and storing them in the database for further use with the query parameter ID.
And whenever user visits for the seconds time, results.jsp should not process everything, it should just grab the output from the database we previously stored.
Now I am doing too many database operations on results.jsp, but i feel this is the only way I can show them the dynamic status without changing the URL on browser.
Can you please help me out with any other effective way of doing things?
results.jsp?ID=1 draft
database.getOutput(ID) --- to get output corresponding to ID
<% if(ID!= null && database.getOutput(ID) == null) // checking for the first time
{
checking for files on refresh
}
if(database.getOutput(ID) != null)
{
pull details from databse and show here
}
i was trying to call the following web service from my android app, it hung then completed without returning the result:
web service:http://androidexample.com/media/webservice/JsonReturn.php
However when I clicked on the link, it worked fine - the json file displayed. yet it would not work in my app..
but now, it works fine now in my android app, perhaps it was temporarily down is what I am guessing. How can I know if a web service is up and running for an android app to consume ?
Typically, web services are designed to have a status page that can return status text or a HTTP return code to indicate service status.
If it doesn't have that, you can design a function to periodically do a very basic request with a known result to determine state. This is much better than doing a simple ping.
If it was down it would most likely show a HTML error page, which your app would try to parse, which would cause an error.
I had a similar issue, because I needed to know if the user was returning HTML or the correct JSON, to do this I created the ArrayList I was about to use outside of the try/catch of the parse area. You should do the same if you are using a string.
What I mean is, use:
ArrayList<Something> arrayList = new ArrayList<Something>();
String testString = ""; instead of String testString = null;
I was using only ArrayList<Something> arrayList; at one point which is incorrect. If the user then returns HTML, you won't get an error, the user will simply return an empty arraylist or empty string.
You can then plan for that and show some sort of error message. This way you only need one network request but you can still plan for getting the data back, and the server being down.
As I talk about befor I'm using to jQuery to refresh / update a webcam image.
This works just fine if you wanna update the image every 5th or 10sec.
But when your gonna do a stream with 10-15fps it gets into problems with most browsers
it seems. The problem seem to be that it sends a request befor the first one was done.
Is there a way to wait for the first request to be done befor sending a new update request for the webcam image? Because to me it seems to stack up requests if there is alittle delay on the server with the image.
Sorry if I did explain it alittle bad but... I'm norwegian and blode. Not the best combination. :)
Webcam Image is a single url
ex. http://www.ohoynothere.com/image.jpg
Old code I use.
$(document).ready(function() {
setInterval('updateCamera()',3000);
});
function updateCamera() {
$('.online2').each(function() {
var url = $(this).attr('src').split('&')[0];
$(this).attr('src', url + '&rand=' + new Date().getTime());
})
}
Definitely!
It sounds like your best bet would be to use the jQuery.ajax() method ( http://api.jquery.com/jQuery.ajax/ ) or .get() method to chain your requests. Basically, you want a JavaScript function that does a request for the image using the .ajax() call. In the response handler, simply call the function again:
function getMyImage() {
jQuery.get(image_url, function(response) {
jQuery('#img-name').attr('src', response);
getMyImage();
});
}
Whenever getMyImage successfully returns the image's src value from the webcam, it will immediately go out and try to retrieve a new image, but not before the previous one is loaded.
If I haven't understood what you're trying to do, please let me know. It would be helpful to know more about how the webcam image is retrieved (i.e. is it the same image src returned every time, etc.).
I have seen that I can add errors and render them via <form:errors /> tag and addError() method of BindingResult class, but I wonder if there is something similar to send information messages to the JSP.
I mean, I want to add messages when the operation has been successful. I know I could do it by sending an error, but it would make no sense to me to add errors when there haven't been any error at all.
Why don't you just add the messages as properties of the model that is passed to the view. In your JSP you would check to see if they are not null, and if not, display them.
Interesting. I found this in an old project of mine.:
(this was a base controller, but could well be an utility method)
protected void addMessage(String key, boolean isError,
HttpServletRequest request, Object... args) {
List<Message> msgs = (List<Message>) request.getAttribute(MESSAGES_KEY);
if (msgs == null) {
msgs = new LinkedList<Message>();
}
Message msg = new Message();
msg.setMessage(msg(key, args));
msg.setError(isError);
msgs.add(msg);
request.setAttribute(MESSAGES_KEY, msgs);
}
and then in a messages.jsp which was included in all pages I had:
<c:forEach items="${messages}" var="message">
//display messages here
</c:forEach>
MESSAGES_KEY is a constant of mine with a value "messages" (so that it is later accessible in the forEach loop).
The Message class is a simple POJO with those two properties. I used it for info messages as well as for custom non-validation errors.
This is a rather custom solution, but perhaps I hadn't found a built-in solution then. Google a bit more before using such a solution.
Use something like Flash messages in Rails.
The hard part is to keep the message in a redirect after post. I.e. you submit a form and you redirect to another page that shows a message notifying you that you have succeded in your action.
It is obvious that you can't keep the message in the request because it will be lost after the redirect.
My solution consists in keeping a session scoped bean that contains the message text and its type (notice, warning or error). The first time you read it in a JSP you have to clear it to avoid showing it more than once.
I have an application in which users frequently leave the page running in their browser all day. The page has an intervalrendered that updates data every 30 seconds.
However, if the user doesn't interact with the page for a while, it still expires. I'd like it to auto-extend for as long as the browser is open and making the scheduled requests.
Is there a way to automatically extend the session everytime that the scheduled renderer is fired for one of these pages?
I don't really want to do Javascript hacks to click buttons when my code is already being called every 30 seconds by ICEFaces. The following hacked workaround for ICEFaces internal timer seems to work:
private void updateSessionExpiration () {
HttpSession sess = getSession();
if (sess != null) {
try {
Field fld = SessionDispatcher.class.getDeclaredField("SessionMonitors");
fld.setAccessible(true);
Map map = (Map)fld.get(null);
String sessID = sess.getId();
if (map.containsKey(sessID)) {
log.info("About to touch session...");
SessionDispatcher.Monitor mon =
(SessionDispatcher.Monitor)map.get(sessID);
mon.touchSession();
}
} catch (Exception e) {
log.error("Failed to touch session");
}
}
}
Also, with ICEFaces 1.8.2RC1 (and presumably eventually with the release version of ICEFaces 1.8.2 as well), there are two new workarounds available:
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpSession session = request.getSession();
if (session != null) {
SessionDispatcher.Monitor.lookupSessionMonitor(session).touchSession();
}
Or, put this in the web.xml to update on any hit to URLs within a specific pattern:
<filter>
<filter-name>Touch Session</filter-name>
<filter-class>com.icesoft.faces.webapp.http.servlet.TouchSessionFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>Touch Session</filter-name>
<url-pattern>/seam/remoting/*</url-pattern>
</filter-mapping>
ICEfaces has an internal mechanism for keeping sessions alive (the mechanism is sometimes referred to as "heartbeating"). You may have a look at the belonging documentation and configure the mentioned parameters (especially heartbeatInterval and heartbeatTimeout) in your web.xml.
Iceface's Ajax bridge has callback APIs, for example Ice.onSessionExpired() which you may be able to use.
<body id="document:body">
<script type="text/javascript">
Ice.onSessionExpired('document:body', function() {
<!-- Do something here -->
});
</script>
See http://www.icefaces.org/docs/v1_8_0/htmlguide/devguide/references4.html#1094786 for more details.
If I have undestood what is your real purpose, then you need a sort of timer which automatically extends every user session client side, consequentely overriding the server side default page session expiration timing ( usually 30 minutes, only for detail because I understood that the script "renders" every 30 seconds if the user is active on the page ).
If so well, before JSF I always extended inactive user sessions via underground Javascript, so I'll use it also in your case. Pratically you can build a simple page listener in Javascript starting from:
window.setInterval( expression , msecIntervalTiming );
the "expression" can be a called function in which you invoke some dummy JSF needed to keep the session alive without reloading the current page visited by the user, in the past I used standard frame or iframe to make http calls, now with XHR / Ajax is more simple too.
Javascript example:
var timerID = null;
function simplePageListener() { // invoked by some user event denoting his absence status
// here goes all events and logic you need and know for firing the poller...
if (timerID == null) // avoid duplicates
timerID = window.setInterval( "window.startPoller()", msecIntervalTiming );
}
//...
function pollerStart() {
// make iframe or ajax/xhr requests
}
function pollerStop() {
// make action like page reloading or other needings
}
//...
function triggeredCleanTimer(timer) { // invoked by some user event denoting his active status
clearTimeout(timer); // it can be also the global variable "timerID"
timerID = null;
}
Substantially you use the "timerID" as a global reference to keep track of the listener status, so when you need to activate the "autoextension" you assign it a setInterval. Instead when the user come back to the page (triggered by some event you know), you clear the timeout stopping the listener polling. The above example obviously implies that the user, when comes back, must reload the page manually.
However in your specifical case, I'll avoid to interfere with javascript automatically generated by Icefaces framework. Even if, for ipothesys, you could simulate periodically some user events on invisible input elements (style set on "visibility: hidden", absolutely not on "display: none"), this causes the Icefaces event to not stop it and making itself work continuosly
Elements like <input type="button" name="invisivleButton" value="..." style="visibility: hidden, z-index: 0;" /> on which you can call periodically invoke the click event by
document.forms["YourDummyForm"]["invisivleButton"].click();
For the usage, see the old great JS docs of Devedge Online :-)
http://devedge-temp.mozilla.org/library/manuals/2000/javascript/1.3/reference/window.html#1203669