Expand my Community achievements bar.

Create a Servlet to read a property from Multiple pages

Avatar

Level 2

Hi All,

I have a requirement to create a Sling Servlet that will get the root path of my website. It should loop through the children pages and read a particular property and expose the page url and the property as JSON. I want this to be displayed in the frontend by creating a HTL component that will get the data from this servlet. Could you please suggest the best approach that can be followed to accomplish this?


19 Replies

Avatar

Level 10

This is quite possible with HTL and Sling Models, You would need to create a BEAN on the Java side to store all props. THen have logic in Java to read all of the child pages and read the props that you want. I would look at using JCR SQL2 to perform the query:

String sqlStatement = "select * from [cq:Page] where isdescendantnode('/content/we-retail') "

Then iterate through the result set and get the props you want. in the Java - use GSON (or another JSON Java lib) to construct the JSON too.

Avatar

Level 10

The value /content/we-retail can be passed to sling model via a path finder in component dialog. 

Avatar

Level 10

Also be aware that looping through all the pages in a result set will be bad performance - esp if you want to display in a component. If you need to get that data for some reason - it makes more sense to run in a workflow - where it will not impact performace of your site.

Avatar

Level 10

I did testing - even with a smaller page range - it will still take time to progress all pages in the result set. See:

.Size.png

Avatar

Level 2

I'm working on an application as part of an assignment which will have a maximum of 5-6 pages. So it's not an issue if performance is affected.

Avatar

Level 2

That's quite alright but my requirement is to have a Servlet that does this. Is it possible to get the values from a Sling servlet and parse it to be used in HTL? Thanks

Avatar

Level 10

Java logic is what matters. You can call a Servlet or a use a Sling Model. Jusy different ways to invoke Java business logic in a OSGi bundle.

I am working on an example to do this.

Avatar

Level 1

Why only a servlet? Best way is to write a sling model and call the service from that where the service will return the object with the necessary properties.

Avatar

Level 2

I'm working on an application as part of an assignment and the Servlet was already provided. My job is to read the JSON received as response from the Servlet and display it on the page using HTL.

Avatar

Community Advisor

Why don't you use client side approach,  make a Ajax request to servlet and get the son response and parse the json response and update DOM using jQuery.



Arun Patidar

Avatar

Level 2

Unfortunately, the assignment does not allow us to be flexible and asks us to stick to the requirements which are given below:

Use servlet to read the page content and expose it as a json.

In a component display the data dynamically based on the json response in a slightly component

The servlet has been done by my team mate and the other part needs to be done by me. If there is no way this can be done, we can probably request the requirements to be changed.     

Avatar

Community Advisor

The question is here - how will you get the json content from different servlet which is created by your team mate in HTL.

You probably endup making http call to servlet from model class and get json response and convert into list and access in HTL or

you would be making call to servlet from HTL using selector but again you won't be able to parse json data with Javascript code.

If you are exposing all the data in page which coming from servlet there won't be no harm to do it client side, it will also slighty improve page load performance.



Arun Patidar

Avatar

Level 2

The servlet and the HTL component part will be integrated together at the end. I was planning on invoking the servlet using Http from a Sling Model, convert Json to a list and consume it in HTL. However, authorization error occurs in the Author environment.     

Avatar

Community Advisor

Yes, In Author environment you need to make call using authentication.

https://www.baeldung.com/httpclient-4-basic-authentication

If you are planning to integrate both servlet and Model, you can simply just write a model and put all your logic in model itself.



Arun Patidar

Avatar

Level 10

I agree with Arun - use Model to query the pages, read the properties, construct the JSON. If you are only querying 5-6 pages - there will be min impact on performance. I was saying perfor may be impacted if you wanted to read hundreds of pages.

Avatar

Level 10

I coded a little Java with JCR API to read page props. Next - i created this JSON with GSON API. You can use this as a starting point. 

Output:

{

  "name": "page",

  "dataset": [

    {

      "title": "Camping in Western Australia",

      "template": "/conf/we-retail/settings/wcm/templates/experience-page"

    },

    {

      "title": "Arctic Surfing In Lofoten",

      "template": "/conf/we-retail/settings/wcm/templates/experience-page"

    },

    {

      "title": "Steelhead and Spines in Alaska",

      "template": "/conf/we-retail/settings/wcm/templates/experience-page"

    },

    {

      "title": "48 hours of Wilderness",

      "template": "/conf/we-retail/settings/wcm/templates/experience-page"

    },

    {

      "title": "Skitouring",

      "template": "/conf/we-retail/settings/wcm/templates/experience-page"

    },

    {

      "title": "Fly-fishing the Amazon",

      "template": "/conf/we-retail/settings/wcm/templates/experience-page"

    }

  ]

}

Java code:

WRITE CODE TO GET JCR API SESSION....

//Obtain the query manager for the session ...
   javax.jcr.query.QueryManager queryManager = session.getWorkspace().getQueryManager();

  String sqlStatement = "select * from [cq:Page] where isdescendantnode('/content/we-retail/us/en/experience') " ;

  javax.jcr.query.Query query = queryManager.createQuery(sqlStatement,"JCR-SQL2");

   //Execute the query and get the results ...
   javax.jcr.query.QueryResult result = query.execute();

   //Iterate over the nodes in the results ...
   javax.jcr.NodeIterator nodeIter = result.getNodes();

   long size = nodeIter.getSize();

  Bean myBean = null;

  ArrayList<Bean> list = new ArrayList() ;

   while ( nodeIter.hasNext() ) {

   //For each node-- get the path of the node
   javax.jcr.Node node = nodeIter.nextNode();

  myBean = new Bean();

  javax.jcr.Node context = node.getNode("jcr:content");

   //Set Bean and push to List
   myBean.setTitle(context.getProperty("jcr:title").getString());

  myBean.setTemplate(context.getProperty("cq:template").getString());

  list.add(myBean);

  }

  System.out.println("the list is this big "+list.size() ) ;

  doGSON(list) ;

  }

   catch(Exception e){

  e.printStackTrace();

  }

   return null;

}

   public String doGSON(ArrayList theList)

  {

   // create the albums object
   JsonObject albums = new JsonObject();

   // add a property calle title to the albums object
   albums.addProperty("name", "page");

   // create an array called datasets
   JsonArray datasets = new JsonArray();

   // create a dataset
   JsonObject dataset = null ;

   int size = theList.size();

   for (int index =0; index<size; index++)

  {

  dataset = new JsonObject();

  Bean singleBean = (Bean) theList.get(index);

  String title = singleBean.getTitle();

  String template = singleBean.getTemplate();

  dataset.addProperty("title", title);

   // add the property album_year to the dataset
   dataset.addProperty("template", template);

  datasets.add(dataset);

  albums.add("dataset", datasets);

  }

 
   Gson gson = new GsonBuilder().setPrettyPrinting().serializeNulls().setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE).create();

  System.out.println(gson.toJson(albums));

  

   return "";

  }

Avatar

Level 10

BEAN--

public class Bean {

   private String title;

   private String template ;

   public void setTitle(String title)

  {

   this.title = title;

  }

   public String getTitle()

  {

   return this.title  ;

  }

   public void setTemplate(String template)

  {

   this.template = template;

  }

   public String getTemplate()

  {

   return this.template  ;

  }

}

Avatar

Level 2

I tried using HttpClient to access the Servlet from Sling Models class. This doesn't work as it shows me an error - 401 not authorized.

I tried adding this annotation to my servlet -

@Property(name = "sling.auth.requirements", value = "-servlet Path")

However, this doesn't work. Am I doing anything wrong or does 6.3 not support this?