Another Round of URL Fiddling
Jun 19, 2014, 5:59 PM
I was working on another installment of "Be a Better Programmer", but the result isn't where I want it to be yet, so it'll have to wait. Instead, I'll share a bit about my latest futzing around when it comes to improving the URLs used in an XPages app. This has been a long-running saga, with the latest installment seeing me stumbling across a capability that had been right under my nose: the ViewHandler
's ability to manipulate the URLs generated for a page. That method worked, but had the limitation that it didn't affect navigation between pages.
As it turns out, there's a slightly-better option that covers everything I've tried other than navigation rules specified in faces-config.xml (but who uses those in XPages anyway?): the RequestCustomizerFactory
. This is one of the various classes buried in the Extensibility API and present in the XSP Starter Kit, but otherwise little-used. In the Starter Kit stub (and in a similar commented-out stub in the ExtLib), it can be used to add resources to a page without needing to specify them in code - say, for a plugin to add JS or CSS libraries without being specified in a theme.
However, there are other methods in the RequestParameters
object that would appear to let you shim in all sorts of settings on a per-request basis - different Dojo implementations, different theme IDs, whatever "debugAgent" is, and so forth. The one that interests me at the moment is the UrlProcessor
. This is aptly named: when a URL is generated by the XPage, it is passed through this processor before being sent to the browser. This appears to cover everything other than the aforementioned stock-JSF-style navigation rules: link value properties, form actions, normal navigation rules, view panel generated links, etc..
Following my previous desire, I want to make it so that all links inside my task-tracking app Milo use the "/m/*" substitution rule I set up for the server. The method for doing that via a UrlCustomizer
is pretty similar to the ViewHandler
:
package config; import java.util.regex.Pattern; import javax.faces.context.FacesContext; import com.ibm.xsp.context.RequestCustomizerFactory; import com.ibm.xsp.context.RequestParameters; public class ConfigRequestCustomizerFactory extends RequestCustomizerFactory { @Override public void initializeParameters(final FacesContext facesContext, final RequestParameters parameters) { parameters.setUrlProcessor(ConfigUrlProcessor.instance); } public static class ConfigUrlProcessor implements RequestParameters.UrlProcessor { public static ConfigUrlProcessor instance = new ConfigUrlProcessor(); public String processActionUrl(final String url) { String contextPath = FacesContext.getCurrentInstance().getExternalContext().getRequestContextPath(); String pathPattern = "^" + Pattern.quote(contextPath); return url.replaceAll(pathPattern, "/m"); } public String processGlobalUrl(final String url) { return url; } public String processResourceUrl(final String url) { String contextPath = FacesContext.getCurrentInstance().getExternalContext().getRequestContextPath(); String pathPattern = "^" + Pattern.quote(contextPath); return url.replaceAll(pathPattern, "/m"); } } }
The method of adding this customizer to your app directly is, however, different: instead of being in the faces-config file, this is added as a "service", as mentioned in this handy list. For an in-NSF service, you accomplish this by adding a folder called "META-INF/services" to your app's classpath (you can add it in Code/Java) and a file in there named "com.ibm.xsp.RequestParameters":
In this file, you type the full name of your class, such as "config.ConfigRequestCustomizerFactory" from my example. You'll likely have to Clean your project after doing this to have it take effect (and possibly in between each change to the code).
Now, my current use is very limited; the UrlProcessor
has free reign for its transformations and is passed every URL generated by the XPage (except hard-coded HTML, unsurprisingly) - you could change URLs like "/post.xsp?documentId=foo" to "/blog/posts/foo" without having to code that explicitly in your XPage. You could write a customizer that looks up all Substitution rules for the current Web Site from names.nsf and automatically transforms URLs to match. You could go further than that, trapping external links to process in some way in the "processGlobalUrl" method. The possibilities with the other methods of the customizer go much further (and are largely undocumented, to make them more fun), but for now just fiddling with the URLs is a nice boon.
Harald Reisinger - Aug 2, 2021, 3:39 AM
Hello Jesse,
thank you for this wonderful info, it helped me a lot in my current project to rewrite the uris for the global xsp ressources!
best regards
Harald