org.sakaiproject.kernel.rest.search.RestSearchProvider.java Source code

Java tutorial

Introduction

Here is the source code for org.sakaiproject.kernel.rest.search.RestSearchProvider.java

Source

/*
 * Licensed to the Sakai Foundation (SF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The SF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations under the License.
 */

package org.sakaiproject.kernel.rest.search;

import com.google.common.collect.Lists;
import com.google.inject.Inject;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.sakaiproject.kernel.api.Registry;
import org.sakaiproject.kernel.api.RegistryService;
import org.sakaiproject.kernel.api.jcr.JCRConstants;
import org.sakaiproject.kernel.api.jcr.JCRService;
import org.sakaiproject.kernel.api.rest.RestProvider;
import org.sakaiproject.kernel.api.serialization.BeanConverter;
import org.sakaiproject.kernel.util.IOUtils;
import org.sakaiproject.kernel.util.JCRNodeMap;
import org.sakaiproject.kernel.util.rest.RestDescription;
import org.sakaiproject.kernel.webapp.Initialisable;
import org.sakaiproject.kernel.webapp.RestServiceFaultException;

import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;

import javax.jcr.LoginException;
import javax.jcr.Node;
import javax.jcr.NodeIterator;
import javax.jcr.Property;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.query.InvalidQueryException;
import javax.jcr.query.Query;
import javax.jcr.query.QueryManager;
import javax.jcr.query.QueryResult;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 *
 */
public class RestSearchProvider implements RestProvider, Initialisable {

    private static final Log LOG = LogFactory.getLog(RestSearchProvider.class);
    private static final RestDescription DESC = new RestDescription();
    private static final String KEY = "search";
    private static final String QUERY = "q";
    private static final String NRESUTS_PER_PAGE = "n";
    private static final String PAGE = "p";
    private static final String SORT = "s";
    private static final String PATH = "path";
    private static final String SQL = "sql";
    private static final String MIMETYPE = "mimetype";

    static {
        DESC.setTitle("Search");
        DESC.setBackUrl("../__describe__");
        DESC.setShortDescription("Provides search functionality into the JCR.");
        DESC.addSection(1, "Introduction", "This service allows search using the JCR search implemetation.");
        DESC.addSection(2, "Search ", "Performs a search operation, and returns  " + HttpServletResponse.SC_OK
                + " on sucess, the content of the response is of the form.... TDOD DOC ");
        DESC.addURLTemplate("/rest/" + KEY, "Accepts GET to perform the search");
        DESC.addParameter(QUERY, "The query string");
        DESC.addParameter(NRESUTS_PER_PAGE, "the number of results per page");
        DESC.addParameter(PAGE, "the page, 0 = the first page ");
        DESC.addParameter(PATH,
                "an absolute path into the JCR eg '/_private', this path must be a "
                        + "whole element and cant be partial. eg '/_priv' will select a path "
                        + "starting '/_priv/' and not '/_priv*' ");
        DESC.addParameter(MIMETYPE, "limit the search to a single mime type");

        DESC.addParameter(SORT, "an array of fields to sort by eg sakai:firstName sakai:lastName ");
        DESC.addResponse(String.valueOf(HttpServletResponse.SC_OK),
                "The search was completed OK and the result set is returned ");
        DESC.addResponse(String.valueOf(HttpServletResponse.SC_FORBIDDEN), "If permission to search was denied ");
        DESC.addResponse(String.valueOf(HttpServletResponse.SC_INTERNAL_SERVER_ERROR), " Any other error");

    }

    private JCRService jcrService;
    private BeanConverter beanConverter;
    private Registry<String, RestProvider> registry;

    /**
     *
     */
    @Inject
    public RestSearchProvider(RegistryService registryService, JCRService jcrService, BeanConverter beanConverter) {
        registry = registryService.getRegistry(RestProvider.REST_REGISTRY);
        registry.add(this);
        System.err.println("ADDED " + this
                + " to registry ===================================================================================================================================================================================================================================================");
        this.jcrService = jcrService;
        this.beanConverter = beanConverter;
    }

    /**
     * {@inheritDoc}
     * @see org.sakaiproject.kernel.webapp.Initialisable#destroy()
     */
    public void destroy() {
        registry.remove(this);
    }

    /**
     * {@inheritDoc}
     * @see org.sakaiproject.kernel.webapp.Initialisable#init()
     */
    public void init() {
    }

    /**
     * {@inheritDoc}
     *
     * @see org.sakaiproject.kernel.api.rest.RestProvider#dispatch(java.lang.String[],
     *      javax.servlet.http.HttpServletRequest,
     *      javax.servlet.http.HttpServletResponse)
     */
    public void dispatch(String[] elements, HttpServletRequest request, HttpServletResponse response) {
        try {
            Map<String, Object> map = doSearch(request, response);
            if (map != null) {
                String responseBody = beanConverter.convertToString(map);
                response.setContentType(RestProvider.CONTENT_TYPE);
                response.getOutputStream().print(responseBody);
            }
        } catch (SecurityException ex) {
            throw ex;
        } catch (RestServiceFaultException ex) {
            throw ex;
        } catch (Exception ex) {
            ex.printStackTrace();
            throw new RestServiceFaultException(ex.getMessage(), ex);
        }
    }

    /**
     * @param request
     * @param response
     * @return
     * @throws RepositoryException
     * @throws LoginException
     * @throws IOException
     * @throws UnsupportedEncodingException
     */
    private Map<String, Object> doSearch(HttpServletRequest request, HttpServletResponse response)
            throws RestServiceFaultException, LoginException, RepositoryException, UnsupportedEncodingException,
            IOException {
        Session session = jcrService.getSession();
        String query = request.getParameter(QUERY);
        String nresults = request.getParameter(NRESUTS_PER_PAGE);
        String page = request.getParameter(PAGE);
        String sql = request.getParameter(SQL);
        String[] sort = request.getParameterValues(SORT);
        String path = request.getParameter(PATH);
        String mimeTypeSearch = request.getParameter(MIMETYPE);

        if (StringUtils.isEmpty(query)) {
            throw new RestServiceFaultException(HttpServletResponse.SC_BAD_REQUEST, "No Query");
        }
        String nodeType = "nt:base";
        int nr = 50;
        if (!StringUtils.isEmpty(nresults)) {
            nr = Integer.parseInt(nresults);
        }
        int np = 0;
        if (!StringUtils.isEmpty(page)) {
            np = Integer.parseInt(page);
        }

        int start = np * nr;
        int end = (np + 1) * nr;

        // escape the query
        QueryManager queryManager = session.getWorkspace().getQueryManager();

        Query q = null;
        String escapedQuery = org.sakaiproject.kernel.util.StringUtils.escapeJCRSQL(query);
        StringBuilder sb = new StringBuilder();
        sb.append("SELECT * FROM ").append(nodeType).append(" WHERE ");
        if (!StringUtils.isEmpty(path)) {
            path = path.trim();
            if (!(path.charAt(0) == '/')) {
                path = "/" + path;
            }
            if (!(path.charAt(path.length() - 1) == '/')) {
                path = path + "/";
            }
            path = path + "%";
            path = org.sakaiproject.kernel.util.StringUtils.escapeJCRSQL(path);
            sb.append("jcr:path LIKE '").append(path).append("' AND ");
        }
        if (!StringUtils.isEmpty(mimeTypeSearch)) {
            mimeTypeSearch = mimeTypeSearch.trim();
            mimeTypeSearch = org.sakaiproject.kernel.util.StringUtils.escapeJCRSQL(mimeTypeSearch);
            sb.append("jcr:mimeType = '").append(mimeTypeSearch).append("' AND ");
        }
        sb.append("CONTAINS(.,'").append(escapedQuery).append("' )");
        if (sort != null && sort.length > 0) {
            sb.append(" ORDER BY ").append(sort[0]);
            if (sort.length > 1) {
                for (int i = 1; i < sort.length; i++) {
                    sb.append(",").append(sort[i]);
                }
            }

        }
        String sqlQuery = sb.toString();
        if (!StringUtils.isEmpty(sql)) {
            sqlQuery = sql;
        }

        try {
            q = queryManager.createQuery(sqlQuery, Query.SQL);

        } catch (InvalidQueryException ex) {
            throw new RestServiceFaultException(HttpServletResponse.SC_BAD_REQUEST,
                    "Invalid query presented to content system: " + sqlQuery + " " + ex.getMessage(), ex);
        }
        long startMs = System.currentTimeMillis();

        QueryResult result = q.execute();
        NodeIterator ni = result.getNodes();
        long endMs = System.currentTimeMillis();
        LOG.info("Executed " + sqlQuery + " in " + (endMs - startMs) + " ms " + ni.getSize() + " hits");
        Map<String, Object> results = new HashMap<String, Object>();
        List<Map<String, Object>> resultList = Lists.newArrayList();
        long size = ni.getSize();
        long startPos = 0;
        long endPos = 0;
        try {
            System.err.println("Skipping " + start + " going to " + end);
            ni.skip(start);
            startPos = ni.getPosition();
            endPos = startPos;

            for (int i = start; (i < end) && (ni.hasNext()); i++) {
                Node n = ni.nextNode();

                Node parentNode = n;
                if (!n.isNodeType(JCRConstants.NT_FILE) && !n.isNodeType(JCRConstants.NT_FOLDER)) {
                    parentNode = n.getParent();
                }
                Map<String, Object> itemResponse = new HashMap<String, Object>();
                itemResponse.put("path", parentNode.getPath());
                itemResponse.put("nodeproperties", new JCRNodeMap(parentNode, 1));
                if (JCRConstants.NT_FILE.equals(parentNode.getPrimaryNodeType().getName())) {
                    String mimeType = "application/octet-stream";
                    String encoding = "UTF-8";
                    if (n.hasProperty(JCRConstants.JCR_MIMETYPE)) {
                        Property mimeTypeProperty = n.getProperty(JCRConstants.JCR_MIMETYPE);
                        if (mimeTypeProperty != null) {
                            mimeType = mimeTypeProperty.getString();
                        }
                    }
                    if (n.hasProperty(JCRConstants.JCR_ENCODING)) {
                        Property contentEncoding = n.getProperty(JCRConstants.JCR_ENCODING);
                        if (contentEncoding != null) {
                            encoding = contentEncoding.getString();
                        }
                    }
                    if (mimeType != null && mimeType.startsWith("text") && n.hasProperty(JCRConstants.JCR_DATA)) {
                        Property p = n.getProperty(JCRConstants.JCR_DATA);
                        if (p.getLength() < 10240) {
                            InputStream in = null;
                            try {
                                in = p.getStream();
                                itemResponse.put("content", IOUtils.readFully(in, encoding));
                            } finally {
                                try {
                                    in.close();
                                } catch (Exception ex) {
                                }
                            }
                        }
                    }
                }
                resultList.add(itemResponse);
                endPos = ni.getPosition();
            }

        } catch (NoSuchElementException ex) {
            // went over the end.
        }
        results.put("page", np);
        results.put("pageSize", nr);
        results.put("size", size);
        results.put("start", startPos);
        results.put("end", endPos);
        results.put("results", resultList);
        return results;
    }

    /**
     * {@inheritDoc}
     *
     * @see org.sakaiproject.kernel.api.rest.RestProvider#getDescription()
     */
    public RestDescription getDescription() {
        return DESC;
    }

    /**
     * {@inheritDoc}
     *
     * @see org.sakaiproject.kernel.api.Provider#getKey()
     */
    public String getKey() {
        return KEY;
    }

    /**
     * {@inheritDoc}
     *
     * @see org.sakaiproject.kernel.api.Provider#getPriority()
     */
    public int getPriority() {
        return 0;
    }

}