/*
* RHQ Management Platform
* Copyright (C) 2005-2013 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
package org.rhq.enterprise.server.rest;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import javax.ejb.EJB;
import javax.ejb.Stateless;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import javax.interceptor.Interceptors;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.GenericEntity;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriInfo;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jboss.resteasy.annotations.GZIP;
import org.jboss.resteasy.annotations.cache.Cache;
import org.rhq.core.domain.criteria.ResourceGroupCriteria;
import org.rhq.core.domain.criteria.ResourceGroupDefinitionCriteria;
import org.rhq.core.domain.measurement.DataType;
import org.rhq.core.domain.measurement.MeasurementDefinition;
import org.rhq.core.domain.resource.Resource;
import org.rhq.core.domain.resource.ResourceType;
import org.rhq.core.domain.resource.group.GroupDefinition;
import org.rhq.core.domain.resource.group.ResourceGroup;
import org.rhq.core.domain.util.PageList;
import org.rhq.core.domain.util.PageOrdering;
import org.rhq.enterprise.server.resource.ResourceManagerLocal;
import org.rhq.enterprise.server.resource.ResourceTypeManagerLocal;
import org.rhq.enterprise.server.resource.ResourceTypeNotFoundException;
import org.rhq.enterprise.server.resource.group.ResourceGroupAlreadyExistsException;
import org.rhq.enterprise.server.resource.group.ResourceGroupDeleteException;
import org.rhq.enterprise.server.resource.group.ResourceGroupNotFoundException;
import org.rhq.enterprise.server.resource.group.definition.GroupDefinitionManagerLocal;
import org.rhq.enterprise.server.resource.group.definition.exception.GroupDefinitionAlreadyExistsException;
import org.rhq.enterprise.server.resource.group.definition.exception.GroupDefinitionCreateException;
import org.rhq.enterprise.server.resource.group.definition.exception.GroupDefinitionDeleteException;
import org.rhq.enterprise.server.resource.group.definition.exception.GroupDefinitionNotFoundException;
import org.rhq.enterprise.server.rest.domain.GroupDefinitionRest;
import org.rhq.enterprise.server.rest.domain.GroupRest;
import org.rhq.enterprise.server.rest.domain.Link;
import org.rhq.enterprise.server.rest.domain.MetricSchedule;
import org.rhq.enterprise.server.rest.domain.ResourceWithType;
import com.wordnik.swagger.annotations.Api;
import com.wordnik.swagger.annotations.ApiError;
import com.wordnik.swagger.annotations.ApiErrors;
import com.wordnik.swagger.annotations.ApiOperation;
import com.wordnik.swagger.annotations.ApiParam;
/**
* Deal with group related things.
* @author Heiko W. Rupp
*/
@Stateless
@Interceptors(SetCallerInterceptor.class)
@Path("/group")
@Api(value="Deal with groups and DynaGroups", description = "Api that deals with resource groups and group definitions")
public class GroupHandlerBean extends AbstractRestBean {
private final Log log = LogFactory.getLog(GroupHandlerBean.class);
@EJB
ResourceManagerLocal resourceManager;
@EJB
ResourceTypeManagerLocal resourceTypeManager;
@EJB
GroupDefinitionManagerLocal definitionManager;
@GZIP
@GET
@Path("/")
@ApiOperation(value = "List all groups", multiValueResponse = true, responseClass = "GroupRest")
@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
public Response getGroups(@ApiParam("String to search in the group name") @QueryParam("q") String q,
@ApiParam("Page size for paging") @QueryParam("ps") @DefaultValue("20") int pageSize,
@ApiParam("Page number for paging, 0-based") @QueryParam("page") Integer page,
@Context HttpHeaders headers, @Context UriInfo uriInfo) {
ResourceGroupCriteria criteria = new ResourceGroupCriteria();
criteria.fetchGroupDefinition(true);
criteria.addSortId(PageOrdering.ASC);
if (q!=null) {
criteria.addFilterName(q);
}
if (page!=null) {
criteria.setPaging(page,pageSize);
}
PageList<ResourceGroup> groups = resourceGroupManager.findResourceGroupsByCriteria(caller, criteria);
List<GroupRest> list = new ArrayList<GroupRest>();
for (ResourceGroup group : groups) {
list.add(fillGroup(group, uriInfo));
}
MediaType mediaType = headers.getAcceptableMediaTypes().get(0);
Response.ResponseBuilder builder = Response.ok();
builder.type(mediaType);
if (mediaType.equals(MediaType.TEXT_HTML_TYPE)) {
builder.entity(renderTemplate("listGroup", list));
}
else if (mediaType.equals(wrappedCollectionJsonType)) {
wrapForPaging(builder,uriInfo,groups,list);
}
else {
GenericEntity<List<GroupRest>> ret = new GenericEntity<List<GroupRest>>(list) {};
builder.entity(ret);
createPagingHeader(builder,uriInfo,groups);
}
return builder.build();
}
@Cache(isPrivate = true,maxAge = 60)
@GET
@Path("{id}")
@ApiOperation(value = "Get the group with the passed id")
@ApiError(code = 404, reason = "Group with passed id not found")
public Response getGroup(@ApiParam(value = "Id of the group") @PathParam("id") int id,
@Context HttpHeaders headers,
@Context UriInfo uriInfo) {
ResourceGroup group = fetchGroup(id, false);
GroupRest groupRest = fillGroup(group, uriInfo);
MediaType mediaType = headers.getAcceptableMediaTypes().get(0);
Response.ResponseBuilder builder = Response.ok();
builder.type(mediaType);
if (mediaType.equals(MediaType.TEXT_HTML_TYPE)) {
builder.entity(renderTemplate("group", groupRest));
}
else {
builder.entity(groupRest);
}
return builder.build();
}
@POST
@Path("/")
@ApiOperation(value = "Create a new group")
@ApiErrors({
@ApiError(code = 404, reason = "Resource type for provided type id does not exist"),
@ApiError(code = 406, reason = "No group provided"),
@ApiError(code = 406, reason = "Provided group has no name")
})
public Response createGroup(
@ApiParam(value = "A GroupRest object containing at least a name for the group") GroupRest group,
@Context HttpHeaders headers, @Context UriInfo uriInfo) {
if (group==null)
throw new BadArgumentException("A group must be provided");
if (group.getName()==null)
throw new BadArgumentException("A group name is required");
ResourceGroup newGroup = new ResourceGroup(group.getName());
if (group.getResourceTypeId()!=null) {
ResourceType resourceType = null;
try {
resourceType = resourceTypeManager.getResourceTypeById(caller,group.getResourceTypeId());
newGroup.setResourceType(resourceType);
} catch (ResourceTypeNotFoundException e) {
throw new StuffNotFoundException("ResourceType with id " + group.getResourceTypeId());
}
}
MediaType mediaType = headers.getAcceptableMediaTypes().get(0);
Response.ResponseBuilder builder;
UriBuilder uriBuilder = uriInfo.getBaseUriBuilder();
uriBuilder.path("/group/{id}");
try {
newGroup = resourceGroupManager.createResourceGroup(caller, newGroup);
URI uri = uriBuilder.build(newGroup.getId());
builder=Response.created(uri);
} catch (ResourceGroupAlreadyExistsException e) {
ResourceGroupCriteria criteria = new ResourceGroupCriteria();
criteria.setStrict(true);
criteria.addFilterName(newGroup.getName());
// TODO also case sensitive?
List<ResourceGroup> groups = resourceGroupManager.findResourceGroupsByCriteria(caller,criteria);
newGroup = groups.get(0);
URI uri = uriBuilder.build(newGroup.getId());
builder=Response.ok(uri);
} catch (Exception e) {
builder=Response.status(Response.Status.NOT_ACCEPTABLE);
builder.type(mediaType);
builder.entity(e.getCause());
}
builder.type(mediaType);
builder.entity(fillGroup(newGroup,uriInfo));
putToCache(newGroup.getId(),ResourceGroup.class,newGroup);
return builder.build();
}
@PUT
@Path("{id}")
@ApiOperation(value = "Update the passed group. Currently only name change is supported")
@ApiErrors({
@ApiError(code = 404, reason = "Group with the passed id does not exist"),
@ApiError(code = 406, reason = "Updating the name failed")
})
public Response updateGroup(@ApiParam(value = "Id of the group to update") @PathParam("id") int id,
@ApiParam(value = "New version of the group") GroupRest in,
@Context HttpHeaders headers,
@Context UriInfo uriInfo) {
ResourceGroup resourceGroup = fetchGroup(id, false);
resourceGroup.setName(in.getName());
Response.ResponseBuilder builder;
try {
resourceGroup = resourceGroupManager.updateResourceGroup(caller,resourceGroup);
builder=Response.ok(fillGroup(resourceGroup,uriInfo));
putToCache(resourceGroup.getId(),ResourceGroup.class,resourceGroup);
}
catch (Exception e) {
builder = Response.status(Response.Status.NOT_ACCEPTABLE);
}
MediaType mediaType = headers.getAcceptableMediaTypes().get(0);
builder.type(mediaType);
return builder.build();
}
@DELETE
@Path("{id}")
@ApiOperation(value="Delete the group with the passed id", notes = "This operation is by default idempotent, returning 204." +
"If you want to check if the group existed at all, you need to pass the 'validate' query parameter.")
@ApiErrors({
@ApiError(code = 204, reason = "Group was deleted or did not exist with validation not set"),
@ApiError(code = 404, reason = "Group did not exist and validate was set")
})
public Response deleteGroup(@ApiParam("Id of the group to delete") @PathParam("id") int id,
@ApiParam("Validate if the group exists") @QueryParam("validate") @DefaultValue("false") boolean validate) {
Response.ResponseBuilder builder;
try {
resourceGroupManager.deleteResourceGroup(caller,id);
removeFromCache(id,ResourceGroup.class);
builder = Response.noContent();
} catch (ResourceGroupNotFoundException e) {
if (validate) {
builder = Response.status(Response.Status.NOT_FOUND);
} else {
builder = Response.noContent();
}
} catch (ResourceGroupDeleteException e) {
builder = Response.serverError();
builder.entity(e.getMessage());
}
return builder.build();
}
@GZIP
@GET
@Path("{id}/resources")
@Cache(isPrivate = true,maxAge = 60)
@ApiOperation(value="Get the resources of the group", multiValueResponse = true, responseClass = "ResourceWithType")
@ApiError(code = 404, reason = "Group with passed id does not exist")
public Response getResources(@ApiParam("Id of the group to retrieve the resources for") @PathParam("id") int id,
@Context HttpHeaders headers,
@Context UriInfo uriInfo) {
ResourceGroup resourceGroup = fetchGroup(id, false);
Set<Resource> resources = resourceGroup.getExplicitResources();
List<ResourceWithType> rwtList = new ArrayList<ResourceWithType>(resources.size());
for (Resource res: resources) {
rwtList.add(fillRWT(res,uriInfo));
}
MediaType mediaType = headers.getAcceptableMediaTypes().get(0);
Response.ResponseBuilder builder;
if (mediaType.equals(MediaType.TEXT_HTML_TYPE)) {
builder = Response.ok(renderTemplate("listResourceWithType", rwtList), mediaType);
}
else {
GenericEntity<List<ResourceWithType>> list = new GenericEntity<List<ResourceWithType>>(rwtList){};
builder = Response.ok(list);
}
return builder.build();
}
@PUT
@Path("{id}/resource/{resourceId}")
@ApiOperation(value="Add a resource to an existing group", notes = "If you have created the group as " +
"a compatible group and a resource type was provided on creation, only resources with this type" +
"may be added.")
@ApiErrors({
@ApiError(code = 404,reason = "If there is no resource or group with the passed id "),
@ApiError(code = 409,reason =" Resource type does not match the group one")
})
public Response addResource(@ApiParam("Id of the existing group") @PathParam("id") int id,
@ApiParam("Id of the resource to add") @PathParam("resourceId") int resourceId,
@Context HttpHeaders headers, @Context UriInfo uriInfo) {
MediaType mediaType = headers.getAcceptableMediaTypes().get(0);
ResourceGroup resourceGroup = fetchGroup(id, false);
Resource res = resourceManager.getResource(caller,resourceId);
if (res==null)
throw new StuffNotFoundException("Resource with id " + resourceId);
// A resource type is set for the group, so only allow to add resources with the same type.
if (resourceGroup.getResourceType()!=null) {
if (!res.getResourceType().equals(resourceGroup.getResourceType())) {
Response.ResponseBuilder status = Response.status(Response.Status.CONFLICT);
status.type(mediaType);
return status.build();
}
}
// TODO if comp group and no resourceTypeId set, shall we allow to have it change to a mixed group?
resourceGroupManager.addResourcesToGroup(caller,id,new int[]{resourceId});
resourceGroup = fetchGroup(id, false);
GroupRest gr = fillGroup(resourceGroup,uriInfo);
Response.ResponseBuilder builder = Response.ok();
builder.entity(gr);
builder.type(mediaType);
return builder.build();
}
@DELETE
@Path("{id}/resource/{resourceId}")
@ApiOperation(value = "Remove the resource with the passed id from the group", notes = "This operation is by default idempotent, returning 204" +
"even if the resource was not member of the group." +
"If you want to check if the resource existed at all, you need to pass the 'validate' query parameter.")
@ApiErrors({
@ApiError(code = 404, reason = "Group with the passed id does not exist"),
@ApiError(code = 404, reason = "Resource with the passed id does not exist"),
@ApiError(code = 204, reason = "Resource was removed from the group or was no member and validation was not set"),
@ApiError(code = 404, reason = "Resource was no member of the group and validate was set")
})
public Response removeResource(@ApiParam("Id of the existing group") @PathParam("id") int id,
@ApiParam("Id of the resource to remove") @PathParam("resourceId") int resourceId,
@ApiParam("Validate if the resource exists in the group") @QueryParam(
"validate") @DefaultValue("false") boolean validate) {
ResourceGroup resourceGroup = fetchGroup(id, false);
Resource res = resourceManager.getResource(caller, resourceId);
if (res==null)
throw new StuffNotFoundException("Resource with id " + resourceId);
boolean removed = resourceGroup.removeExplicitResource(res);
if (!removed && validate) {
throw new StuffNotFoundException("Resource " + resourceId + " in group " + id);
}
return Response.noContent().build();
}
@GET
@GZIP
@Path("{id}/metricDefinitions")
@ApiOperation(value = "Get the metric definitions for the compatible group with the passed id")
@ApiError(code = 404, reason = "Group with the passed id does not exist")
public Response getMetricDefinitionsForGroup(@ApiParam(value = "Id of the group") @PathParam("id") int id,
@Context HttpHeaders headers,
@Context UriInfo uriInfo) {
ResourceGroup group = fetchGroup(id, true);
Set<MeasurementDefinition> definitions = group.getResourceType().getMetricDefinitions();
List<MetricSchedule> schedules = new ArrayList<MetricSchedule>(definitions.size());
for (MeasurementDefinition def : definitions) {
MetricSchedule schedule = new MetricSchedule(def.getId(),def.getName(),def.getDisplayName(),false,def.getDefaultInterval(),
def.getUnits().getName(),def.getDataType().toString());
schedule.setDefinitionId(def.getId());
if (def.getDataType()== DataType.MEASUREMENT) {
UriBuilder uriBuilder = uriInfo.getBaseUriBuilder();
uriBuilder.path("/metric/data/group/{groupId}/{definitionId}");
URI uri = uriBuilder.build(id,def.getId());
Link link = new Link("metric",uri.toString());
schedule.addLink(link);
}
schedules.add(schedule);
}
MediaType mediaType = headers.getAcceptableMediaTypes().get(0);
Response.ResponseBuilder builder;
if (mediaType.equals(MediaType.TEXT_HTML_TYPE)) {
builder = Response.ok(renderTemplate("listMetricDefinitions",schedules));
}
else {
GenericEntity<List<MetricSchedule>> ret = new GenericEntity<List<MetricSchedule>>(schedules) {};
builder = Response.ok(ret);
}
return builder.build();
}
@GZIP
@GET
@Path("/definitions")
@ApiOperation(value="List all existing GroupDefinitions",multiValueResponse = true, responseClass = "GroupDefinitionRest")
public Response getGroupDefinitions(
@ApiParam("String to search in the group definition name") @QueryParam("q") String q,
@Context HttpHeaders headers,
@Context UriInfo uriInfo) {
ResourceGroupDefinitionCriteria criteria = new ResourceGroupDefinitionCriteria();
if (q!=null) {
criteria.addFilterName(q);
}
PageList<GroupDefinition> gdlist = definitionManager.findGroupDefinitionsByCriteria(caller, criteria);
List<GroupDefinitionRest> list = new ArrayList<GroupDefinitionRest>();
for (GroupDefinition def: gdlist) {
GroupDefinitionRest definitionRest = buildGDRestFromDefinition(def);
createLinksForGDRest(uriInfo,definitionRest);
list.add(definitionRest);
}
MediaType mediaType = headers.getAcceptableMediaTypes().get(0);
Response.ResponseBuilder builder;
if (mediaType.equals(MediaType.TEXT_HTML_TYPE)) {
builder = Response.ok(renderTemplate("listGroupDefinition", list), mediaType);
}
else {
GenericEntity<List<GroupDefinitionRest>> ret = new GenericEntity<List<GroupDefinitionRest>>(list) {
};
builder = Response.ok(ret);
}
return builder.build();
}
@GZIP
@GET
@Path("/definition/{id}")
@Cache(isPrivate = true,maxAge = 60)
@ApiOperation(value = "Retrieve a single GroupDefinition by id", responseClass = "GroupDefinitionRest")
@ApiError(code = 404, reason = "Group definition with the passed id does not exist.")
public Response getGroupDefinition(
@ApiParam("The id of the definition to retrieve") @PathParam("id") int definitionId,
@Context HttpHeaders headers, @Context UriInfo uriInfo) {
try {
GroupDefinition def = definitionManager.getById(definitionId);
GroupDefinitionRest gdr = buildGDRestFromDefinition(def);
createLinksForGDRest(uriInfo,gdr);
MediaType mediaType = headers.getAcceptableMediaTypes().get(0);
Response.ResponseBuilder builder;
if (mediaType.equals(MediaType.TEXT_HTML_TYPE)) {
builder = Response.ok(renderTemplate("groupDefinition", gdr), mediaType);
}
else {
builder= Response.ok(gdr);
}
return builder.build();
} catch (GroupDefinitionNotFoundException e) {
throw new StuffNotFoundException("Group definition with id " + definitionId);
}
}
private GroupDefinitionRest buildGDRestFromDefinition(GroupDefinition def) {
GroupDefinitionRest gdr = new GroupDefinitionRest(def.getId(),def.getName(),def.getDescription(), def.getRecalculationInterval());
gdr.setRecursive(def.isRecursive());
List<Integer> generatedGroups;
if (def.getManagedResourceGroups()!=null) {
generatedGroups = new ArrayList<Integer>(def.getManagedResourceGroups().size());
for (ResourceGroup group : def.getManagedResourceGroups() ) {
generatedGroups.add(group.getId());
}
} else {
generatedGroups = Collections.emptyList();
}
gdr.setGeneratedGroupIds(generatedGroups);
gdr.setExpression(def.getExpressionAsList());
return gdr;
}
@DELETE
@Path("/definition/{id}")
@ApiOperation(value = "Delete the GroupDefinition with the passed id", notes = "This operation is by default idempotent, returning 204." +
"If you want to check if the definition existed at all, you need to pass the 'validate' query parameter.")
@ApiErrors({
@ApiError(code = 204, reason = "Definition was deleted or did not exist with validation not set"),
@ApiError(code = 404, reason = "Definition did not exist and validate was set")
})
public Response deleteGroupDefinition(
@ApiParam("The id of the definition to delete") @PathParam("id") int definitionId,
@ApiParam("Validate if the definition exists") @QueryParam("validate") @DefaultValue("false") boolean validate,
@Context HttpHeaders headers) {
Response.ResponseBuilder builder;
try {
GroupDefinition def = definitionManager.getById(definitionId);
definitionManager.removeGroupDefinition(caller,definitionId);
builder = Response.noContent();
} catch (GroupDefinitionNotFoundException e) {
if (validate) {
builder = Response.status(Response.Status.NOT_FOUND);
builder.entity("Definition with id " + definitionId);
}
else {
builder = Response.noContent();
}
} catch (GroupDefinitionDeleteException e) {
builder = Response.status(Response.Status.INTERNAL_SERVER_ERROR);
builder.entity(e.getMessage());
}
MediaType type = headers.getAcceptableMediaTypes().get(0);
builder.type(type);
return builder.build();
}
@POST
@Path("/definitions")
@Consumes({MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML})
@ApiOperation(
value = "Create a new GroupDefinition.", notes = "The name of the group is required in the passed " +
"definition, as well as a non-empty expression. A recalcInterval of 0 means to never recalculate.")
@ApiErrors({
@ApiError(code = 406, reason = "Passed group definition has no name"),
@ApiError(code = 406, reason = "Passed expression was empty"),
@ApiError(code = 406, reason = "Recalculation interval is < 0 "),
@ApiError(code = 409, reason = "There already exists a definition by this name"),
@ApiError(code = 406, reason = "Group creation failed")
})
public Response createGroupDefinition(GroupDefinitionRest definition,
@Context HttpHeaders headers,
@Context UriInfo uriInfo) {
Response.ResponseBuilder builder = null;
if (definition.getName()==null||definition.getName().trim().isEmpty()) {
builder = Response.status(Response.Status.NOT_ACCEPTABLE);
builder.entity("No name for the definition given");
}
if (builder!=null)
return builder.build();
GroupDefinition gd = new GroupDefinition(definition.getName());
gd.setDescription(definition.getDescription());
addGroupDefinitionExpression(definition, gd);
if (definition.getRecalcInterval() < 0 ) {
throw new BadArgumentException("Recalculation interval must be >= 0");
}
gd.setRecalculationInterval(definition.getRecalcInterval());
gd.setRecursive(definition.isRecursive());
try {
GroupDefinition res = definitionManager.createGroupDefinition(caller,gd);
UriBuilder uriBuilder = uriInfo.getBaseUriBuilder();
uriBuilder.path("/group/definition/{id}");
URI location = uriBuilder.build(res.getId());
Link link = new Link("edit",location.toString());
builder= Response.created(location);
GroupDefinitionRest gdr = buildGDRestFromDefinition(res);
createLinksForGDRest(uriInfo,gdr);
builder.entity(gdr);
} catch (GroupDefinitionAlreadyExistsException e) {
builder =Response.status(Response.Status.CONFLICT);
builder.entity(e.getMessage());
} catch (GroupDefinitionCreateException e) {
e.printStackTrace(); // TODO: Customise this generated block
builder = Response.status(Response.Status.NOT_ACCEPTABLE);
builder.entity(e.getMessage());
}
return builder.build();
}
private void addGroupDefinitionExpression(GroupDefinitionRest definition, GroupDefinition gd) {
boolean isEmpty = false;
List<String> expressionList = definition.getExpression();
if (expressionList.isEmpty()) {
isEmpty=true;
}
StringBuilder sb = new StringBuilder();
int countEmpty = 0;
for(String e : expressionList ) {
if (e==null) {
countEmpty++;
continue;
}
sb.append(e);
if (e.trim().isEmpty()) {
countEmpty++;
}
sb.append("\n");
}
if (countEmpty == expressionList.size()) {
isEmpty = true;
}
if (isEmpty) {
throw new BadArgumentException("The expression must not be empty");
}
gd.setExpression(sb.toString());
}
@PUT
@Path("/definition/{id}")
@Consumes({MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML})
@ApiOperation(value = "Update or recalculate an existing GroupDefinition",
notes = "If the query param 'recalculate' is set to true, the group with the passed id is recalculated. " +
"Otherwise the existing group will be updated with the passed definition. The expression in the " +
"definition must be empty. If the name is emtpy, the old name is kept. A recalcInterval" +
"of 0 means no recalculation.")
@ApiErrors({
@ApiError(code = 404, reason = "Group with the passed id does not exist"),
@ApiError(code = 406, reason = "Passed expression was empty"),
@ApiError(code = 406, reason = "Recalculation interval is < 0 "),
@ApiError(code = 406, reason = "Group membership calculation failed")
})
public Response updateGroupDefinition(@ApiParam("Id fo the definition to update") @PathParam("id") int definitionId,
@ApiParam("If true, trigger a re-calculation") @QueryParam( "recalculate")
@DefaultValue("false") boolean recalculate,
GroupDefinitionRest definition, // TODO mark as optional?
@Context HttpHeaders headers,
@Context UriInfo uriInfo) {
GroupDefinition gd;
try {
gd = definitionManager.getById(definitionId);
} catch (GroupDefinitionNotFoundException e) {
throw new StuffNotFoundException("Group Definition with id " + definitionId);
}
Response.ResponseBuilder builder = null;
if (recalculate) {
try {
definitionManager.calculateGroupMembership(caller,gd.getId());
builder = Response.noContent();
} catch (Exception e) {
builder = Response.status(Response.Status.NOT_ACCEPTABLE);
builder.entity(e.getLocalizedMessage());
}
return builder.build();
}
// Not recalculation, but an update
if (!definition.getName().isEmpty()) {
gd.setName(definition.getName());
}
gd.setDescription(definition.getDescription());
addGroupDefinitionExpression(definition,gd);
if (definition.getRecalcInterval() < 0 ) {
throw new BadArgumentException("Recalculation interval must be >= 0");
}
gd.setRecalculationInterval(definition.getRecalcInterval());
gd.setRecursive(definition.isRecursive());
try {
definitionManager.updateGroupDefinition(caller,gd);
} catch (Exception e) {
builder = Response.status(Response.Status.NOT_ACCEPTABLE);
builder.entity(e.getLocalizedMessage());
return builder.build();
}
try {
// Re-fetch, as groups may have changed
gd = definitionManager.getById(gd.getId());
GroupDefinitionRest gdr = buildGDRestFromDefinition(gd);
createLinksForGDRest(uriInfo, gdr);
builder = Response.ok(gdr);
} catch (GroupDefinitionNotFoundException e) {
builder = Response.status(Response.Status.INTERNAL_SERVER_ERROR);
builder.entity("Group Definition with id " + gd.getId());
}
MediaType mediaType = headers.getAcceptableMediaTypes().get(0);
builder.type(mediaType);
return builder.build();
}
private void createLinksForGDRest(UriInfo uriInfo, GroupDefinitionRest gdr) {
UriBuilder uriBuilder = uriInfo.getBaseUriBuilder();
uriBuilder.path("/group/definition/{id}");
URI location = uriBuilder.build(gdr.getId());
Link link = new Link("edit",location.toString());
gdr.addLink(link);
uriBuilder = uriInfo.getBaseUriBuilder();
uriBuilder.path("/group/definition");
location = uriBuilder.build(new Object[]{});
link = new Link("create",location.toString());
gdr.addLink(link);
}
}