Expand my Community achievements bar.

Guidelines for the Responsible Use of Generative AI in the Experience Cloud Community.
SOLVED

Rendering out a page from an OSGI Bundle

Avatar

Level 1

Hi,

I've run into an issue while trying to create a custom OSGI bundle that solves the following problem:

We have a list of several thousand keys in a CSV file that need to point to specific pages, and then render the content of that page.

Conceptually, I believe I understand how I'm supposed to do it:

  1. The OSGI bundle runs in the background, available for a *.JSP to reference.
  2. The bundle loads the CSV*, parsing the key->value from the JSP that was referenced. (eg. 54, /path/to/the/requested/resource)
  3. The bundle then renders the resource out as part of the request.

* I know the second step needs refinement (it should be loaded after the server loads, and remain in memory until it's shut down; we don't want to thrash the HDD and memory is much faster but for now this is mainly for a proof of concept.)

I should add that I'm pretty lousy when it comes to Java programming but using various tutorials I've managed to return a string so far. From that I've then tried to resolve the resource inside of the Implementation class for the bundle.

Code as follows (var names changed):

ExampleService.java:

package com.adobe.cq; public interface ExampleService { public void csvLocation(String val); public void setKey(String val); public void main(); }

ExampleServiceImpl.java:

package com.adobe.cq; import java.io.BufferedReader; import java.io.FileReader; import java.io.IOException; // Required Felix files import org.apache.felix.scr.annotations.Component; import org.apache.felix.scr.annotations.Reference; import org.apache.felix.scr.annotations.Service; // Required Sling Files import org.apache.sling.api.resource.ResourceResolver; import org.apache.sling.api.resource.ResourceResolverFactory; import org.apache.sling.api.resource.LoginException; // Required CQ Files import com.day.cq.wcm.api.Page; import com.day.cq.wcm.api.PageManager; // @Component // @Service public class ExampleServiceImpl implements ExampleService { private String csvLocation = "C:\\AdobeCQ\\LargeEntry.csv"; private String key = "0"; private ResourceResolverFactory resolverFactory; public void setKey(String val) { this.key = val; } public void csvLocation(String val) { this.csvLocation = val; } public void main() { BufferedReader br = null; String pageUrl = null; // // This part loads the CSV file // try { String sCurrentLine; br = new BufferedReader(new FileReader(csvLocation)); while ((sCurrentLine = br.readLine()) != null) { String[] split = sCurrentLine.split(","); if (split[0].equals(key)) { pageUrl = (split[1]).toString(); } } } catch (IOException e) { e.printStackTrace(); } finally { try { if (br != null) { br.close(); } } catch (IOException ex) { ex.printStackTrace(); } } // // Page load request // try { ResourceResolver resourceResolver = resolverFactory.getAdministrativeResourceResolver(null); PageManager pageManager = resourceResolver.adaptTo(PageManager.class); // perform actions here Page page = pageManager.getPage(pageUrl); // Right here: // This is where the issue exists-- // what would you do here to render the // contents of a retrieved page out to // the browser? resourceResolver.close(); } catch (LoginException e) { e.printStackTrace(); } } }

Please, please help me here. I've been tearing my hair out trying to figure out how to output a rendered page after retrieving a resource. What am I missing? What am I doing wrong here?

Thanks for any assistance.

1 Accepted Solution

Avatar

Correct answer by
Level 10

Instead of returning the values in a String - why not JSON or XML:

ie - XML

<keyvalues>

<keyvalue>

  <key>keyname1</key.>

   <value>keyval</value>

 </keyvalue>

<keyvalue>

  <key>keyname2</key.>

   <value>keyval</value>

 </keyvalue>

</keyvalues>

This would make it much easier to pass back from the OSGi service and then also much easier to parse on front end component using JQuery.

View solution in original post

8 Replies

Avatar

Level 10

I believe, the pages are already created with templates. You can have a placeholder in the content with a component to hold these key value pair.

with in the JSP, you can call your service class which returns the value or the content required for the component in the JSP which takes care of rendering it.

Avatar

Correct answer by
Level 10

Instead of returning the values in a String - why not JSON or XML:

ie - XML

<keyvalues>

<keyvalue>

  <key>keyname1</key.>

   <value>keyval</value>

 </keyvalue>

<keyvalue>

  <key>keyname2</key.>

   <value>keyval</value>

 </keyvalue>

</keyvalues>

This would make it much easier to pass back from the OSGi service and then also much easier to parse on front end component using JQuery.

Avatar

Level 10

Also - to learn how to dynamically create XML and return it as part of an AEM custom service and how to parse it on the front end - see this AEM community article:

https://helpx.adobe.com/experience-manager/using/querying-experience-manager-data-using1.html

Avatar

Level 10

Likewise - here is an article that shows how to return JSON from the AEM service and parse it on the front end:

https://helpx.adobe.com/experience-manager/using/restful-services.html

That is one of the powerful things about AEM - building back end services that return data to meet your needs (ie - xml/json) and front end components that can parse and use the data. 

Avatar

Employee

I think you are mixing the 'Page' and output here.

If you want to spool content to the response, best way is to create a servlet and then push the content with response.getWriter().

The Page-api points to an existing 'content-page' already in CRX.

If you want you can create new pages via the PageManager-api.

BTW: In you sample code you should always close the resourceResolver in the finally block, and even more try to avoid the use of admin-sessions.

Avatar

Level 1

By the way Simon, are you the same gentleman who runs the Phonegap blog? If so, it was terrific stuff-- helped me out with a lot of smaller apps that I was making a while back. Thanks. I'd like to add at this point that I could implement the XML and JSON services, but the amount of time and effort involved would simply not be worth it (at this point in time.) :)

So I've been trying all day to get this thing to return even basic stuff, like a page title. All I keep on getting are Server 500 errors and I'm at my wit's end now.

Here's the stack trace:

09.03.2015 17:04:58.740 *ERROR* [0:0:0:0:0:0:0:1 [1425884698419] GET /content/KeyTemplate.html HTTP/1.1] org.apache.sling.engine.impl.SlingRequestProcessorImpl service: Uncaught SlingException java.lang.NullPointerException: null at com.adobe.cq.DoubleclickServiceImpl.getTitle(ExampleServiceImpl.java:95)

Here's an example of the key-value pair that I'm using:

0,content/geometrixx/en/services
1,content/geometrixx
2.content/geometrixx/en

 

... and I've got some updated code here. Have I missed anything here? I simply can't get this thing working, no matter what I do. I've tried the documentation on the Sling site (http://sling.apache.org/documentation/tutorials-how-tos/getting-resources-and-properties-in-sling.ht...) as well, and I'm still getting no joy.

package com.adobe.cq; public interface ExampleService { public void csvLocation(String val); public void setKey(String val); public void main(); public String getString(); public String getTitle(); }

... and the Implementation:

package com.adobe.cq; import java.io.BufferedReader; import java.io.FileReader; import java.io.IOException; // Required Felix files import org.apache.felix.scr.annotations.Service; import org.apache.felix.scr.annotations.Component; // Required Sling files import org.apache.sling.api.resource.LoginException; import org.apache.sling.api.resource.Resource; import org.apache.sling.api.resource.ResourceResolver; import org.apache.sling.api.resource.ResourceResolverFactory; // Required CQ Files import com.day.cq.wcm.api.Page; // @Component @Service public class ExampleServiceImpl implements ExampleService { private ResourceResolverFactory resolverFactory; private LoginException loginException; private String csvLocation = "C:\\AdobeCQ\\LargeEntry.csv"; private String key = "0"; private String pageUrl = null; public void setKey(String val) { this.key = val; } public void csvLocation(String val) { this.csvLocation = val; } public String getString() { return this.pageUrl; } private void getCSV() { BufferedReader br = null; // // This part loads the CSV file // try { String sCurrentLine; br = new BufferedReader(new FileReader(csvLocation)); while ((sCurrentLine = br.readLine()) != null) { String[] split = sCurrentLine.split(","); if (split[0].equals(key)) { pageUrl = (split[1]).toString(); } } } catch (IOException e) { e.printStackTrace(); } finally { try { if (br != null) { br.close(); } } catch (IOException ex) { ex.printStackTrace(); } } } public void main() { this.getCSV(); } public String getTitle() { String x = null; try { ResourceResolver resourceResolver = resolverFactory.getResourceResolver(null); Resource res = resourceResolver.getResource(pageUrl); Page getPage = res.adaptTo(Page.class); x = getPage.getTitle(); resourceResolver.close(); } catch (LoginException e) { e.printStackTrace(); } return x; } }

I also tried (originally) to get it to write out using response.getWriter().write(), but I didn't have any luck. I figure if I can get this first, simple part working then everything should be easier after that.

Thanks for any assistance rendered.

Avatar

Level 1

Thanks guys, I'll give that a try today and see what kind of results that I can get.

I appreciate it. :)

Avatar

Employee

You miss the @Reference at this line:

private ResourceResolverFactory resolverFactory;

write it like this:

@Reference
private ResourceResolverFactory resolverFactory;