I recently came again over a developer behaviour, which is quite common: Check how a certain action can be performed, and then reproduce it in code.
For example: How do I start a certain workflow in CQ5 via code? The easiest way to find this out is to perform this action within the UI and then replay the request. That means sending a POST request to localhost:4502/etc/workflows/instances with a number of POST parameters. A developer might then start thinking about using httpclient to send a HTTP request to CQ5 itself to start a certain workflow. But because this request needs authentication, the developer requires some user with enough permissions to start the workflow, in the worst case “admin” is hardcoded as password 😦
This approach has several severe drawbacks:
- The port might not be 4502 …
- A password must be provided, because on authoring all ressources are protected by ACLs.
- You need to parse the returncode to check if something went wrong.
In the end that’s pretty much work to do. Just because the developer wasn’t aware of the WorkflowService. Same applies to many other features of the UI.
So one thing is quite important to know: Every feature of the UI can be accessed also by other means, without remote control over HTTP. In most (all?) cases there are services available, which allow direct access to that functionality via a regular Java API. So if you are a developer looking to access some piece of functionality, lookup the API docs and the Felix console. You’ll certainly find that feature, especially if it’s that prominent as the WorkflowService. If you are an admin, check your installation and deployment guidelines and make sure, that none of the 3 items above are required.
But … there is one (and only one) valid requirement to send a request from CQ5 to itself: If you want to get hold of a rendered resource. For example you need to build a ZIP archive out of pages beneath a page. For this you need these pages rendered. One way would be to reconstruct the way sling request processing works. The other and easiest way is to just do a HTTP request and do everything like it is supposed to work. But then you need to provide username/password and the port… But since CQ 5.4 there is a solution for this problem. You can use the SlingRequestProcessor service to inject a HttpServletRequest and HttpServletResponse object, plus a ResourceResolver (which is already authenticated!). You need to provide your own mock implementations of these, plus a method, which returns the body of the response.
5 thoughts on “CQ5 requesting itself”
Using Chrome Developer Tools (or Firebug) to figure out which servlet invokes an action is a big part of how I learned to develop on CQ beyond building “text and image” components. However, there is limited visibility into what services are available, and I really wish Adobe would remedy that. Even the API docs are mostly cryptic and difficult to digest without a very specific destination in mind.
I would suggest taking the request inspection a step further. Use a Java decompiler to find inspect the Java for the servlet. That way you can see what code the platform uses and develop using the same patterns that Adobe/Day uses.
Excellent article. It addresses a perspective that I’ve not seen covered thoroughly.
Ryan, thanks for your comment. I agree with you, that it’s sometimes hard to know if there’s a service for this specific case and how it is called, so you can look it up. But I don’t know if better API docs (well, lot of space for improvement here) helps here that much.
For the moment I hope that the CQ5 Advanced Developer training can help. And of course digging through the bundles and looking up the services they provide and then reading the API docs.
In case you need to get hold of rendered resources the RetrieverService (http://dev.day.com/docs/en/cq/5-4/javadoc/com/day/cq/retriever/RetrieverService.html) will come in handy.
Interesting. How does this differ from ResourceResolver?
For mocking the request or the response, one can use the RequestResponseFactory (com.day.cq.contentsync.handler.util). That will provide nice methods to create an artificial request based on method and URI (including selectors) and an artificial response, writing its body to a provided OutputStream (http://dev.day.com/docs/en/cq/current/javadoc/index.html?com/day/cq/contentsync/handler/util/RequestResponseFactory.html).
Comments are closed.