Servlet
This is a really simple servlet which searches for resources on a particular pathimport java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.logging.Logger;
import javax.management.Query;
import javax.naming.directory.SearchResult;
import org.w3c.dom.Node;
/**
* Servlet that can be used to search for nodes flagged as current = true on nt:unstructured nodes.
*/
@SlingServlet(paths = "/bin/search/current", methods = "GET")
public class SearchCurrentServlet extends SlingSafeMethodsServlet
{
/**
* Default serialisation.
*/
private static final long serialVersionUID = 1L;
/**
* Logger.
*/
private static final Logger LOG = LoggerFactory.getLogger(SearchCurrentServlet.class);
/**
* The path to search.
*/
private static final String SEARCH_PATH = "/content/my/website/data";
/**
* The JSON indentation level.
*/
private static final int JSON_INDENTATION_LEVEL = 4;
/**
* The method called by the get.
* {@inheritDoc}
*/
@Override
protected void doGet(final SlingHttpServletRequest request, final SlingHttpServletResponse response) throws ServletException, IOException
{
// Get the properties that have been requested
final Map<String, Object> map = new HashMap<>();
map.put("type", "nt:unstructured");
map.put("path", SEARCH_PATH);
map.put("1_property", "current");
map.put("1_property.value", true);
// Get the QueryBuilder and do the query from the Map above.
final Session session = request.getResourceResolver().adaptTo(Session.class);
final QueryBuilder builder = request.getResourceResolver().adaptTo(QueryBuilder.class);
final Query query = builder.createQuery(PredicateGroup.create(map), session);
query.setHitsPerPage(Integer.MAX_VALUE);
final SearchResult result = query.getResult();
// Iterate over the results
final Iterator<Resource> resources = result.getResources();
try
{
while (resources.hasNext())
{
// Get the next resource, convert to Node and make it as JSON.
final Resource resource = resources.next();
final Node node = resource.adaptTo(Node.class);
final JsonJcrNode jsonJcrNode = new JsonJcrNode(node, getNodeIgnoreSet());
response.getOutputStream().println(jsonJcrNode.toString(JSON_INDENTATION_LEVEL));
}
}
catch (final RepositoryException | JSONException e)
{
response.getOutputStream().println("Error iterating over the results " + e.getMessage());
LOG.error("Error iterating over the search results", e);
}
response.flushBuffer();
}
}
Unit Test
The unit test uses AemContext to set up a load of mocks which are needed for the test. However, the AemContext does not give a QueryBuilder object out of the box so an adapter has to be defined for this. It is added to the AemContext.import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.jcr.Session;
import javax.servlet.ServletException;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.testing.mock.sling.ResourceResolverType;
import org.apache.sling.testing.mock.sling.servlet.MockSlingHttpServletRequest;
import org.apache.sling.testing.mock.sling.servlet.MockSlingHttpServletResponse;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import com.day.cq.search.PredicateGroup;
import com.day.cq.search.Query;
import com.day.cq.search.QueryBuilder;
import com.day.cq.search.result.SearchResult;
import io.wcm.testing.mock.aem.junit.AemContext;
/**
* Test class for the {@link SearchCurrentServlet}.
*/
@RunWith(MockitoJUnitRunner.class)
public class SearchCurrentServletTest
{
/**
* The mocked query builder.
*/
@Mock
private QueryBuilder queryBuilder;
/**
* The AEM context to test with.
*/
@Rule
public final AemContext context = new AemContext(aemContext ->
{
aemContext.load().json("/result.json", "/result.json");
aemContext.registerAdapter(ResourceResolver.class, QueryBuilder.class, queryBuilder);
}, ResourceResolverType.JCR_MOCK);
/**
* Test the output in json includes fields from the node.
*
* @throws IOException thrown by the servlet method
* @throws ServletException thrown by the servlet method
*/
@Test
public void testDoGet() throws ServletException, IOException
{
// Arrange
final SearchCurrentServlet servlet = new SearchCurrentServlet();
final MockSlingHttpServletRequest request = context.request();
final MockSlingHttpServletResponse response = context.response();
final Query query = mock(Query.class);
final SearchResult searchResult = mock(SearchResult.class);
final Resource resource = context.currentResource("/result.json");
final List<Resource> results = new ArrayList<>();
results.add(resource);
when(queryBuilder.createQuery(any(PredicateGroup.class), any(Session.class))).thenReturn(query);
when(query.getResult()).thenReturn(searchResult);
when(searchResult.getResources()).thenReturn(results.iterator());
// Act
servlet.doGet(request, response);
// Assert
// System.out.println(response.getOutputAsString());
assertTrue(response.getOutputAsString().contains("\"name\": \"Bob\""));
assertTrue(response.getOutputAsString().contains("\"cost\": \"12.34\""));
}
}
Unit Test Data
The AemContext loads a json file from src/test/resources (the classpath) and makes it available for the path specified. When the resource is loaded with context.currentResource("/result.json"); the Resource object returned is made up of the values in the result.json file.
{
"jcr:primaryType":"nt:unstructured",
"current":true,
"name":"Bob",
"cost":12.34
}