Java tutorial
/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF 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.apache.rave.opensocial.service.impl; import com.google.common.collect.ImmutableSortedSet; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import com.google.common.util.concurrent.Futures; import org.apache.rave.model.ActivityStreamsEntry; import org.apache.rave.model.ActivityStreamsObject; import org.apache.rave.portal.model.impl.ActivityStreamsEntryImpl; import org.apache.rave.portal.model.impl.ActivityStreamsMediaLinkImpl; import org.apache.rave.portal.repository.ActivityStreamsRepository; import org.apache.rave.util.ActivityConversionUtil; import org.apache.shindig.auth.BasicSecurityToken; import org.apache.shindig.auth.SecurityToken; import org.apache.shindig.protocol.ProtocolException; import org.apache.shindig.protocol.RestfulCollection; import org.apache.shindig.protocol.model.SortOrder; import org.apache.shindig.social.core.model.ActivityEntryImpl; import org.apache.shindig.social.opensocial.model.ActivityEntry; import org.apache.shindig.social.opensocial.model.Person; import org.apache.shindig.social.opensocial.spi.*; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.*; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.logging.Logger; @Service public class DefaultActivityStreamsService implements ActivityStreamService { public static final String OBJECT_TYPE_PERSON = "person"; @Autowired private ActivityStreamsRepository repository; @Autowired public DefaultActivityStreamsService(ActivityStreamsRepository repository, PersonService personService) { this.repository = repository; this.personService = personService; } private PersonService personService; private static Logger log = Logger.getLogger(DefaultActivityStreamsService.class.getName()); private static ActivityConversionUtil converter = new ActivityConversionUtil(); /** * ActivityStreamService impl: */ /** * Returns a list of activities that correspond to the passed in users and group. * <p/> * Specified by: getActivityEntries(...) in ActivityStreamService * <p/> * Parameters: * userIds The set of ids of the people to fetch activities for. * groupId Indicates whether to fetch activities for a group. * appId The app id. * fields The fields to return. Empty set implies all * options The sorting/filtering/pagination options * token A valid SecurityToken * Returns: * a response item with the list of activities. */ @Override public Future<RestfulCollection<ActivityEntry>> getActivityEntries(Set<UserId> userIds, GroupId groupId, String appId, Set<String> fields, CollectionOptions options, SecurityToken token) { List<ActivityEntry> result = getFromRepository(userIds, groupId, appId, fields, options, token); return Futures.immediateFuture(new RestfulCollection<ActivityEntry>(result)); } /** * Returns a set of activities for the passed in user and group that corresponds to a list of activityIds. * <p/> * Specified by: getActivityEntries(...) in ActivityStreamService * <p/> * Parameters: * userId The set of ids of the people to fetch activities for. * groupId Indicates whether to fetch activities for a group. * appId The app id. * fields The fields to return. Empty set implies all * options The sorting/filtering/pagination options * activityIds The set of activity ids to fetch. * token A valid SecurityToken * Returns: * a response item with the list of activities. * Throws: * ProtocolException - if any. */ @Override public Future<RestfulCollection<ActivityEntry>> getActivityEntries(UserId userId, GroupId groupId, String appId, Set<String> fields, CollectionOptions options, Set<String> activityIds, SecurityToken token) throws ProtocolException { List<ActivityEntry> entries = Lists.newLinkedList(); Map<String, Person> peopleById = Maps.newHashMap(); for (String id : activityIds) { entries.add(getActivity(fields, userId.getUserId(token), peopleById, id)); } return Futures.immediateFuture(new RestfulCollection<ActivityEntry>(entries)); } /** * Returns an activity for the passed in user and group that corresponds to a single activityId. * <p/> * Specified by: getActivityEntry(...) in ActivityStreamService * Parameters: * userId The id to fetch activities for. * groupId Indicates whether to fetch activities for a group. * appId The app id. * fields The fields to return. Empty set implies all * activityId The activity id to fetch. * token A valid SecurityToken * Returns: * a response item with the list of activities. * Throws: * ProtocolException - if any. */ @Override public Future<ActivityEntry> getActivityEntry(UserId userId, GroupId groupId, String appId, Set<String> fields, String activityId, SecurityToken token) throws ProtocolException { return Futures.immediateFuture( getActivity(fields, userId.getUserId(token), Maps.<String, Person>newHashMap(), activityId)); } /** * Deletes the activity for the passed in user and group that corresponds to the activityId. * <p/> * Specified by: deleteActivityEntries(...) in ActivityStreamService * Parameters: * userId The user. * groupId The group. * appId The app id. * activityIds A list of activity ids to delete. * token A valid SecurityToken. * Returns: * a response item containing any errors * Throws: * ProtocolException - if any. */ @Override public Future<Void> deleteActivityEntries(UserId userId, GroupId groupId, String appId, Set<String> activityIds, SecurityToken token) throws ProtocolException { String uid = userId.getUserId(token); for (String id : activityIds) { ActivityStreamsEntry activity = repository.get(id); //TODO: should we be checking against the user id? if (activity != null && activity.getUserId().equalsIgnoreCase(uid)) { repository.delete(activity); } } return Futures.immediateFuture(null); } /** * Updates the specified Activity. * <p/> * Specified by: updateActivityEntry(...) in ActivityStreamService * Parameters: * userId The id of the person to update the activity for * groupId The group * appId The app id * fields The fields to return * activity The updated activity * activityId The id of the existing activity to update * token A valid SecurityToken * Returns: * a response item containing any errors * Throws: * ProtocolException - if any */ @Override public Future<ActivityEntry> updateActivityEntry(UserId userId, GroupId groupId, String appId, Set<String> fields, ActivityEntry activity, String activityId, SecurityToken token) throws ProtocolException { String uid = userId.getUserId(token); //Verify / set ids if (activityId == null) { if (activity.getId() == null) { //One of these must have an id //TODO: Throw an error? return null; } else { activityId = activity.getId(); } } else { if (activity.getId() == null) { activity.setId(activityId); } else if (!activity.getId().equalsIgnoreCase(activityId)) { //TODO: should this throw an error if two different ids are passed in? return null; } } ActivityStreamsEntryImpl tmp = converter.convert(activity); tmp.setUserId(uid); tmp.setGroupId(groupId.getObjectId().toString()); tmp.setAppId(appId); ActivityStreamsEntry saved = repository.save(tmp); ActivityEntryImpl impl = converter.convert(saved); return Futures.immediateFuture((ActivityEntry) impl); } /** * Creates the passed in activity for the passed in user and group. Once createActivity is called, getActivities will be able to return the Activity. * <p/> * Specified by: createActivityEntry(...) in ActivityStreamService * Parameters: * userId The id of the person to create the activity for. * groupId The group. * appId The app id. * fields The fields to return. * activity The activity to create. * token A valid SecurityToken * Returns: * a response item containing any errors * Throws: * ProtocolException - if any. */ @Override public Future<ActivityEntry> createActivityEntry(UserId userId, GroupId groupId, String appId, Set<String> fields, ActivityEntry activity, SecurityToken token) throws ProtocolException { String uid = userId.getUserId(token); ActivityStreamsEntryImpl activityEntity = converter.convert(activity); ActivityStreamsEntry saved = repository.save(activityEntity); return Futures.immediateFuture((ActivityEntry) converter.convert(saved)); } /** * AggregatorProcessor impl: */ //@Override public void process(Object input) { // TODO: what data format will be returned by ActivityStream-producing feeds? JSON Collection? // parse Activities out of stream; turn into ActivityEntries // store ActivityEntries in repository if (input instanceof ArrayList) { ArrayList<ActivityStreamsEntryImpl> activities = (ArrayList<ActivityStreamsEntryImpl>) input; for (ActivityStreamsEntryImpl activity : activities) { repository.save(activity); } } } //@Override public Collection<? extends ActivityEntry> getContent() { return converter.convert(repository.getAll()); } /** * Utilities */ /** * From JsonDbOpensocialService. I believe this is supposed to find the intersection of * the set of userids passed in, and the set of users belonging to the group. If the * group type is "friends", then maybe it's supposed to return the list of friends * for each of the user ids provided? * * @param users * @param group * @param token * @return */ public Set<String> getIdSet(Set<UserId> users, GroupId group, SecurityToken token) { Set<String> ids = Sets.newLinkedHashSet(); for (UserId user : users) { ids.addAll(getIdSet(user, group, token)); } return ids; } private ActivityEntry getActivity(Set<String> fields, String uid, Map<String, Person> peopleById, String id) { ActivityStreamsEntry entry = repository.get(id); if (entry != null) { if (entry.getUserId().equalsIgnoreCase(uid)) { populatePersonObjects(entry, peopleById); return filterFields(entry, fields); } } return null; } //From JsonDbOpensocialService private Set<String> getIdSet(UserId user, GroupId group, SecurityToken token) { String userId = user.getUserId(token); if (group == null) { return ImmutableSortedSet.of(userId); } Set<String> returnVal = Sets.newLinkedHashSet(); returnVal.add(userId); Set<UserId> userIdSortedSet = new HashSet<UserId>(); userIdSortedSet.add(user); try { List<Person> people = personService.getPeople(userIdSortedSet, group, null, returnVal, token).get() .getList(); for (Person p : people) { returnVal.add(p.getId()); } } catch (InterruptedException e) { log.info("request interrupted " + e); } catch (ExecutionException x) { log.info("execution exception " + x); } catch (Exception e) { log.info("problem: " + e); } return returnVal; } /** * Given an DeserializableActivityEntry, return a new one that only has * the specified fields set on it. * * @param entry * @param fields * @return */ private ActivityEntry filterFields(ActivityStreamsEntry entry, Set<String> fields) { // TODO: implement return converter.convert(entry); } private List<ActivityEntry> getFromRepository(Set<UserId> userIds, GroupId groupId, String appId, Set<String> fields, CollectionOptions options, SecurityToken token) { List<ActivityStreamsEntry> result = Lists.newArrayList(); Map<String, Person> peopleById = Maps.newHashMap(); Set<String> idSet = getIdSet(userIds, groupId, token); for (String id : idSet) { List<ActivityStreamsEntry> entries = repository.getByUserId(id); if (entries != null) { result.addAll(populateActivityEntries(entries, peopleById)); } } sortByPublished(result, options == null ? null : options.getSortOrder()); return convert(result); } private List<ActivityStreamsEntry> populateActivityEntries(List<ActivityStreamsEntry> entries, Map<String, Person> peopleById) { for (ActivityStreamsEntry entry : entries) { populatePersonObjects(entry, peopleById); } return entries; } private void sortByPublished(List<ActivityStreamsEntry> result, final SortOrder order) { Collections.sort(result, new Comparator<ActivityStreamsEntry>() { @Override public int compare(ActivityStreamsEntry one, ActivityStreamsEntry two) { Date publishedOne = one.getPublished(); Date publishedTwo = two.getPublished(); if (publishedOne == null) { return publishedTwo == null ? 0 : -1; } else if (publishedTwo == null) { return 1; } else { return order != null && order == SortOrder.descending ? publishedOne.compareTo(publishedTwo) : publishedTwo.compareTo(publishedOne); } } }); } private List<ActivityEntry> convert(List<ActivityStreamsEntry> result) { List<ActivityEntry> converted = Lists.newArrayList(); for (ActivityStreamsEntry entry : result) { converted.add(converter.convert(entry)); } return converted; } private void populatePersonObjects(ActivityStreamsEntry entry, Map<String, Person> peopleById) { ActivityStreamsObject actor = entry.getActor(); if (entry.getActor() != null && OBJECT_TYPE_PERSON.equals(actor.getObjectType())) { populatePerson(peopleById, actor); } if (entry.getObject() != null && OBJECT_TYPE_PERSON.equals(entry.getObject().getObjectType())) { populatePerson(peopleById, entry.getObject()); } if (entry.getTarget() != null && OBJECT_TYPE_PERSON.equals(entry.getTarget().getObjectType())) { populatePerson(peopleById, entry.getTarget()); } } private void populatePerson(Map<String, Person> peopleById, ActivityStreamsObject actor) { String id = actor.getId(); Person person = peopleById.containsKey(id) ? peopleById.get(id) : getPerson(id); if (person != null) { peopleById.put(id, person); actor.setUrl(person.getProfileUrl()); actor.setDisplayName(person.getDisplayName()); ActivityStreamsMediaLinkImpl image = new ActivityStreamsMediaLinkImpl(); image.setUrl(person.getThumbnailUrl()); actor.setImage(image); } } private Person getPerson(String id) { Person person; SecurityToken token = new BasicSecurityToken(null, id, null, null, null, null, null, null, null); try { person = personService.getPerson(new UserId(UserId.Type.viewer, id), null, token).get(); } catch (Exception e) { throw new RuntimeException("Unable to retrieve person", e); } return person; } /** * @return the repository */ public ActivityStreamsRepository getRepository() { return repository; } /** * @param repository the repository to set */ public void setRepository(ActivityStreamsRepository repository) { this.repository = repository; } }