Java tutorial
/** * 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(); } }