I am trying to update a document using rpc service.
I have an xpage with a button. I want to click that button and by using CS javascript to setInterval and update a document field every 5 seconds.
I used the remote service control and the code is this:
<xe:jsonRpcService id="jsonRpcService1" serviceName="heartBeat">
<xe:this.methods>
<xe:remoteMethod name="send">
<xe:this.script><![CDATA[var db = session.getCurrentDatabase();
var heartView = db.getView("Heartbeats");
var doc:NotesDocument = heartView.getFirstDocument();
var vl = doc.getItemValueString();
if(vl==""){
doc.replaceItemValue("dummyH","z");
}else{
doc.replaceItemValue("dummyH",vl+"z");
}
doc.computeWithForm(false, false);
doc.save();]]></xe:this.script>
</xe:remoteMethod>
</xe:this.methods></xe:jsonRpcService>
The code of the button is this:
<xp:button value="Label" id="button1">
<xp:eventHandler event="onclick" submit="false">
<xp:this.script><![CDATA[setInterval(function(){heartBeat.send()},3000);]]></xp:this.script>
</xp:eventHandler></xp:button>
It doesnt work. I get no error but i also get no updates in the document... What is wrong in the code?
UPDATE
if i replace rpc script with a simple print("aaaaa") it works perfectly (i also followed the suggestions of Tim in the answer below, for my client side code).
when i use again my previous code i get the following javascript error:
Unable to load /Databases/Test/Mike/my1.nsf/RPCpage.xsp/mm?$$viewid=!dqducrahog! status:400
how can i access the database and documents from a remote service?
The syntax of RPC calls is slightly unusual; your call to heartbeat.send(), for example, does not actually call the remote send method. Rather, it returns a handle on a remote method descriptor... to actually call the remote method, you need to add a callback:
var remoteMethod = heartbeat.send();
remoteMethod.addCallback(function(response){
// handle whatever came back from the server
});
If, for instance, you designed your remote method to return whether or not the heartbeat was successful, a more complete example would look like this:
var heartbeatInterval = 3000;
var periodicHeartbeat = function() {
var remoteMethod = heartbeat.send();
remoteMethod.addCallback(function(response){
if (response.stillAlive) {
setTimeout(periodicHeartbeat, heartbeatInterval);
}
});
}
setTimeout(periodicHeartbeat, heartbeatInterval);
It's generally recommended to use setTimeout to initially schedule a deferred function, and then call setTimeout again from within that deferred function to schedule its own next execution, instead of using setInterval.
In this use case, however, this is particularly true because each remote method call is asynchronous, so with setInterval, you would eventually end up with request overlap, because each call requires at least a few milliseconds to complete.
Additionally, the preferred pattern of scheduling the next execution from inside the current allows you to check what the server sent back to determine whether to even bother continuing to send subsequent requests. Right now you're not returning any value from your remote method; if you change just the last line to:
return { stillAlive: doc.save() };
...then that JSON object will be automatically passed to your callback function as its first argument. Taking another look at one line from the prior example:
if (response.stillAlive) {
...so the RPC call only reschedules itself if the prior call succeeded. This probably isn't the precise behavior you want, but given that you can return data from the remote method, and the data that was returned is passed to your callback function, you can design a call and response approach that best fits your business specifications for this functionality.
Having said all that, you may want to take a look at the keepAlive component from the XPages Extension Library... it doesn't store heartbeat data in Domino documents, but if all you're trying to do is prevent idle sessions from expiring, it's a much simpler solution.
Related
I've been using selenium (with python bindings and through protractor mostly) for a rather long time and every time I needed to execute a javascript code, I've used execute_script() method. For example, for scrolling the page (python):
driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
Or, for infinite scrolling inside an another element (protractor):
var div = element(by.css('div.table-scroll'));
var lastRow = element(by.css('table#myid tr:last-of-type'));
browser.executeScript("return arguments[0].offsetTop;", lastRow.getWebElement()).then(function (offset) {
browser.executeScript('arguments[0].scrollTop = arguments[1];', div.getWebElement(), offset).then(function() {
// assertions
});
});
Or, for getting a dictionary of all element attributes (python):
driver.execute_script('var items = {}; for (index = 0; index < arguments[0].attributes.length; ++index) { items[arguments[0].attributes[index].name] = arguments[0].attributes[index].value }; return items;', element)
But, WebDriver API also has execute_async_script() which I haven't personally used.
What use cases does it cover? When should I use execute_async_script() instead of the regular execute_script()?
The question is selenium-specific, but language-agnostic.
When should I use execute_async_script() instead of the regular execute_script()?
When it comes to checking conditions on the browser side, all checks you can perform with execute_async_script can be performed with execute_script. Even if what you are checking is asynchronous. I know because once upon a time there was a bug with execute_async_script that made my tests fail if the script returned results too quickly. As far as I can tell, the bug is gone now so I've been using execute_async_script but for months beforehand, I used execute_script for tasks where execute_async_script would have been more natural. For instance, performing a check that requires loading a module with RequireJS to perform the check:
driver.execute_script("""
// Reset in case it's been used already.
window.__selenium_test_check = undefined;
require(["foo"], function (foo) {
window.__selenium_test_check = foo.computeSomething();
});
""")
result = driver.wait(lambda driver:
driver.execute_script("return window.__selenium_test_check;"))
The require call is asynchronous. The problem with this though, besides leaking a variable into the global space, is that it multiplies the network requests. Each execute_script call is a network request. The wait method works by polling: it runs the test until the returned value is true. This means one network request per check that wait performs (in the code above).
When you test locally it is not a big deal. If you have to go through the network because you are having the browsers provisioned by a service like Sauce Labs (which I use, so I'm talking from experience), each network request slows down your test suite. So using execute_async_script not only allows writing a test that looks more natural (call a callback, as we normally do with asynchronous code, rather than leak into the global space) but it also helps the performance of your tests.
result = driver.execute_async_script("""
var done = arguments[0];
require(["foo"], function (foo) {
done(foo.computeSomething());
});
""")
The way I see it now is that if a test is going to hook into asynchronous code on the browser side to get a result, I use execute_async_script. If it is going to do something for which there is no asynchronous method available, I use execute_script.
Here's the reference to the two APIs (well it's Javadoc, but the functions are the same), and here's an excerpt from it that highlights the difference
[executeAsyncScript] Execute an asynchronous piece of JavaScript in
the context of the currently selected frame or window. Unlike
executing synchronous JavaScript, scripts executed with this method
must explicitly signal they are finished by invoking the provided
callback. This callback is always injected into the executed function
as the last argument.
Basically, execSync blocks further actions being performed by the selenium browser, while execAsync does not block and calls on a callback when it's done.
Since you've worked with protractor, I'll use that as example.
Protractor uses executeAsyncScript in both get and waitForAngular
In waitForAngular, protractor needs to wait until angular announces that all events settled. You can't use executeScript because that needs to return a value at the end (although I guess you can implement a busy loop that polls angular constantly until it's done). The way it works is that protractor provides a callback, which Angular calls once all events settled, and that requires executeAsyncScript. Code here
In get, protractor needs to poll the page until the global window.angular is set by Angular. One way to do it is driver.wait(function() {driver.executeScript('return window.angular')}, 5000), but that way protractor would pound at the browser every few ms. Instead, we do this (simplified):
functions.testForAngular = function(attempts, callback) {
var check = function(n) {
if (window.angular) {
callback('good');
} else if (n < 1) {
callback('timedout');
} else {
setTimeout(function() {check(n - 1);}, 1000);
}
};
check(attempts);
};
Again, that requires executeAsyncScript because we don't have a return value immediately. Code here
All in all, use executeAsyncScript when you care about a return value in a calling script, but that return value won't be available immediately. This is especially necessary if you can't poll for the result, but must get the result using a callback or promise (which you must translate to callback yourself).
I am having some trouble with a bit of code. I have a function that does some stuff to some data, calls a remote system (activating a script on that system and passing in the data), and then makes another call to the same system to activate a different script (which acts on the data saved above). The problem is that the 1st call to the remote system appears to get lost in the execution.
This is being run in Safari, uses jquery; the function is tied to a button click, which is defined in the javascript code with an onclick function (i.e. it is not defined in the html button definition).
Here's a rough breakdown of the function (cleaned out for viewing purposes - I hope I left enough to make it clear):
function compareJSON() {
// loop through the objects, testing and changing data
// ...
dataSession=({ //build object for output });
$.each( dataSession.chapters , function( indexC, value ) {
//compare objects to some others, testing and changing data
});
// ...
//Call remote script on other system
urlString="url://blah.dee.com/Blar?script=SaveJSON&$JSONobject=";
window.location= urlString + JSON.stringify(dataSession);
//Call remote script on other system
window.location="url://blah.dee.com/Blar?script=EditJSON";
}
The last three lines of code are the two calls. It uses the window.location to actually trigger the remote system, passing the data through the URL. But I need BOTH scripts to get called and run. It appears that only the LAST script in the sequence ever gets run. If I switch them around it remains whatever is in last place.
Is there something about the window.location that doesn't actually process until the end of the function?
This script actually used to be a series of separate function calls, but I figured I was running into asynchronous execution that was causing the various script calls to not register. But once I put the code into this single function, it was still happening.
Any clues would be helpful.
Thanks,
J
Modifing the value of window.location is reserved exclusively for instances in which you'd like to cause a browser redirect.
It looks like you want to trigger a page request instead. You say you already have jQuery loaded, if so, you can trigger such a request using jQuery.get or a similar function.
For example:
// Loads the myscript.php page in the background
$.get('myscript.php');
// You can also pass data (in the form of an object as the second argument)
$.get('myscript.php', { name: "John", time: "2pm" });
Based on chrome developer tools a breakpoints I think I'm dealing with a scope issue I can figure out. Is it the way I define the function? The script below is an include js file and the array ' timeStamp I want available for use in other functions without having to call my loadData function everytime.
The timeStamp array goes undefined once it leaves the for loop before it even leaves the function.
var timeStamp = []; // Want this array to be global
function loadData (url){
$.getJSON(url, function(json) {
for (var i=0;i<json.length;i++){
timeStamp.push(json[i].TimeStamp);
}
console.log(inputBITS); //returns the value
});
console.log(inputBITS); //undefined
}
Thank you for anyhelp
It looks like the issue is that getJSON is asynchronous. When it executes and finishes and your code continues on, it indicates only the START of the networking operation to retrieve the data. The actual networking operation does not complete until some time later.
When it does complete, the success handler is called (as specified as the second argument to your getJSON() call) and you populate the timeStamp array. ONLY after that success handler has been called is the timeStamp array valid.
As such, you cannot use the timeStamp array in code that immediately follows the getJSON() call (it hasn't been filled in yet). If other code needs the timeStamp array, you should call that code from the success handler or use some other timing mechanism to make sure that the code that uses the timeStamp array doesn't try to use it until AFTER the success handler has been called and the timeStamp array has been populated.
It is possible to make some Ajax calls be synchronous instead of asynchronous, but that is generally a very bad idea because it locks up the browser during the entire networking operation which is very unfriendly to the viewer. It is much better to fix the coding logic to work with asynchronous networking.
A typical design pattern for an ajax call like this is as follows:
function loadData (url){
$.getJSON(url, function(json) {
// this will execute AFTER the ajax networking finishes
var timeStamp = [];
for (var i=0;i<json.length;i++) {
timeStamp.push(json[i].TimeStamp);
}
console.log(timeStamp);
// now call other functions that need timeStamp data
myOtherFunc(timeStamp);
});
// this will execute when the ajax networking has just been started
//
// timeStamp data is NOT valid here because
// the ajax call has not yet completed
// You can only use the ajax data inside the success handler function
// or in any functions that you call from there
}
And here's another person who doesn't understand basic AJAX...
getJSON is asynchronous. Meaning, code keeps running after the function call and before the successful return of the JSON request.
You can "fix" this by forcing the request to be synchronous with an appropriate flag, but that's a really bad idea for many reasons (the least of which is that you're violating the basic idea of AJAX). The best way is to remember how AJAX works and instead put all your code that should be executed when the AJAX returns, in the right place.
I have an app that loads several resources when it's first run, which are stored in localStorage. I have a function that checks whether all the local storage variables are set, so that part is working okay.
My method of working is like this:
Display a loading message.
Initialize the AJAX requests.
Start a timer interval to check if everything has loaded.
When the data has loaded, initialize the application etc.
If the data did not load, display an error message.
The problem is with #5 - how to detect if there was an error? For example if there was a connection problem or the sever sent back invalid data for whatever reason. Here is my current code - downloadData just performs a basic AJAX request:
// check local storage and download if any missing
if ( !checkLocalStorage() )
{
$('#content').before( '<div class="notice" id="downloading">Downloading data, please wait...</div>' );
for ( var i in db_tables )
{
if ( localStorage[db_tables[i]] == null )
downloadData( db_tables[i] );
}
}
// check progress
var timer = setInterval( function() {
if ( checkLocalStorage() )
{
// everything is downloaded
$('#downloading').hide();
clearInterval(timer);
initApp();
}
}, 500 );
Could you turn it around a bit? Something like this (with sensible variable names and a "real" API) would simplify things:
Display a loading message.
Instantiate an application initializer, ai.
Crank up the AJAX requests:
Success handlers call ai.finished(task).
Error handlers call ai.error(task).
Register with the initializer, ai.register(task), in case a "you're taking too long" check is desired.
Once all the AJAX requests have called ai.finished, initialize the application etc.
If any of the AJAX tasks called ai.error, then display an error message and start cleaning things up.
This way you wouldn't need to setInterval() and the individual AJAX tasks will tell you when they have finished or fallen over. You might still want the interval to deal with tasks that are taking too long but most of the logic would be notification based rather than polling based.
Seeing your actual ajax calls in downloadData would help, but I suggest you look over the jquery AJAX API again. Ajax calls have callbacks not just for overall completion but specifically for success and failure including errors. Try to do something like retrying if there is an error and if it continues to fail you can warn the user. You can also use these callbacks to notify your application when the loading is done instead of using an interval timer.
A little (!) bit of background before I can get to the question :
I am implementing a web based search solution. Technology used: javascript (jquery), .net, html etc. etc.
All my web service calls are done through javascript (cross domain ws call). I have few sequential web service calls which all have different success callback function.
I am not able to digest - when i call those ws individually in seperate places they are returning me proper results but sequentially sometime they are giving and sometime not.
sample code: this is not giving expected results all the time.
function submitSearchRequest(_queryString, Stores) {
if (Stores[1].length>0) {
//generate 'searchRequestForArtifact' request object
getSearchResponse("successcallForArtifact", _searchRequestForArtifact);
}
if (Stores[2].length > 0) {
//generate 'searchRequestForPerson' request object
getSearchResponse("successcallForPerson", _searchRequestForPerson);
}
}
function successcallForArtifact(response)
{
//show the results
}
function successcallForPerson(response)
{
//show the results
}
}
If you need sequentially you will need to kick off each search only after one has returned. Currently you are making async calls, meaning it gets kicked off then continues with the code. Currently if the second call is simply faster the order will be off. You will either need to make a sync call or simply have the order enforced by calling the second search from the success function for the artifact.
If you are using JQuery which it seems you are you can set the async parameter to false which will force the order you want but it will slow the overall performance of your page. See this question.