Package org.apache.shindig.social.opensocial.jpa.spi

Source Code of org.apache.shindig.social.opensocial.jpa.spi.PersonServiceDb

/*
* 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.shindig.social.opensocial.jpa.spi;

import java.util.List;
import java.util.Set;
import java.util.concurrent.Future;

import javax.persistence.EntityManager;
import javax.persistence.Query;
import javax.servlet.http.HttpServletResponse;

import org.apache.shindig.auth.SecurityToken;
import org.apache.shindig.common.util.ImmediateFuture;
import org.apache.shindig.protocol.ProtocolException;
import org.apache.shindig.protocol.RestfulCollection;
import org.apache.shindig.social.opensocial.jpa.PersonDb;
import org.apache.shindig.social.opensocial.jpa.api.FilterCapability;
import org.apache.shindig.social.opensocial.jpa.api.FilterSpecification;
import org.apache.shindig.social.opensocial.model.Person;
import org.apache.shindig.social.opensocial.spi.CollectionOptions;
import org.apache.shindig.social.opensocial.spi.GroupId;
import org.apache.shindig.social.opensocial.spi.PersonService;
import org.apache.shindig.social.opensocial.spi.UserId;

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

/**
* Implements the PersonService from the SPI binding to the JPA model and providing queries to
* support the OpenSocial implementation.
*/
public class PersonServiceDb implements PersonService {

  /**
   * This is the JPA entity manager, shared by all threads accessing this service (need to check
   * that its really thread safe).
   */
  private EntityManager entiyManager;

  /**
   * Create the PersonServiceDb, injecting an entity manager that is configured with the social
   * model.
   *
   * @param entityManager the entity manager containing the social model.
   */
  @Inject
  public PersonServiceDb(EntityManager entityManager) {
    this.entiyManager = entityManager;
  }

  /**
   * {@inheritDoc}
   */
  public Future<RestfulCollection<Person>> getPeople(Set<UserId> userIds,
       GroupId groupId, CollectionOptions collectionOptions, Set<String> fields,
       SecurityToken token) throws ProtocolException {
    // for each user id get the filtered userid using the token and then, get the users identified
    // by the group id, the final set is filtered
    // using the collectionOptions and return the fields requested.

    // not dealing with the collection options at the moment, and not the fields because they are
    // either lazy or at no extra costs, the consumer will either access the properties or not
    List<Person> plist = null;
    int lastPos = 1;
    Long totalResults = null;

    StringBuilder sb = new StringBuilder();
    // sanitize the list to get the uid's and remove duplicates
    List<String> paramList = SPIUtils.getUserList(userIds, token);
    // select the group Id as this will drive the query
    switch (groupId.getType()) {
    case all:
      // select all contacts
      sb.append(PersonDb.JPQL_FINDALLPERSON);
      lastPos = JPQLUtils.addInClause(sb, "p", "id", lastPos, paramList.size());
      break;
    case friends:
      // select all friends (subset of contacts)
      sb.append(PersonDb.JPQL_FINDPERSON_BY_FRIENDS);
      lastPos = JPQLUtils.addInClause(sb, "p", "id", lastPos, paramList.size());
      sb.append(") ");
      // TODO Group by doesn't work in HSQLDB or Derby - causes a "Not in aggregate function or group by clause" jdbc exception
      // sb.append(" group by p ");
      break;
    case objectId:
      // select those in the group
      sb.append(PersonDb.JPQL_FINDPERSON_BY_GROUP);
      lastPos = JPQLUtils.addInClause(sb, "p", "id", lastPos, paramList.size());
      sb.append(" and g.id = ?").append(lastPos);
      lastPos++;
      break;
    case self:
      // select self
      sb.append(PersonDb.JPQL_FINDPERSON);
      lastPos = JPQLUtils.addInClause(sb, "p", "id", lastPos, paramList.size());
      break;
    default:
      throw new ProtocolException(HttpServletResponse.SC_BAD_REQUEST, "Group ID not recognized");

    }

    if (GroupId.Type.self.equals(groupId.getType())) {
      plist = JPQLUtils.getListQuery(entiyManager, sb.toString(), paramList, collectionOptions);
      totalResults = Long.valueOf(1);
      if (plist.isEmpty()) {
        throw new ProtocolException(HttpServletResponse.SC_BAD_REQUEST, "Person not found");
      }
    } else {
      int filterPos = addFilterClause(sb, PersonDb.getFilterCapability(), collectionOptions,
          lastPos);
      if (filterPos > 0) {
        paramList.add(collectionOptions.getFilterValue());
      }

      // Get total results, that is count the total number of rows for this query
      totalResults = JPQLUtils.getTotalResults(entiyManager, sb.toString(), paramList);

      // Execute ordered and paginated query
      if (totalResults > 0) {
        addOrderClause(sb, collectionOptions);
        plist = JPQLUtils.getListQuery(entiyManager, sb.toString(), paramList, collectionOptions);
      }

      if (plist == null) {
        plist = Lists.newArrayList();
      }
    }

    // all of the above could equally have been placed into a thread to overlay the
    // db wait times.
    RestfulCollection<Person> restCollection = new RestfulCollection<Person>(
        plist, collectionOptions.getFirst(), totalResults.intValue(), collectionOptions.getMax());
    return ImmediateFuture.newInstance(restCollection);

  }

  /**
   * {@inheritDoc}
   */
  public Future<Person> getPerson(UserId id, Set<String> fields, SecurityToken token)
      throws ProtocolException {
    String uid = id.getUserId(token);
    Query q = entiyManager.createNamedQuery(PersonDb.FINDBY_PERSONID);
    q.setParameter(PersonDb.PARAM_PERSONID, uid);
    q.setFirstResult(0);
    q.setMaxResults(1);
    List<?> plist = q.getResultList();
    Person person = null;
    if (plist != null && !plist.isEmpty()) {
      person = (Person) plist.get(0);
    }
    return ImmediateFuture.newInstance(person);
  }

  /**
   * {@inheritDoc}
   */
  public Future<Person> updatePerson(UserId id, Person person, SecurityToken token)
      throws ProtocolException {
    String viewer = token.getViewerId(); // viewer
    String uid = id.getUserId(token); // person to update

    if (!viewerCanUpdatePerson(viewer,uid)) {
      throw new ProtocolException(HttpServletResponse.SC_FORBIDDEN, "User '" + viewer + "' does not have enough privileges to update person '"+uid+"'");
    }

    Query q = null;
    // Get the person object from db
    q = entiyManager.createNamedQuery(PersonDb.FINDBY_PERSONID);
    q.setParameter(PersonDb.PARAM_PERSONID, uid);
    q.setFirstResult(0);
    q.setMaxResults(1);

    List<?> plist = q.getResultList();
    PersonDb personDb = null;
    if (plist != null && !plist.isEmpty()) {
      personDb = (PersonDb) plist.get(0);
      // update person's fields: displayName, aboutMe, age
      // add fields that has to be updated
      personDb.setThumbnailUrl(person.getThumbnailUrl());
    }

    // TODO How should transactions be managed? Should samples be using warp-persist instead?
    if (!entiyManager.getTransaction().isActive()) {
      entiyManager.getTransaction().begin();
    }
    // update person's names object (in another db table)
    // if (person.getName() != null) {
    //   if (personDb.getName() != null) {
    //     entiyManager.remove(personDb.getName());
    //   }
    //   personDb.setName(person.getName());
    // }
    // update person's emails object (in another db table)
    // if (person.getEmails() != null) {
    //   for (Object e : personDb.getEmails()) {
    //     entiyManager.remove(e);
    //   }
    //   List<ListField> emails = Lists.newArrayList();
    //   for (ListField c : person.getEmails()) {
    //     c.setPerson(personDb);
    //     emails.add(c);
    //   }
    //   personDb.setEmails(emails);
    // }
    // provide other person's data updates similarily to name and emails

    entiyManager.persist(personDb);

    entiyManager.getTransaction().commit();

    // send personDb data back
    return ImmediateFuture.newInstance((Person) personDb);
  }

  /** Check if a viewer is allowed to update the given person record. **/
  protected boolean viewerCanUpdatePerson(String viewer, String person) {
    // A person can only update his own personal data (by default)
    // if you wish to allow other people to update the personal data of the user
    // you should change the current function
    return viewer.equals(person) ? true : false;
  }

  /**
   * Add a filter clause specified by the collection options.
   *
   * @param sb the query builder buffer
   * @param collectionOptions the options
   * @param lastPos the last positional parameter that was used so far in the query
   * @return
   */
  private int addFilterClause(StringBuilder sb, FilterCapability filterable,
      CollectionOptions collectionOptions, int lastPos) {
    // this makes the filter value saf
    String filter = filterable.findFilterableProperty(collectionOptions.getFilter(),
        collectionOptions.getFilterOperation());
    String filterValue = collectionOptions.getFilterValue();
    int filterPos = 0;
    if (FilterSpecification.isValid(filter)) {
      if (FilterSpecification.isSpecial(filter)) {
        if (PersonService.HAS_APP_FILTER.equals(filter)) {
          // Retrieves all friends with any data for this application.
          // TODO: how do we determine which application is being talked about,
          // the assumption below is wrong
          filterPos = lastPos + 1;
          sb.append(" f.application_id  = ?").append(filterPos);
        } else if (PersonService.TOP_FRIENDS_FILTER.equals(filter)) {
          // Retrieves only the user's top friends, this is defined here by the implementation
          // and there is an assumption that the sort order has already been applied.
          // to do this we need to modify the collections options
          // there will only ever b x friends in the list and it will only ever start at 1

          collectionOptions.setFirst(1);
          collectionOptions.setMax(20);

        } else if (PersonService.ALL_FILTER.equals(filter)) {
           // select all, ie no filtering
        } else if (PersonService.IS_WITH_FRIENDS_FILTER.equals(filter)) {
          filterPos = lastPos + 1;
          sb.append(" f.friend  = ?").append(filterPos);
        }
      } else {
        sb.append("p.").append(filter);
        switch (collectionOptions.getFilterOperation()) {
        case contains:
          filterPos = lastPos + 1;
          sb.append(" like ").append(" ?").append(filterPos);
          filterValue = '%' + filterValue + '%';
          collectionOptions.setFilter(filterValue);
          break;
        case equals:
          filterPos = lastPos + 1;
          sb.append(" = ").append(" ?").append(filterPos);
          break;
        case present:
          sb.append(" is not null ");
          break;
        case startsWith:
          filterPos = lastPos + 1;
          sb.append(" like ").append(" ?").append(filterPos);
          filterValue = '%' + filterValue + '%';
          collectionOptions.setFilter(filterValue);
          break;
        }
      }
    }
    return filterPos;
  }

  /**
   * Add an order clause to the query string.
   *
   * @param sb the buffer for the query string
   * @param collectionOptions the options to use for the order.
   */
  private void addOrderClause(StringBuilder sb, CollectionOptions collectionOptions) {
    String sortBy = collectionOptions.getSortBy();
    if (sortBy != null && sortBy.length() > 0) {
      if (PersonService.TOP_FRIENDS_SORT.equals(sortBy)) {
        // TODO sorting by friend.score doesn't work right now because of group by issue (see above TODO)
        // this assumes that the query is a join with the friends store.
        sb.append(" order by f.score ");
      } else {
        if ("name".equals(sortBy)) {
          // TODO Is this correct?
          // If sortBy is name then order by p.name.familyName, p.name.givenName.
          sb.append(" order by p.name.familyName, p.name.givenName ");
        } else {
          sb.append(" order by p.").append(sortBy);
        }
        switch (collectionOptions.getSortOrder()) {
        case ascending:
          sb.append(" asc ");
          break;
        case descending:
          sb.append(" desc ");
          break;
        }
      }
    }
  }
}
TOP

Related Classes of org.apache.shindig.social.opensocial.jpa.spi.PersonServiceDb

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.