To execute an event on the server, you normally have to send a POST request, because actions will be executed in the Invoke Application phase of the JSF lifecycle. A GET request will only process the Restore View and the Render Response phase, that why you can not execute an event with a GET request.
But with the help of a PhaseListener, the execution can be done earlier in the Restore View phase:
package ch.hasselba.xpages.util;
import javax.faces.event.PhaseEvent;
import javax.faces.event.PhaseId;
import javax.faces.event.PhaseListener;
import com.ibm.xsp.component.xp.XspEventHandler;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import com.ibm.xsp.util.FacesUtil;
public class ExecuteOnServerPhaseListener implements PhaseListener {
private static final long serialVersionUID = 1L;
public void beforePhase(PhaseEvent event) {}
public PhaseId getPhaseId() {
return PhaseId.RESTORE_VIEW;
}
public void afterPhase(PhaseEvent event) {
FacesContextExImpl = FacesContextExImpl.getCurrentInstance();
ExternalContext ec = fc.getExternalContext();
String url = ec.getRequestPathInfo();
String[] pathes = url.split( "/" );
try{
if( pathes.length > 2 ){
if( "executeOnServer".equals( pathes[pathes.length -2 ] ) ){
String[] fullId = pathes[ pathes.length - 1 ].split(":");
String actionId = fullId[ fullId.length - 1 ];
XspEventHandler eventHandler = (XspEventHandler)
FacesUtil.findComponentWithFullId( fc.getViewRoot(), actionId );
if( eventHandler != null ){
eventHandler.getAction().invoke( fc, null );
fc.responseComplete();
}
}
}
}catch(Exception e){
e.printStackTrace();
}
}
}
To activate the PhaseListener, it has to be enabled in the faces-config.xml:
<?xml version="1.0" encoding="UTF-8"?>
<faces-config>
<lifecycle>
<phase-listener>ch.hasselba.xpages.util.ExecuteOnServerPhaseListener</phase-listener>
</lifecycle>
</faces-config>
The following Javascript snippet extends the XSP object and adds the new function executeOnServerGet to it. The parameter is the if of the event to invoke.
XSP.executeOnServerGet = function( eventId ){
var viewId = dojo.query('[name="$$viewid"]')[0].value;
var url = document.forms[0].action;
url += "/executeOnServer/" + eventId;
url += "?$$viewid=" + viewId;
url += "&$$ajaxid=@none";
dojo.xhrGet({url: url});
}
When calling the function, it sends a GET request and adds the current view id to the request. With the parameter $$ajaxId set to @none, the XPages Engine is forced to send no HTML code back to the client.
And here is an example XPage:
<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core">
<xp:eventHandler id="helloworld" event="helloworld" submit="false">
<xp:this.action>
<![CDATA[#{javascript:
print("hello world " + java.lang.System.currentTimeMillis() );
}]]>
</xp:this.action>
</xp:eventHandler>
<xp:scriptBlock id="scriptBlock1">
<xp:this.value><![CDATA[
dojo.addOnLoad( function(){
XSP.executeOnServerGet = function( eventId ){
var viewId = dojo.query('[name="$$viewid"]')[0].value;
var url = document.forms[0].action;
url += "/executeOnServer/" + eventId;
url += "?$$viewid=" + viewId;
url += "&$$ajaxid=@none";
dojo.xhrGet({url: url});
}
});
]]></xp:this.value>
</xp:scriptBlock>
<xp:button value="Execute" id="button1">
<xp:eventHandler event="onclick" submit="false">
<xp:this.script>
<![CDATA[XSP.executeOnServerGet( "#{id:helloworld}" );]]>
</xp:this.script>
</xp:eventHandler>
</xp:button>
</xp:view>
When clicking the button, the following URL is opened in the background:
http://example.com/db.nsf/EventGet.xsp/executeOnServer/view:_id1:helloworld?$$viewid=!dwjldz64w0!&$$ajaxid=@none
A GET request was sent to the server:If you look on the server console, you will see that the Action was invoked:
Nice deep dive into the JSF lifecyle. However buyers beware: the nature of a GET request should be idempotent: the same 2 calls should yield the same result (short of timer ticks eventually). So don’t do a GET that alters data on the server.
A really nice „hack“ …erm… „extension“ 😉
Thinking about a use case for that. If I find one I’ll remember to come back and steal your code 😀
Cheers!