/*
* Copyright (c) 2009-2010 Lockheed Martin Corporation
*
* Licensed 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.eurekastreams.server.persistence;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import javax.persistence.Query;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.eurekastreams.commons.hibernate.QueryOptimizer;
import org.eurekastreams.server.domain.Followable;
import org.eurekastreams.server.domain.Follower;
import org.eurekastreams.server.domain.PagedSet;
import org.eurekastreams.server.domain.Person;
import org.eurekastreams.server.persistence.strategies.DescendantOrganizationStrategy;
/**
* This class provides the mapper functionality for Person entities.
* TODO {@link #addFollower(long, long)}, {@link #removeFollower(long, long)} fails on oracle because
* the 1 is subtracted after the subquery (in the hql) rather than before
*/
@Deprecated
public class PersonMapper extends DomainEntityMapper<Person> implements FollowMapper
{
/**
* Local log instance.
*/
private static Log logger = LogFactory.getLog(PersonMapper.class);
/**
* Constructor.
*
* @param inQueryOptimizer
* the QueryOptimizer to use for specialized functions.
*/
public PersonMapper(final QueryOptimizer inQueryOptimizer)
{
super(inQueryOptimizer);
}
/**
* The descendant organization strategy.
*/
private DescendantOrganizationStrategy descendantOrgStrategy;
/**
* Set the DescendantOrgStrategy.
*
* @param inDescendantOrgStrategy
* the descendantOrgStrategy to set
*/
public void setDescendantOrgStrategy(final DescendantOrganizationStrategy inDescendantOrgStrategy)
{
descendantOrgStrategy = inDescendantOrgStrategy;
}
/**
* Get the descendant organization strategy.
*
* @return the descendantOrgStrategy the descendant org strategy
*/
protected DescendantOrganizationStrategy getDescendantOrgStrategy()
{
if (descendantOrgStrategy == null)
{
throw new NullPointerException("descendantOrgStrategy is null - make sure to set it on JpaPersonMapper.");
}
return descendantOrgStrategy;
}
/**
* Retrieve the name of the DomainEntity. This is to allow for the super class to identify the table within
* hibernate.
*
* @return The name of the domain entity.
*/
@Override
protected String getDomainEntityName()
{
return "Person";
}
/**
* Find a person by accountId.
*
* @param accountId
* the accountId of the user to search for - lower-cased for uniqueness
* @return the Person with the user account
*/
@SuppressWarnings("unchecked")
public Person findByAccountId(final String accountId)
{
Query q = getEntityManager().createQuery("from Person where accountId = :accountId").setParameter("accountId",
accountId.toLowerCase());
List results = q.getResultList();
return (results.size() == 0) ? null : (Person) results.get(0);
}
/**
* Find a person by opensocial ID.
*
* @param openSocialId
* the openSocialId of the user to search for - lower-cased for uniqueness
* @return the Person with the open social id.
*/
@SuppressWarnings("unchecked")
public Person findByOpenSocialId(final String openSocialId)
{
Query q = getEntityManager().createQuery("from Person where openSocialId = :openSocialId").setParameter(
"openSocialId", openSocialId.toLowerCase());
List results = q.getResultList();
return (results.size() == 0) ? null : (Person) results.get(0);
}
/**
* Creates a follower/following relationship between two Person objects.
*
* @param followerId
* The id of the follower Person
* @param followingId
* The id of the person being Followed.
*/
public void addFollower(final long followerId, final long followingId)
{
Query q = getEntityManager().createQuery(
"FROM Follower where followerId=:followerId and followingId=:followingId").setParameter("followerId",
followerId).setParameter("followingId", followingId);
if (q.getResultList().size() > 0)
{
// already following
return;
}
// add follower
getEntityManager().persist(new Follower(followerId, followingId));
// now update the counts for persons subtracting 1 for themselves.
getEntityManager().createQuery(
"update versioned Person set followingCount = -1 + following.size where id=:followerId").setParameter(
"followerId", followerId).executeUpdate();
getEntityManager().createQuery(
"update versioned Person set followersCount = -1 + followers.size where id=:followingId").setParameter(
"followingId", followingId).executeUpdate();
getEntityManager().flush();
getEntityManager().clear();
// reindex the following in the search index
// Note: Finding the entity by id is massively faster than doing a refresh on
// the entity. This way the recently fetched entity will have the updated counts
// to send to index.
Person followingEntity = findById(followingId);
getFullTextSession().index(followingEntity);
}
/**
* Finds people that have been orphaned from their parent organization.
*
* @return List<Person> A list of person objects that have a parent organization that is not in their related
* organization list
*/
@SuppressWarnings("unchecked")
public List<Person> findOrphanedPeople()
{
Query q = getEntityManager().createQuery(
"SELECT p FROM Person p WHERE p.parentOrganization NOT IN ELEMENTS (p.relatedOrganizations)");
List<Person> orphanedPeople = q.getResultList();
return orphanedPeople;
}
/**
* Purges the related organizations for all people.
*
*/
public void purgeRelatedOrganizations()
{
getEntityManager().createQuery("DELETE FROM PersonRelatedOrganization").executeUpdate();
}
/**
* Removes a follower/following relationship between two Person objects.
*
* @param followerId
* The id of the follower Person
* @param followingId
* The person id being Followed.
*/
public void removeFollower(final long followerId, final long followingId)
{
int rowsDeleted = getEntityManager().createQuery(
"DELETE FROM Follower where followerId=:followerId and followingId=:followingId").setParameter(
"followerId", followerId).setParameter("followingId", followingId).executeUpdate();
if (rowsDeleted == 0)
{
// not following, short circuit.
return;
}
// now update the counts for persons subtracting 1 for themselves.
getEntityManager().createQuery(
"update versioned Person set followingCount = -1 + following.size " + "where id=:followerId")
.setParameter("followerId", followerId).executeUpdate();
getEntityManager().createQuery(
"update versioned Person set followersCount = - 1 + followers.size where id=:followingId").setParameter(
"followingId", followingId).executeUpdate();
getEntityManager().flush();
getEntityManager().clear();
Person followingEntity = findById(followingId);
// reindex the following in the search index
getFullTextSession().index(followingEntity);
}
/**
* Returns a set of People following the specified Person, minus themselves.
*
* @param accountId
* The Person for whom to get followers.
* @param start
* paging start.
* @param end
* paging end.
* @return paged set of followers.
*/
public PagedSet<Person> getFollowers(final String accountId, final int start, final int end)
{
return getConnectionsMinusSelf(accountId, start, end, "followers");
}
/**
* Returns a set of People who are being followed by the specified person.
*
* @param accountId
* The Person for whom to get following.
* @param start
* paging start.
* @param end
* paging end.
* @return paged set of following.
*/
public PagedSet<Followable> getFollowing(final String accountId, final int start, final int end)
{
return getConnectionsMinusSelf(accountId, start, end, "following");
}
/**
*Gets connections for a Person excluding that person in the results.
*
* @param <V>
* allows this method to return PagedSets of different types (Person, Followable).
* @param accountId
* The Person for whom to get info.
* @param start
* paging start.
* @param end
* paging end.
*
* @param which
* should be attribute in the model: "follower" or "following"
*
* @return paged set of connections.
*/
private <V> PagedSet<V> getConnectionsMinusSelf(final String accountId, final int start, final int end,
final String which)
{
HashMap<String, Object> parameters = new HashMap<String, Object>();
parameters.put("accountId", accountId);
String query = "select followedOrFollowingPerson FROM Person as user,"
+ " Person AS followedOrFollowingPerson WHERE followedOrFollowingPerson MEMBER OF user." + which
+ " AND user.accountId = :accountId and followedOrFollowingPerson.accountId != :accountId";
// although the line above is just concatenating a query string
// and this implies vulnerability to SQL injection attacks,
// the call to this.getPagedResults() actually parameterizes the SQL
// so there is not actually risk of SQL injection here.
return getTypedPagedResults(start, end, query, parameters);
}
/**
* Returns true if follower/following relationship exists false otherwise.
*
* @param followerAccountId
* The follower person's account Id.
* @param followingAccountId
* The following person's accountId.
* @return True if follower/following relationship exists false otherwise.
*/
@SuppressWarnings("unchecked")
public boolean isFollowing(final String followerAccountId, final String followingAccountId)
{
Query q = getEntityManager().createQuery(
"FROM Person as follower" + " inner join follower.following as following"
+ " where follower.accountId=:followerAccountId and"
+ " following.accountId=:followingAccountId").setParameter("followerAccountId",
followerAccountId).setParameter("followingAccountId", followingAccountId);
List<Person> results = q.getResultList();
return (results.size() != 0);
}
/**
* Finds people with the last name starting with the prefix.
*
* @param prefix
* the first letters in the last name to search by
*
* @return a list of persons with that last name prefix
*/
@SuppressWarnings("unchecked")
public List<Person> findPeopleByPrefix(final String prefix)
{
String[] prefixes = prefix.split(", ");
String lastName = prefixes[0];
String firstName = "";
if (prefixes.length > 1)
{
firstName = prefixes[1];
}
Query q = getEntityManager().createQuery(
"From Person where lower(lastName) LIKE :lprefix and lower(displayName) LIKE :fprefix "
+ "ORDER BY lastName, firstName, middleName").setParameter("lprefix",
lastName.toLowerCase() + "%").setParameter("fprefix", firstName.toLowerCase() + "%");
return q.getResultList();
}
/**
* Finds people based on a list of OpenSocial Ids. This method supports opensocial calls for retrieving people.
*
* @param openSocialIds
* The list of opensocial ids to return Person objects for.
* @return a list of persons that match the list of ids supplied.
*/
@SuppressWarnings("unchecked")
public List<Person> findPeopleByOpenSocialIds(final List<String> openSocialIds)
{
String[] osIds = openSocialIds.toArray(new String[openSocialIds.size()]);
String osIdSet = "";
for (int index = 0; index < osIds.length; index++)
{
osIdSet += "'" + osIds[index] + "'";
if (index < osIds.length - 1)
{
osIdSet += ", ";
}
}
if (logger.isDebugEnabled())
{
logger.debug("Finding people by openSocialIds: " + osIdSet);
}
Query q = getEntityManager().createQuery("From Person where openSocialId IN (:osIds)").setParameter("osIds",
openSocialIds);
List<Person> people = q.getResultList();
if (logger.isDebugEnabled())
{
logger.debug("Retrieved " + people.size() + " results from opensocialids: " + osIdSet);
}
return people;
}
/**
* Finds people being followed based on a list of follower OpenSocial Ids. This method supports opensocial calls for
* retrieving people. This does not exclude their own account.
*
* @param openSocialIds
* The list of opensocial ids to return followed Person objects for.
* @return a list of follower persons that match the list of ids supplied.
*/
@SuppressWarnings("unchecked")
public List<Person> findPeopleFollowedUsingFollowerOpenSocialIds(final List<String> openSocialIds)
{
List<Person> people = findPeopleByOpenSocialIds(openSocialIds);
List<Person> outList = new ArrayList();
for (Person follower : people)
{
outList.addAll(follower.getFollowing());
}
return outList;
}
}