// Copyright 2010 Google Inc. All Rights Reseved.
//
// 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 com.google.testing.testify.risk.frontend.server.service.impl;
import com.google.common.base.Function;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import com.google.testing.testify.risk.frontend.model.AccElementType;
import com.google.testing.testify.risk.frontend.model.AccLabel;
import com.google.testing.testify.risk.frontend.model.Attribute;
import com.google.testing.testify.risk.frontend.model.Capability;
import com.google.testing.testify.risk.frontend.model.Component;
import com.google.testing.testify.risk.frontend.model.HasLabels;
import com.google.testing.testify.risk.frontend.model.Project;
import com.google.testing.testify.risk.frontend.server.service.ProjectService;
import com.google.testing.testify.risk.frontend.server.service.UserService;
import com.google.testing.testify.risk.frontend.server.util.ServletUtils;
import com.google.testing.testify.risk.frontend.shared.rpc.UserRpc.ProjectAccess;
import com.google.testing.testify.risk.frontend.shared.util.StringUtil;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Logger;
import javax.jdo.PersistenceManager;
import javax.jdo.PersistenceManagerFactory;
import javax.jdo.Query;
/**
* This acts as a broker for the client when accessing project information, such as Attributes and
* Components. Any operation will be checked using the user's current credentials, and fail if the
* user doesn't have access to view or edit any project information.
*
* @author chrsmith@google.com (Chris Smith)
*/
@Singleton
public class ProjectServiceImpl implements ProjectService {
private static final Logger log = Logger.getLogger(ProjectServiceImpl.class.getName());
private final PersistenceManagerFactory pmf;
private final UserService userService;
/**
* Creates a new ProjectServiceImpl instance. Internally all methods will use the
* PersistanceManager associated with JDOHelper.getPersistenceManagerFactory (meaning
* jdoconfig.xml).
*/
@Inject
public ProjectServiceImpl(PersistenceManagerFactory pmf, UserService userService) {
this.pmf = pmf;
this.userService = userService;
}
@SuppressWarnings("unchecked")
@Override
public List<Project> query(String query) {
log.info("Querying: " + query);
PersistenceManager pm = pmf.getPersistenceManager();
// TODO(jimr): this currently does not do a query, it just returns all public projects.
Query jdoQuery = pm.newQuery(Project.class);
jdoQuery.setOrdering("projectId asc");
List<Project> results = null;
try {
List<Project> returnedProjects = (List<Project>) jdoQuery.execute();
List<Project> safeToDisplayProjs = Lists.newArrayList();
for (Project returnedProject : returnedProjects) {
if (userService.hasViewAccess(returnedProject)) {
safeToDisplayProjs.add(returnedProject);
}
}
results = ServletUtils.makeGwtSafe(safeToDisplayProjs, pm);
populateCachedAccess(results);
} finally {
pm.close();
}
return results;
}
private void populateCachedAccess(List<Project> projects) {
for (Project p : projects) {
populateCachedAccess(p);
}
}
private void populateCachedAccess(Project p) {
p.setCachedAccessLevel(userService.getAccessLevel(p));
}
/**
* Retrieves the list of projects relevant to the currently logged in user.
*/
@SuppressWarnings("unchecked")
@Override
public List<Project> queryUserProjects() {
if (!userService.isUserLoggedIn()) {
log.info("Querying user projects; user is not logged in.");
return Lists.newArrayList();
}
log.info("Querying user projects.");
PersistenceManager pm = pmf.getPersistenceManager();
Query jdoQuery = pm.newQuery(Project.class);
jdoQuery.setOrdering("projectId asc");
List<Project> results = null;
try {
List<Long> starredProjects = userService.getStarredProjects();
List<Project> returnedProjects = (List<Project>) jdoQuery.execute();
List<Project> projectsToReturn = Lists.newArrayList();
for (Project returnedProject : returnedProjects) {
// Only returns the projects that are either:
// * Starred by the user and have access (implicit or explicit).
// * Granted VIEW or EDIT access explicitly. (Ignore public projects.)
ProjectAccess access = userService.getAccessLevel(returnedProject);
// If explicit, add it. Otherwise, check starred access (and implicit access).
if (access.hasAccess(ProjectAccess.EXPLICIT_VIEW_ACCESS)) {
projectsToReturn.add(returnedProject);
} else if (access.hasAccess(ProjectAccess.VIEW_ACCESS)
&& starredProjects.contains(returnedProject.getProjectId())) {
projectsToReturn.add(returnedProject);
}
}
results = ServletUtils.makeGwtSafe(projectsToReturn, pm);
populateCachedAccess(results);
} finally {
pm.close();
}
return results;
}
/**
* Returns the list of projects the currently logged in user has EDIT access to.
*/
@Override
public List<Project> queryProjectsUserHasEditAccessTo() {
ServletUtils.requireAccess(userService.isUserLoggedIn());
// Start with all starred projects and those granting explicit access.
List<Project> projects = queryUserProjects();
List<Project> projectsToReturn = Lists.newArrayList();
for (Project project : projects) {
if (userService.hasEditAccess(project)) {
projectsToReturn.add(project);
}
}
populateCachedAccess(projectsToReturn);
return projectsToReturn;
}
@Override
public Project getProjectById(long id) {
log.info("Getting project: " + Long.toString(id));
PersistenceManager pm = pmf.getPersistenceManager();
try {
Project retrievedProject = pm.getObjectById(Project.class, id);
// Don't disclose that the project even exists if they don't have view access.
if (retrievedProject != null && userService.hasViewAccess(retrievedProject)) {
retrievedProject = ServletUtils.makeGwtSafe(retrievedProject, pm);
populateCachedAccess(retrievedProject);
return retrievedProject;
}
} finally {
pm.close();
}
return null;
}
@Override
@SuppressWarnings("unchecked")
public Project getProjectByName(String name) {
log.info("Getting project with name: " + name);
PersistenceManager pm = pmf.getPersistenceManager();
Query jdoQuery = pm.newQuery(Project.class);
jdoQuery.declareParameters("String projectNameParam");
jdoQuery.setFilter("name == projectNameParam");
try {
List<Project> returnedProjects = (List<Project>) jdoQuery.execute(name);
log.info(String.format("Found %s projects with name %s", returnedProjects.size(), name));
for (Project target : returnedProjects) {
if (userService.hasViewAccess(target)) {
target = ServletUtils.makeGwtSafe(target, pm);
populateCachedAccess(target);
return target;
}
}
} finally {
pm.close();
}
return null;
}
@Override
public Long createProject(Project projInfo) {
// The user must be logged in to create a project.
ServletUtils.requireAccess(userService.isUserLoggedIn());
log.info("Creating new Project with name: " + projInfo.getName());
if (projInfo.getProjectId() != null) {
throw new IllegalArgumentException(
"You can only create a project with a null ID.");
}
PersistenceManager pm = pmf.getPersistenceManager();
try {
// Automatically add the current user as an OWNER for the project.
projInfo.addProjectOwner(userService.getEmail());
pm.makePersistent(projInfo);
} finally {
pm.close();
}
return projInfo.getProjectId();
}
@Override
public void updateProject(Project projInfo) {
if (projInfo.getProjectId() == null) {
throw new IllegalArgumentException(
"projInfo has not been saved. Please call createProject first.");
}
ServletUtils.requireAccess(userService.hasEditAccess(projInfo.getProjectId()));
log.info("Updating Project: " + projInfo.getProjectId().toString());
PersistenceManager pm = pmf.getPersistenceManager();
Project oldProject;
try {
oldProject = pm.getObjectById(Project.class, projInfo.getProjectId());
List<String> oldOwners = oldProject.getProjectOwners();
List<String> oldEditors = oldProject.getProjectEditors();
List<String> oldViewers = oldProject.getProjectViewers();
// If they're not an owner, keep the old list of owners around.
if (!userService.hasOwnerAccess(projInfo.getProjectId())) {
projInfo.setIsPubliclyVisible(oldProject.getIsPubliclyVisible());
projInfo.setProjectOwners(oldProject.getProjectOwners());
}
pm.makePersistent(projInfo);
log.info("Notifying users of any changes to access level");
String from = userService.getEmail();
List<String> added = StringUtil.subtractList(projInfo.getProjectOwners(),
oldOwners);
if (added.size() > 0) {
ServletUtils.notifyAddedAccess(from, added, "owner", projInfo.getName(),
projInfo.getProjectId().toString());
}
List<String> removed = StringUtil.subtractList(oldOwners, projInfo.getProjectOwners());
if (removed.size() > 0) {
ServletUtils.notifyRemovedAccess(from, removed, "owner", projInfo.getName(),
projInfo.getProjectId().toString());
}
added = StringUtil.subtractList(projInfo.getProjectEditors(), oldEditors);
if (added.size() > 0) {
ServletUtils.notifyAddedAccess(from, added, "editor", projInfo.getName(),
projInfo.getProjectId().toString());
}
removed = StringUtil.subtractList(oldEditors, projInfo.getProjectEditors());
if (removed.size() > 0) {
ServletUtils.notifyRemovedAccess(from, removed, "editor", projInfo.getName(),
projInfo.getProjectId().toString());
}
added = StringUtil.subtractList(projInfo.getProjectViewers(), oldViewers);
if (added.size() > 0) {
ServletUtils.notifyAddedAccess(from, added, "viewer", projInfo.getName(),
projInfo.getProjectId().toString());
}
removed = StringUtil.subtractList(oldViewers, projInfo.getProjectViewers());
if (removed.size() > 0) {
ServletUtils.notifyRemovedAccess(from, removed, "viewer", projInfo.getName(),
projInfo.getProjectId().toString());
}
} finally {
pm.close();
}
}
@Override
public void removeProject(Project projInfo) {
if (projInfo.getProjectId() == null) {
log.info("Attempting to delete unsaved project. Ignoring.");
return;
}
ServletUtils.requireAccess(userService.hasOwnerAccess(projInfo.getProjectId()));
PersistenceManager pm = pmf.getPersistenceManager();
try {
Project projToDelete = pm.getObjectById(Project.class, projInfo.getProjectId());
// TODO(jimr): This delete could succeed but successive removes might fail, leaving dangling
// data. We should retry or do something to assure complete destruction.
// TODO(jimr): Undo?
pm.deletePersistent(projToDelete);
// Delete any child attributes, components, or capabilities.
removeObjectsWithFieldValue(pm, Attribute.class, "parentProjectId", projInfo.getProjectId());
removeObjectsWithFieldValue(pm, Component.class, "parentProjectId", projInfo.getProjectId());
removeObjectsWithFieldValue(pm, Capability.class, "parentProjectId", projInfo.getProjectId());
// TODO(chrsmith): What about enabled data providers? For example, bugs or checkin data
// associated with this project? We need to clear those out as well.
} finally {
pm.close();
}
}
@SuppressWarnings("unchecked")
@Override
public List<AccLabel> getLabels(long projectId) {
ServletUtils.requireAccess(userService.hasViewAccess(projectId));
log.info("Getting labels for project: " + Long.toString(projectId));
PersistenceManager pm = pmf.getPersistenceManager();
Query jdoQuery = pm.newQuery(AccLabel.class);
jdoQuery.setFilter("projectId == projectIdParam");
jdoQuery.declareParameters("Long projectIdParam");
try {
List<AccLabel> labels = (List<AccLabel>) jdoQuery.execute(projectId);
return ServletUtils.makeGwtSafe(labels, pm);
} finally {
pm.close();
}
}
@SuppressWarnings("unchecked")
@Override
public List<Attribute> getProjectAttributes(long projectId) {
ServletUtils.requireAccess(userService.hasViewAccess(projectId));
log.info("Getting Attributes for project: " + Long.toString(projectId));
PersistenceManager pm = pmf.getPersistenceManager();
Query jdoQuery = pm.newQuery(Attribute.class);
jdoQuery.setFilter("parentProjectId == parentProjectParam");
jdoQuery.setOrdering("displayOrder asc");
jdoQuery.declareParameters("Long parentProjectParam");
try {
List<Attribute> returnedAttributes = (List<Attribute>) jdoQuery.execute(projectId);
returnedAttributes = ServletUtils.makeGwtSafe(returnedAttributes, pm);
populateLabels(returnedAttributes, pm);
return returnedAttributes;
} finally {
pm.close();
}
}
/**
* Populates labels for a HasLabels item.
*
* @param <T> An object that HasLabels.
* @param item The item to get labels for.
* @param pm A persistence manager object.
*/
private <T extends HasLabels> void populateLabels(T item, PersistenceManager pm) {
item.setAccLabels(getLabelsForItem(item, pm));
}
/**
* Populates labels for a list of HasLabels items. All items MUST be for the same project
* and of the same type (ie, all Attributes).
*
* IMPORTANT: you probably want to detach your object before calling this.
*
* @param <T> An object that HasLabels.
* @param items The items to get labels for.
* @param pm A persistence manager object.
*/
@SuppressWarnings("unchecked")
private <T extends HasLabels> void populateLabels(List<T> items, PersistenceManager pm) {
if (items.size() > 0) {
Map<Long, T> idToItem = Maps.newHashMap();
for (T item : items) {
idToItem.put(item.getId(), item);
}
T item = items.get(0);
AccElementType type = item.getElementType();
Long parentProjectId = item.getParentProjectId();
Query query = pm.newQuery(AccLabel.class);
query.declareParameters("AccElementType elementTypeParam, Long projectIdParam");
query.setFilter("elementType == elementTypeParam && projectId == projectIdParam");
List<AccLabel> labels = (List<AccLabel>) query.execute(type, parentProjectId);
log.info("Found labels: " + labels.size());
for (AccLabel label : labels) {
item = idToItem.get(label.getElementId());
if (item != null) {
log.info("Putting label where it belongs: " + label.getLabelText());
item.addLabel(label);
}
}
}
}
@SuppressWarnings("unchecked")
private <T extends HasLabels> List<AccLabel> getLabelsForItem(T item, PersistenceManager pm) {
Query query = pm.newQuery(AccLabel.class);
query.declareParameters(
"AccElementType elementTypeParam, Long elementIdParam, Long projectIdParam");
query.setFilter("elementType == elementTypeParam && elementId == elementIdParam && "
+ "projectId == projectIdParam");
List<AccLabel> labels = (List<AccLabel>) query.execute(item.getElementType(), item.getId(),
item.getParentProjectId());
// This constructs a new, real list -- the list returned by the above execution is a lazy-loaded
// streaming query. That doesn't serialize.
return Lists.newArrayList(labels);
}
/**
* Saves labels for an item.
*
* This is not extremely necessary, but for simplicity ('always call persistLabels') it's here.
*
* @param <T> An object that HasLabels.
* @param item The item to save the labels for.
* @param pm A persistence mananger.
*/
private <T extends HasLabels> void persistLabels(T item, PersistenceManager pm) {
List<AccLabel> toDelete = Lists.newArrayList();
Set<String> newLabelKeys = Sets.newHashSet();
for (AccLabel label : item.getAccLabels()) {
if (label.getId() != null) {
newLabelKeys.add(label.getId());
}
}
List<AccLabel> currentLabels = getLabelsForItem(item, pm);
for (AccLabel label : currentLabels) {
if (!newLabelKeys.contains(label.getId())) {
toDelete.add(label);
}
}
pm.deletePersistentAll(toDelete);
// And add the new ones.
pm.makePersistentAll(item.getAccLabels());
}
private <T extends HasLabels> void updateLabelElementIds(T item, Long id) {
for (AccLabel label : item.getAccLabels()) {
label.setElementId(id);
}
}
private <T extends HasLabels> void deleteLabels(T item, PersistenceManager pm) {
pm.deletePersistentAll(item.getAccLabels());
}
@Override
public Long createAttribute(Attribute attribute) {
ServletUtils.requireAccess(userService.hasEditAccess(attribute.getParentProjectId()));
if (attribute.getAttributeId() != null) {
throw new IllegalArgumentException(
"You can only create an attribute with a null ID.");
}
log.info("Creating new Attribute with name: " + attribute.getName());
attribute.setName(attribute.getName().trim());
PersistenceManager pm = pmf.getPersistenceManager();
try {
pm.makePersistent(attribute);
// If we have labels, update their element ID now that we have an ID.
if (attribute.getAccLabels().size() > 0) {
pm.close();
updateLabelElementIds(attribute, attribute.getAttributeId());
pm = pmf.getPersistenceManager();
persistLabels(attribute, pm);
}
} finally {
pm.close();
}
return attribute.getAttributeId();
}
@Override
public Attribute updateAttribute(Attribute attribute) {
if (attribute.getAttributeId() == null) {
throw new IllegalArgumentException(
"attribute has not been saved. Please call createAttribute first.");
}
// Make sure they can edit the project into which they are trying to save.
ServletUtils.requireAccess(userService.hasEditAccess(attribute.getParentProjectId()));
log.info("Updating Attribute: " + attribute.getAttributeId().toString());
PersistenceManager pm = pmf.getPersistenceManager();
try {
Attribute oldAttribute = pm.getObjectById(Attribute.class, attribute.getAttributeId());
if (oldAttribute.getParentProjectId() != attribute.getParentProjectId()) {
log.severe("Possible attack -- attribute sent in and attribute being overwritten had"
+ " different project IDs.");
ServletUtils.requireAccess(false);
}
persistLabels(attribute, pm);
pm.makePersistent(attribute);
attribute = ServletUtils.makeGwtSafe(attribute, pm);
populateLabels(attribute, pm);
attribute.setAccLabels(ServletUtils.makeGwtSafe(attribute.getAccLabels(), pm));
return attribute;
} finally {
pm.close();
}
}
@Override
public void removeAttribute(Attribute attribute) {
if (attribute.getAttributeId() == null) {
log.info("Attempting to delete unsaved Attribute. Ignoring.");
return;
}
log.info("Removing Attribute: " + attribute.getAttributeId().toString());
PersistenceManager pm = pmf.getPersistenceManager();
try {
Attribute attributeToDelete = pm.getObjectById(Attribute.class, attribute.getAttributeId());
ServletUtils.requireAccess(userService.hasEditAccess(attributeToDelete.getParentProjectId()));
pm.deletePersistent(attributeToDelete);
deleteLabels(attribute, pm);
// Delete any child capabilities.
removeObjectsWithFieldValue(pm, Capability.class, "attributeId", attribute.getAttributeId());
} finally {
pm.close();
}
}
@SuppressWarnings("unchecked")
@Override
public void reorderAttributes(long projectId, List<Long> newOrdering) {
log.info("Reordering Attributes for project: " + Long.toString(projectId));
Function<Attribute, Long> getId = new Function<Attribute, Long>() {
@Override
public Long apply(Attribute input) {
return input.getAttributeId();
}
};
Setter<Attribute> setId = new Setter<Attribute>() {
@Override
public boolean apply(Attribute input, long value) {
if (input.getDisplayOrder() != value) {
input.setDisplayOrder(value);
return true;
}
return false;
}
};
setOrder(projectId, Attribute.class, newOrdering, getId, setId);
}
@SuppressWarnings("unchecked")
@Override
public List<Component> getProjectComponents(long projectId) {
ServletUtils.requireAccess(userService.hasViewAccess(projectId));
log.info("Getting Components for project: " + Long.toString(projectId));
PersistenceManager pm = pmf.getPersistenceManager();
Query jdoQuery = pm.newQuery(Component.class);
jdoQuery.setFilter("parentProjectId == parentProjectParam");
jdoQuery.setOrdering("displayOrder asc");
jdoQuery.declareParameters("Long parentProjectParam");
try {
List<Component> returnedComponents = (List<Component>) jdoQuery.execute(projectId);
returnedComponents = ServletUtils.makeGwtSafe(returnedComponents, pm);
populateLabels(returnedComponents, pm);
return returnedComponents;
} finally {
pm.close();
}
}
@Override
public Long createComponent(Component component) {
ServletUtils.requireAccess(userService.hasEditAccess(component.getParentProjectId()));
if (component.getComponentId() != null) {
throw new IllegalArgumentException(
"You can only create a component with a null ID.");
}
log.info("Creating new Component with name: " + component.getName());
component.setName(component.getName().trim());
PersistenceManager pm = pmf.getPersistenceManager();
try {
pm.makePersistent(component);
// If we have labels, update their element ID now that we have an ID.
if (component.getAccLabels().size() > 0) {
pm.close();
updateLabelElementIds(component, component.getId());
pm = pmf.getPersistenceManager();
persistLabels(component, pm);
}
} finally {
pm.close();
}
return component.getComponentId();
}
@Override
public Component updateComponent(Component component) {
if (component.getComponentId() == null) {
throw new IllegalArgumentException(
"component has not been saved. Please call createComponent first.");
}
// Make sure they can edit the project into which they are trying to save.
ServletUtils.requireAccess(userService.hasEditAccess(component.getParentProjectId()));
log.info("Updating Component: " + component.getComponentId().toString());
PersistenceManager pm = pmf.getPersistenceManager();
try {
Component oldComponent = pm.getObjectById(Component.class, component.getComponentId());
if (oldComponent.getParentProjectId() != component.getParentProjectId()) {
log.severe("Possible attack -- component sent in and component being overwritten had"
+ " different project IDs.");
ServletUtils.requireAccess(false);
}
persistLabels(component, pm);
pm.makePersistent(component);
component = ServletUtils.makeGwtSafe(component, pm);
populateLabels(component, pm);
component.setAccLabels(ServletUtils.makeGwtSafe(component.getAccLabels(), pm));
return component;
} finally {
pm.close();
}
}
@Override
public void removeComponent(Component component) {
if (component.getComponentId() == null) {
log.info("Attempting to delete unsaved Component. Ignoring.");
return;
}
log.info("Removing Component: " + component.getComponentId().toString());
PersistenceManager pm = pmf.getPersistenceManager();
try {
Component componentToDelete = pm.getObjectById(Component.class, component.getComponentId());
ServletUtils.requireAccess(userService.hasEditAccess(componentToDelete.getParentProjectId()));
pm.deletePersistent(componentToDelete);
deleteLabels(componentToDelete, pm);
// Delete any child capabilities.
removeObjectsWithFieldValue(pm, Capability.class, "componentId", component.getComponentId());
} finally {
pm.close();
}
}
@SuppressWarnings("unchecked")
private <T> void setOrder(long projectId, Class<T> clazz, List<Long> order,
Function<T, Long> getId, Setter<T> applyOrder) {
log.info("setOrder executing with " + order.size() + " items passed in.");
ServletUtils.requireAccess(userService.hasEditAccess(projectId));
PersistenceManager pm = pmf.getPersistenceManager();
try {
Query jdoQuery = pm.newQuery(clazz);
jdoQuery.setFilter("parentProjectId == parentProjectParam");
jdoQuery.declareParameters("Long parentProjectParam");
List<T> items = (List<T>) jdoQuery.execute(projectId);
Map<Long, Integer> lookup = Maps.newHashMap();
for (int i = 0; i < order.size(); i++) {
Long id = order.get(i);
lookup.put(id, i);
}
for (T item : items) {
Long id = getId.apply(item);
Integer newIndex = lookup.get(id);
if (newIndex != null) {
if (applyOrder.apply(item, newIndex)) {
pm.makePersistent(item);
}
} else {
log.warning("Project contains item not covered in new ordering - ID: " + id);
}
}
} finally {
pm.close();
}
log.info("setOrder complete");
}
@Override
public void reorderCapabilities(long projectId, List<Long> newOrdering) {
log.info("Reordering capabilities for project: " + Long.toString(projectId));
Function<Capability, Long> getId = new Function<Capability, Long>() {
@Override
public Long apply(Capability input) {
return input.getCapabilityId();
}
};
Setter<Capability> setId = new Setter<Capability>() {
@Override
public boolean apply(Capability input, long value) {
if (input.getDisplayOrder() != value) {
input.setDisplayOrder(value);
return true;
}
return false;
}
};
setOrder(projectId, Capability.class, newOrdering, getId, setId);
}
@SuppressWarnings("unchecked")
@Override
public void reorderComponents(long projectId, List<Long> newOrdering) {
log.info("Reordering Components for project: " + Long.toString(projectId));
Function<Component, Long> getId = new Function<Component, Long>() {
@Override
public Long apply(Component input) {
return input.getComponentId();
}
};
Setter<Component> setId = new Setter<Component>() {
@Override
public boolean apply(Component input, long value) {
if (input.getDisplayOrder() != value) {
input.setDisplayOrder(value);
return true;
}
return false;
}
};
setOrder(projectId, Component.class, newOrdering, getId, setId);
}
private interface Setter<F> {
boolean apply(F input, long value);
}
@Override
@SuppressWarnings("unchecked")
public Capability getCapabilityById(long projectId, long capabilityId) {
ServletUtils.requireAccess(userService.hasViewAccess(projectId));
log.info("Getting capability for project by id: " + Long.toString(projectId) + ", "
+ Long.toString(capabilityId));
PersistenceManager pm = pmf.getPersistenceManager();
Query jdoQuery = pm.newQuery(Capability.class);
jdoQuery.setFilter(
"parentProjectId == parentProjectParam && capabilityId == capabilityIdParam");
jdoQuery.declareParameters("Long parentProjectParam, Long capabilityIdParam");
try {
List<Capability> results = (List<Capability>) jdoQuery.execute(
projectId, capabilityId);
if (results.size() > 0) {
Capability c = results.get(0);
c = ServletUtils.makeGwtSafe(c, pm);
populateLabels(c, pm);
return c;
} else {
return null;
}
} finally {
pm.close();
}
}
@SuppressWarnings("unchecked")
@Override
public List<Capability> getProjectCapabilities(long projectId) {
ServletUtils.requireAccess(userService.hasViewAccess(projectId));
log.info("Getting Capabilities for project: " + Long.toString(projectId));
PersistenceManager pm = pmf.getPersistenceManager();
Query jdoQuery = pm.newQuery(Capability.class);
jdoQuery.setFilter("parentProjectId == parentProjectParam");
jdoQuery.setOrdering("displayOrder asc");
jdoQuery.declareParameters("Long parentProjectParam");
try {
List<Capability> returnedCapabilities = (List<Capability>) jdoQuery.execute(projectId);
returnedCapabilities = ServletUtils.makeGwtSafe(returnedCapabilities, pm);
populateLabels(returnedCapabilities, pm);
return returnedCapabilities;
} finally {
pm.close();
}
}
@Override
public Capability createCapability(Capability capability) {
ServletUtils.requireAccess(userService.hasEditAccess(capability.getParentProjectId()));
if (capability.getCapabilityId() != null) {
throw new IllegalArgumentException(
"You can only create a capability with a null ID.");
}
log.info("Creating new Capability with name: " + capability.getName());
capability.setName(capability.getName().trim());
PersistenceManager pm = pmf.getPersistenceManager();
try {
pm.makePersistent(capability);
capability = ServletUtils.makeGwtSafe(capability, pm);
// If we have labels, update their element ID now that we have an ID.
if (capability.getAccLabels().size() > 0) {
pm.close();
updateLabelElementIds(capability, capability.getId());
pm = pmf.getPersistenceManager();
persistLabels(capability, pm);
}
return capability;
} finally {
pm.close();
}
}
@Override
public void updateCapability(Capability capability) {
if (capability.getCapabilityId() == null) {
throw new IllegalArgumentException(
"Capability has not been saved. Please call createCapability first.");
}
// Make sure they can edit the project into which they are trying to save.
ServletUtils.requireAccess(userService.hasEditAccess(capability.getParentProjectId()));
log.info("Updating capability: " + capability.getCapabilityId().toString());
PersistenceManager pm = pmf.getPersistenceManager();
try {
Capability oldCapability = pm.getObjectById(Capability.class, capability.getCapabilityId());
if (oldCapability.getParentProjectId() != capability.getParentProjectId()) {
log.severe("Possible attack -- capability sent in and capability being overwritten had"
+ " different project IDs.");
ServletUtils.requireAccess(false);
}
pm.makePersistent(capability);
persistLabels(capability, pm);
} finally {
pm.close();
}
}
@Override
public void removeCapability(Capability capability) {
if (capability.getCapabilityId() == null) {
log.info("Attempting to delete unsaved Component. Ignoring.");
return;
}
log.info("Removing Capability: " + capability.getCapabilityId().toString());
PersistenceManager pm = pmf.getPersistenceManager();
try {
Capability capabilityToDelete = pm.getObjectById(
Capability.class, capability.getCapabilityId());
ServletUtils.requireAccess(userService.hasEditAccess(
capabilityToDelete.getParentProjectId()));
pm.deletePersistent(capabilityToDelete);
deleteLabels(capabilityToDelete, pm);
} finally {
pm.close();
}
}
/**
* Deletes all stored objects of the given type with the field matching the specified ID.
* Primarily this is used for deleting dependent, child objects when the parent is removed.
*
* @param pm the PersistenceManager used to perform the deletion operation.
* @param type the type of the object to be removed.
* @param fieldName the name on the object to check.
* @param fieldValue the value which must match in order for the object to get deleted.
*/
private <T> void removeObjectsWithFieldValue(
PersistenceManager pm, Class<T> type, String fieldName, long fieldValue)
throws IllegalArgumentException {
// Do a brief sanity check on the field name.
fieldName = fieldName.trim();
if ((fieldName.contains(" ")) || (fieldName.contains(";"))) {
throw new IllegalArgumentException();
}
StringBuilder message = new StringBuilder();
message.append("Deleting all ");
message.append(type.getCanonicalName());
message.append(" with '");
message.append(fieldName);
message.append("' equal to ");
message.append(Long.toString(fieldValue));
log.info(message.toString());
Query objectsToDeleteQuery = pm.newQuery(type);
objectsToDeleteQuery.setFilter(fieldName + " == fieldValueParam");
objectsToDeleteQuery.declareParameters("int fieldValueParam");
objectsToDeleteQuery.deletePersistentAll(fieldValue);
}
}