org.sakaiproject.nakamura.meservice.LiteAbstractMyGroupsServlet.java Source code

Java tutorial

Introduction

Here is the source code for org.sakaiproject.nakamura.meservice.LiteAbstractMyGroupsServlet.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.nakamura.meservice;

import com.google.common.collect.HashMultiset;
import com.google.common.collect.Lists;
import com.google.common.collect.Multiset;
import com.google.common.primitives.Ints;

import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.SlingHttpServletResponse;
import org.apache.sling.api.resource.ValueMap;
import org.apache.sling.api.servlets.SlingSafeMethodsServlet;
import org.apache.sling.commons.json.JSONException;
import org.sakaiproject.nakamura.api.lite.Session;
import org.sakaiproject.nakamura.api.lite.StorageClientException;
import org.sakaiproject.nakamura.api.lite.StorageClientUtils;
import org.sakaiproject.nakamura.api.lite.accesscontrol.AccessDeniedException;
import org.sakaiproject.nakamura.api.lite.authorizable.Authorizable;
import org.sakaiproject.nakamura.api.lite.authorizable.AuthorizableManager;
import org.sakaiproject.nakamura.api.lite.authorizable.Group;
import org.sakaiproject.nakamura.api.profile.ProfileService;
import org.sakaiproject.nakamura.api.user.UserConstants;
import org.sakaiproject.nakamura.util.ExtendedJSONWriter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.regex.Pattern;

import javax.jcr.RepositoryException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletResponse;

public abstract class LiteAbstractMyGroupsServlet extends SlingSafeMethodsServlet {
    private static final long serialVersionUID = -8743012430930506449L;
    private static final Logger LOGGER = LoggerFactory.getLogger(LiteAbstractMyGroupsServlet.class);

    public static final String PARAM_TEXT_TO_MATCH = "q";

    /**
    * This and related code attempts to mimic the usual client-server API for paged searching.
    *
    * TODO Replace the MyGroupsServlet and MyManagedGroupsServlet with search queries.
    * (This was not possible when the servlets were first written, but should be now.)
    */
    public static final String PARAMS_ITEMS_PER_PAGE = "items";
    /**
    *
    */
    public static final String PARAMS_PAGE = "page";
    /**
     * The default amount of items in a page.
     */
    public static final int DEFAULT_PAGED_ITEMS = 25;

    /**
    *
    */
    public static final String TOTAL = "total";
    /**
     *
     */
    public static final String JSON_RESULTS = "results";
    private static final Set<String> IGNORE_PROPERTIES = new HashSet<String>();
    private static final String[] IGNORE_PROPERTY_NAMES = new String[] { "members", "principals", };

    static {
        for (String ignore : IGNORE_PROPERTY_NAMES) {
            IGNORE_PROPERTIES.add(ignore);
        }
    }

    protected transient ProfileService profileService;

    protected void bindProfileService(ProfileService profileService) {
        this.profileService = profileService;
    }

    protected void unbindProfileService(ProfileService profileService) {
        this.profileService = null;
    }

    @Override
    protected void doGet(SlingHttpServletRequest request, SlingHttpServletResponse response)
            throws ServletException, IOException {
        javax.jcr.Session jcrSession = request.getResourceResolver().adaptTo(javax.jcr.Session.class);
        Session session = StorageClientUtils.adaptToSession(jcrSession);
        String userId = session.getUserId();
        String requestedUserId = request.getParameter("uid");
        if (requestedUserId != null && requestedUserId.length() > 0) {
            userId = requestedUserId;
        }
        try {
            // Find the Group entities associated with the user.
            AuthorizableManager am = session.getAuthorizableManager();
            Authorizable authorizable = am.findAuthorizable(userId);
            if (authorizable == null) {
                response.sendError(HttpServletResponse.SC_BAD_REQUEST, "User " + userId + " not found.");
                return;
            }
            TreeMap<String, Group> groups = getGroups(authorizable, am, request);

            // Get the specified search query filter, if any.
            Pattern filterPattern = getFilterPattern(request.getParameter(PARAM_TEXT_TO_MATCH));

            // Filter the Profiles so as to set up proper paging.
            List<ValueMap> filteredProfiles = new ArrayList<ValueMap>();
            for (Group group : groups.values()) {
                ValueMap profile = profileService.getProfileMap(group, jcrSession);
                if (profile != null) {
                    if ((filterPattern == null) || (isValueMapPattternMatch(profile, filterPattern))) {
                        filteredProfiles.add(profile);
                    }
                } else {
                    LOGGER.info("No Profile found for group {}", group.getId());
                }
            }

            // Write out the Profiles.
            long nitems = longRequestParameter(request, PARAMS_ITEMS_PER_PAGE, DEFAULT_PAGED_ITEMS);
            long page = longRequestParameter(request, PARAMS_PAGE, 0);
            long offset = page * nitems;

            List<String> selectors = Arrays.asList(request.getRequestPathInfo().getSelectors());
            response.setContentType("application/json");
            response.setCharacterEncoding("UTF-8");
            ExtendedJSONWriter writer = new ExtendedJSONWriter(response.getWriter());
            writer.setTidy(selectors.contains("tidy"));

            writer.object();
            writer.key(PARAMS_ITEMS_PER_PAGE);
            writer.value(nitems);
            writer.key(JSON_RESULTS);

            writer.array();
            int i = 0;
            for (ValueMap profile : filteredProfiles) {
                if (i >= (offset + nitems)) {
                    break;
                } else if (i >= offset) {
                    writer.valueMap(profile);
                }
                i++;
            }
            writer.endArray();

            writeFacetFields(filteredProfiles, writer);

            writer.key(TOTAL);
            writer.value(filteredProfiles.size());

            writer.endObject();

        } catch (JSONException e) {
            LOGGER.error("Failed to write out groups for user " + userId, e);
            response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
                    "Failed to build a proper JSON output.");
        } catch (StorageClientException e) {
            LOGGER.error("Failed to write out groups for user " + userId, e);
            response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Storage error.");
        } catch (AccessDeniedException e) {
            LOGGER.error("Failed to write out groups for user " + userId, e);
            response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Access denied error.");
        } catch (RepositoryException e) {
            LOGGER.error("Failed to write out groups for user " + userId, e);
            response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Storage error.");
        }
    }

    private long longRequestParameter(SlingHttpServletRequest request, String paramName, long defaultValue) {
        String p = request.getParameter(paramName);
        if (p == null || p.trim().length() == 0) {
            return defaultValue;
        }
        try {
            return Long.valueOf(p);
        } catch (Exception e) {
            return defaultValue;
        }
    }

    protected String stringRequestParameter(final SlingHttpServletRequest request, final String paramName,
            final String defaultValue) {
        final String p = request.getParameter(paramName);
        if (p == null || p.trim().length() == 0) {
            return defaultValue;
        }
        try {
            return p;
        } catch (Exception e) {
            LOGGER.debug(e.getLocalizedMessage(), e);
            return defaultValue;
        }
    }

    protected boolean isPseudoGroup(Group group) {
        return (Boolean.parseBoolean(String.valueOf(group.getProperty(UserConstants.PROP_PSEUDO_GROUP)))
                && group.getProperty(UserConstants.PROP_PARENT_GROUP_ID) != null);
    }

    protected boolean isManagerGroup(Group group, AuthorizableManager userManager)
            throws AccessDeniedException, StorageClientException {
        String groupId = group.getId();
        String childGroupId = (String) group.getProperty(UserConstants.PROP_PARENT_GROUP_ID);

        Authorizable childGroup = (Authorizable) userManager.findAuthorizable(childGroupId);

        if (childGroup == null) {
            return false;
        }

        String[] managers = (String[]) childGroup.getProperty(UserConstants.PROP_GROUP_MANAGERS);

        if (managers == null) {
            return false;
        }

        for (String manager : managers) {
            if (groupId.equals(manager)) {
                return true;
            }
        }

        return false;
    }

    protected abstract TreeMap<String, Group> getGroups(Authorizable member, AuthorizableManager userManager,
            SlingHttpServletRequest request) throws StorageClientException, AccessDeniedException;

    protected static Pattern getFilterPattern(String filterParameter) {
        Pattern filterPattern;
        if (filterParameter == null) {
            filterPattern = null;
        } else {
            // Translate Jackrabbit-style wildcards to Java-style wildcards.
            String translatedParameter = "(?i).*\\b" + filterParameter.replaceAll("\\*", ".*") + "\\b.*";
            filterPattern = Pattern.compile(translatedParameter);
        }
        return filterPattern;
    }

    private boolean isObjectPatternMatch(Object object, Pattern queryFilter) {
        boolean match = false;
        if (object instanceof ValueMap) {
            match = isValueMapPattternMatch((ValueMap) object, queryFilter);
        } else if (object instanceof Object[]) {
            match = isArrayPatternMatch((Object[]) object, queryFilter);
        } else if (object != null) {
            match = isStringPatternMatch(object.toString(), queryFilter);
        }
        return match;
    }

    private boolean isArrayPatternMatch(Object[] array, Pattern queryFilter) {
        for (Object object : array) {
            if (isObjectPatternMatch(object, queryFilter)) {
                return true;
            }
        }
        return false;
    }

    private boolean isValueMapPattternMatch(ValueMap valueMap, Pattern queryFilter) {
        for (Map.Entry<String, Object> entry : valueMap.entrySet()) {
            Object rawValue = entry.getValue();
            if (!IGNORE_PROPERTIES.contains(entry.getKey())) {
                if (isObjectPatternMatch(rawValue, queryFilter)) {
                    LOGGER.info("Matched Property {} {} ", entry.getKey(), rawValue);
                    return true;
                }
            }
        }
        return false;
    }

    private boolean isStringPatternMatch(String stringValue, Pattern queryFilter) {
        return queryFilter.matcher(stringValue).matches();
    }

    private void writeFacetFields(List<ValueMap> filteredProfiles, ExtendedJSONWriter writer) throws JSONException {
        Multiset<String> tags = HashMultiset.create();
        for (ValueMap profile : filteredProfiles) {
            Object profileTags = profile.get("sakai:tags");
            if (profileTags != null && profileTags instanceof String[]) {
                Collections.addAll(tags, (String[]) profileTags);
            }
        }
        // sort the tags in descending order of their occurrence
        List<Multiset.Entry<String>> sortedTags = Lists.newArrayList(tags.entrySet());
        Collections.sort(sortedTags, new Comparator<Multiset.Entry<String>>() {
            @Override
            public int compare(Multiset.Entry<String> a, Multiset.Entry<String> b) {
                return Ints.compare(b.getCount(), a.getCount());
            }
        });

        // write out the tag names and their counts
        writer.key("facet_fields");
        writer.array();
        writer.object();
        writer.key("tagname");
        writer.array();
        for (Multiset.Entry<String> tag : sortedTags) {
            writer.object();
            writer.key(tag.getElement());
            writer.value(tag.getCount());
            writer.endObject();
        }
        writer.endArray();
        writer.endObject();
        writer.endArray();
    }

}