Reload jsp page after computations - javascript

So my project demonstrates the results of a k-means clustering algorithm using servlets.
When I cluster for the first time, everything works well and my program relocates the user to the results.jsp page.
But because k-means is a bit random at selecting centroids, I added a re-cluster feature in my code in the results.jsp which re-clusters my data with the settings originally used.
My servlet code for re-clustering looks like this:
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
do stuff...
//reload results page
String htmlResponse = "<html><header><meta http-equiv=\"refresh\" content=\"0; URL='results.jsp\" /></header></html>";
writer.println(htmlResponse);
//but this doesnt work...
}
So after the clustering is done, I want to reload the resutls.jsp page. I tried adding a JavaScript to my jsp page since refresh is a client side operation. But the problem with that approach is that refresh happens before the clustering is fully completed. So I get Exceptions when trying to print the results. Any advice?
My refresh script looks like this:
<script type="text/javascript">
function refresh() {
location.reload(true);
}
</script>
<script type="text/javascript">
function reCluster(cluster_id) {
recluster();
}
</script>
and on the .jsp i'm calling this like this:
<form class="form-horizontal" action="generateArff" method="GET" enctype="multipart/form-data">
<button type="button" class="btn btn-default btn-lg" onclick="reCluster(); refresh();">
<span class="glyphicon glyphicon-refresh"></span> Re-Cluster
</button>
<button type="submit" class="btn btn-info btn-lg">
<span class="glyphicon glyphicon-download"></span> Generate file
</button>
</form>
Can you advice on the matter?

So, #MarcoBolis provided a very good and informative answer, and definitely gets a +1 from me.
However, I think that the most simple solutions are the most effective ones.
As I see your code, I can only assume that you are using an AJAX request to your servlet because I see 1 html form with 2 buttons that do different things. So, what I would do, is go to your ReClusterServlet and add the following:
public class ReClusterServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
PrintWriter writer = response.getWriter();
//Call your Cluster class and methods...
//wait for it...
//return something to your AJAX call.
writer.print("refresh page");
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
doGet(request, response);
}
}
now, go to your AJAX request.
I assume it will be in the recluster() function I see you are calling in the head of your http code.
so it must be something like this:
function recluster() {
$.ajax({
url:'ReClusterServlet',
type:'POST'
});
}
This thing, is calling the servlet but did you know that when your servlet was done with the computation it returned a success message to it??? But you are ignoring it!!!
it's this line writer.print("refresh page");
Now re-write the function as follows:
function recluster() {
$.ajax({
url:'ReClusterServlet',
type:'POST',
success:function() { //handle the successful return by reloading the current page
location.reload();
}
});
}
Again, these are speculations because they seem a bit incomplete, so feel free to correct me if I'm wrong, but I have a strong feeling that you need to study a bit more on the client-server architecture, JQuery and AJAX

I understand you are issuing an AJAX call to the server to start an asynchronous task, performing the clustering.
You could notify the client of task completion by means of a message returned through the XHR:
function reCluster() {
var xhr = new XMLHttpRequest();
xhr.open('GET', 'ReClusterServlet', true);
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
// Refresh if everything went OK
if (xhr.responseText === 'Done') refresh();
}
};
}
The tricky part is handling the notification Java-side.
If you are using Servlet API 3.x+, then you are lucky: just make your request an async one:
public class ReClusterServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// Defer the request to this HTTP request to later
AsyncContext asyncCtx = request.startAsync(request, response);
asyncCtx.setTimeout(0);
// Start the long running computation in another thread,
// passing the AsyncContext
new MyLongRunningTask(asyncCtx).start();
}
}
Then complete it in the other thread:
public class MyLongRunningTask {
private final AsyncContext asyncCtx;
public MyLongRunningTask(AsyncContext asyncCtx) {
this.asyncCtx = asyncCtx;
}
// ...
private void finish() {
// Send the response asynchronously
Writer out = this.asyncCtx.getResponse().getWriter();
out.println("Done");
out.flush();
this.asyncContext.complete();
}
}
If instead you are using an older version of the Servlet API, you will have to make the serving thread (the one on which the doGet method gets called) block until the computation is finished.
If you have a reference to the thread on which the computation is performed available, you can call its join method to await termination:
public class ReClusterServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
Thread async = new MyLongRunningThread();
async.start();
// Await end of computation
try {
async.join();
} catch (InterruptedException e) {
// Computation was interrupted...
}
response.getWriter().println("Done");
}
}
If instead the computation task is queued for execution, for example by an ExecutorService, you have to use wait/notify:
public class ReClusterServlet extends HttpServlet {
private ExecutorService executor;
// ...
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
MyLongRunningRunnable task = new MyLongRunningRunnable();
synchronized (task) {
executor.execute(task);
// Put current thread to sleep until task is no more RUNNING
// (assuming state is a property of MyLongRunningRunnable)
while (task.getState() == State.RUNNING) {
try {
task.wait();
} catch (InterruptedException e) {
// Computation was interrupted...
}
}
}
response.getWriter().println("Done");
}
}
Assuming that the async task calls notify when done:
public class MyLongRunningRunnable implements Runnable {
private State state = State.RUNNING;
// ...
private synchronized void finish() {
this.state = State.DONE;
this.notify();
}
}
NOTE that these two solutions that block the request thread may cause performance issues if you have lots of incoming requests and the clustering job takes very long.

Related

Spring #ExceptionHandler does not render view if webapp is restarted in the background

I'm dealing with a HttpSessionRequiredException and read about (https://spring.io/blog/2013/11/01/exception-handling-in-spring-mvc) how to catch it via the #ExceptionHandling annotation.
This does work since my breakpoint hits the spot. Unfortunately the string containing the view name does not trigger any view resolving and therefore no error page is being rendered.
Steps to Reproduce:
Open http://localhost:8080/manager (Session Attributes are being initialized)
Restart Web Application (Session/SessionAttributes are being reset)
Fill out form and press button (launches POST Request via AJAX - see Edit #2)
ExceptionHandler jumps in. Since a HttpSessionRequiredException is being thrown due the missing session attributes
Check Network Panel in Chrome which yields following information on the failed POST:
Controller Class incl. Exception Handler
#Controller
#Log
#RequestMapping(value = "/manager")
#SessionAttributes({"moduleDescriptors", "Environments", "moduleExecutionData"})
public class ManagerController {
private static final String MANAGER_VIEW = "manager";
private final ManagerHelper managerHelper;
private final ModuleExecution moduleExecution;
private final SystemProperties SystemProperties;
#Autowired
public ManagerController(ManagerHelper managerHelper, ModuleExecution moduleExecution,
SystemProperties SystemProperties) {
this.managerHelper = managerHelper;
this.moduleExecution = moduleExecution;
this.SystemProperties = SystemProperties;
}
#GetMapping
public ModelAndView render() {
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName(MANAGER_VIEW);
modelAndView.addObject("moduleExecutionData", new ModuleExecutionData());
List<ModuleDescriptor> moduleDescriptorsFromServices = managerHelper.getModuleDescriptorsFromServices();
modelAndView.addObject("moduleDescriptors", moduleDescriptorsFromServices);
//TODO: Change varName for Systems
modelAndView.addObject("Environments", SystemProperties.getEnvironments());
log.info("Found " + moduleDescriptorsFromServices.size() + " module descriptors");
return modelAndView;
}
#PostMapping
public String execute(#ModelAttribute ModuleExecutionData moduleExecutionData) {
moduleExecution.execute(moduleExecutionData);
// sessionStatus.setComplete();
return MANAGER_VIEW;
}
#ExceptionHandler(HttpSessionRequiredException.class)
public String sessionError(){
return "sessionError";
}
}
My view lies within all the folder with all working views.
Any idea what I'm doing wrong? I'm configuring spring completely with annotations - not XML.
Edit:
I checked the response and saw that it actually returns the HTML of the View. But this HTML is not rendered. Is it because I made a POST request? Which actually ignores any return HTML?
But that does not make sense - because my original PostHandler does return a view as well and it is rendered.
PostHandler
#PostMapping
public String execute(#ModelAttribute ModuleExecutionData moduleExecutionData) {
moduleExecution.execute(moduleExecutionData);
return MANAGER_VIEW;
}
Edit #2
Here is the POST JavaScript Ajax logic
function postForm(event) {
const moduleId = $(event.currentTarget).data("module-id");
const formData = $("#" + moduleId).serialize();
$.ajax({
type: "post",
data: formData,
url: "/manager",
dataType: "json"
});
}

session variable is not changing via servlet submit

I have a servlet that gets parameters from an HTML dropdown page. On button click the data is sent to the servlet. It works the first time the data is sent, but if I stay on the page and select a different value from the dropdown
and click the submit button, the new data is not set into the session variable.
My servlet is below. Do I need to modify the DoGet method? Again, it works the first time but the session variable doesn't change afterwards.
#WebServlet("/ListStudentServlet")
public class ListStudentServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
public ListStudentServlet() {
super();
// TODO Auto-generated constructor stub
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String sessid = request.getParameter("studentid");
ArrayList<FactStudentDataBean> result = new ArrayList<>();
try ( Connection con = JdbcUtil.getConnection()) {
String sql= "select F.Sessionid "
+ "from FACT_STUDENT F "
+ "where studentid = '"+sessid+"';";
try (Statement st = con.createStatement()) {
ResultSet rs = st.executeQuery(sql);
while (rs.next()){
result.add(new FactStudentDataBean(rs.getString(1)));
}
for (FactStudentDataBean factStudentDataBean : result) {
sessid = factStudentDataBean.getSessid();
}
} catch (Exception e) {
e.printStackTrace();
}
}
catch (Exception e) {
e.printStackTrace();
}
//Trying to set the session variable below, works the first time but anything after doesn't change
HttpSession session = request.getSession(true);
session.setAttribute("sessid", sessid);
}
}
Your code is a little bit "dirty". First of all: why you are writing this sql query like this?:
String sql= "select F.Sessionid "
+ "from FACT_STUDENT F "
+ "where studentid = '"+sessid+"';";
and not like this?:
String sql= "select F.Sessionid from FACT_STUDENT F where studentid = '"+sessid+"';";
Second: Always try to use prepareStatement instead of createStatement (for explanation of what i am telling please see this question:prepareStatement vs executeStatement)
And for the answer now: Ithink you must use session.getAttribute("sessid", sessid);
Check the jsessionid values you are getting on server side. If both are different then try to get session by passing false instead of true.
request.getSession(false);
Also goto tomcat manager application and monitor active sessions.
Hope this will help.

Getting data from Wep Pages using Jsoup Java

Its my first qustion in this site and hope to stay longer :=)
I have read a lot of article and examine many kind of example about taking specific datas from web site using Jsoup. Alread, I could manage to get some values but I couldn't succed my target which is to read alarm states from some web server so that I can collect them and send to technician.
Unfortunatelly, I don't know the hierarchy. If anyone can tell me how to read the value headlined with red squre. I hope I could explain what ı need clearly.
Thanks in advance
public static void main(String[] args) throws IOException {
File htmlFile = new File("http://162.196.43.36");
Document doc = Jsoup.parse(htmlFile, "UTF-8");
// First <div> element has class ="related-container"
Element div = doc.select("td.imgstatus").first();
System.out.println(div);
public static void mainjdk7(String ... args){
Connection connect = Jsoup.connect("http://www.yahoo.com");
try {
Document dom = connect.get();
dom.getElementsByTag("section").forEach(new Consumer<Element>() {
#Override
public void accept(Element element) {
Elements imgstatus = element.getElementsByClass("imgstatus");
if(null != imgstatus){
//Do something
}
}
});
} catch (IOException e) {
e.printStackTrace();
}
}
public static void mainjdk8(String ... args){
Connection connect = Jsoup.connect("http://www.yahoo.com");
try {
Document dom = connect.get();
dom.getElementsByTag("section").forEach(element -> {
Elements imgstatus = element.getElementsByClass("imgstatus");
if(null != imgstatus){
//Do something
}
});
} catch (IOException e) {
e.printStackTrace();
}
}
hope this works for you.... Happy Coding :)

Wicket AbstractDefaultAjaxBehavior do recursive update the page

I have some ajax Behaviour that should pick some data using JS, and turn it back to Java. Sometimes it works but quite ofen it is just add url parameter and do page refresing/
public abstract class LoggedVKIdBehaviour extends AbstractDefaultAjaxBehavior {
private static final Logger logger = LoggerFactory.getLogger(LoggedVKIdBehaviour.class);
#Override
protected void respond(AjaxRequestTarget target) {
String loggedVkId = RequestCycle.get().getRequest().getRequestParameters().getParameterValue("logged_vkid").toString();
logger.info("ajax has comming with logged VK ID " + loggedVkId);
recived(target, loggedVkId);
}
protected abstract void recived(AjaxRequestTarget target, String loggedVkId);
#Override
public void renderHead(final Component component, IHeaderResponse response) {
super.renderHead(component, response);
Map<String, Object> map = new HashMap<>();
map.put("callbackFunction", getCallbackFunction(CallbackParameter.explicit("logged_vkid")));
//
PackageTextTemplate ptt = new PackageTextTemplate(LoggedVKIdBehaviour.class, "vkid_callback.js");
OnDomReadyHeaderItem onDomReadyHeaderItem = OnDomReadyHeaderItem.forScript(ptt.asString(map));
response.render(onDomReadyHeaderItem);
}
}
js template
var calback = ${callbackFunction};
var logged_vk_id = 11;
function authInfo(response) {
if (response.session) {
logged_vk_id = response.session.mid;
calback(response.session.mid);
console.log("recived callback from VK " + logged_vk_id);
}
}
$(document).ready(function () {
VK.Auth.getLoginStatus(authInfo);
});
it is do recursive redirection like http://localhost:8080/mytool/product/1?logged_vkid=332797331&logged_vkid=332797331&logged_vkid=332797331&logged_vkid=332797331&logged_vkid=332773...
As i understand Ajaj technology - iti asynchronus requests, that shouldn't touch main url at all. So what is the reason for page refreshing?
this is generated Callback function
function (logged_vkid) {
var attrs = {"u":"../wicket/bookmarkable/com.tac.kulik.pages.product.ProductPage?12-1.IBehaviorListener.0-&productID=1"};
var params = [{"name":"logged_vkid","value":logged_vkid}];
attrs.ep = params.concat(attrs.ep || []);
Wicket.Ajax.ajax(attrs);
}
I use wicket 7.2
I did a lot investigations for few days. And found that when i remove
setPageManagerProvider(new NoSerializationPageManagerProvider(this));
Application throw me exepton in polite logs
org.apache.wicket.WicketRuntimeException: A problem occurred while
trying to collect debug information about not serializable object look
like it is could come from aused by: java.io.NotSerializableException:
com.tac.kulik.panel.smaccounts.SMAccountsPanel$1
which means that page tryed to be serialized for SOME REASON but $1 it is mean Anonimous class. I had few class created anonimously to ges some ajax links coming from ListView to be managed on parent panel. So After removing this Anonimous class logic, everything start and run well.
So i am happy, but still don't understand which reason page did serialization after ajax, and what the reason was to refresh whole page.

U2F JavaScript for Client Side in GWT (JSNI)

I'm trying to get response from U2F Token in GWT project using this source code:
public class Test implements EntryPoint {
#Override
public void onModuleLoad() {
Window.alert("Alert 3:"+u2FTest());
}
public static native String u2FTest()/*-{
var respond = {rep: "Clear"};
var RegistrationData = {"challenge":"dG7vN-E440ZnJaKQ7Ynq8AemLHziJfKrBpIBi5OET_0",
"appId":"https://localhost:8443",
"version":"U2F_V2"};
$wnd.u2f.register([RegistrationData], [],
function(data) {if(data.errorCode) {
alert("U2F failed with error: " + data.errorCode);
return;
}
respond.rep=JSON.stringify(data);
alert("Alert 1: "+respond.rep);
});
alert("Alert 2: "+respond.rep);
return respond.rep;
}-*/;
}
for some reasons I get The Alerts like so:
(Alert 2) first with "Clear" result
(Alert 3) with "Clear"
(Alert 1) with Token response
Normally I've to get (Alert 1) with Token response then 2,3. So how can I stop execution until I'll get the token response
Thank you,
Embrace asynchronicity!
public static native void u2FTest(com.google.gwt.core.client.Callback<String, Integer> callback) /*-{
// …
$wnd.u2f.register(regReqs, signReqs, $entry(function(response) {
if (response.errorCode) {
callback.#com.google.gwt.core.client.Callback::onFailure(*)(#java.lang.Integer::valueOf(I)(response.errorCode));
} else {
callback.#com.google.gwt.core.client.Callback::onSuccess(*)(JSON.stringify(response));
}
}));
}*-/;
(don't forget to wrap callbacks in $entry() so that exceptions are routed to the GWT.UnhandledExceptionHandler, if there's one)

Categories