/*
* 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.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import javax.ejb.EJB;
import javax.ejb.Stateless;
import javax.interceptor.Interceptors;
import javax.ws.rs.DELETE;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.EntityTag;
import javax.ws.rs.core.GenericEntity;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Request;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
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;
import org.jboss.resteasy.annotations.GZIP;
import org.jboss.resteasy.annotations.cache.Cache;
import org.rhq.core.domain.auth.Subject;
import org.rhq.core.domain.configuration.Configuration;
import org.rhq.core.domain.configuration.PropertySimple;
import org.rhq.core.domain.resource.Resource;
import org.rhq.core.domain.resource.group.ResourceGroup;
import org.rhq.enterprise.server.auth.SubjectManagerLocal;
import org.rhq.enterprise.server.resource.ResourceManagerLocal;
import org.rhq.enterprise.server.resource.ResourceNotFoundException;
import org.rhq.enterprise.server.resource.group.ResourceGroupNotFoundException;
import org.rhq.enterprise.server.rest.domain.GroupRest;
import org.rhq.enterprise.server.rest.domain.ResourceWithType;
import org.rhq.enterprise.server.rest.domain.UserRest;
/**
* Class that deals with user specific stuff
* @author Heiko W. Rupp
*/
@Produces({ "application/json", "application/xml", "text/plain", "text/html" })
@Path("/user")
@Api(value = "Api that deals with user related stuff")
@Interceptors(SetCallerInterceptor.class)
@Stateless
public class UserHandlerBean extends AbstractRestBean {
/**
* List of favorite {@link org.rhq.core.domain.resource.Resource} id's, delimited by '|' characters. Default is "".
*/
public static final String RESOURCE_HEALTH_RESOURCES = ".dashContent.resourcehealth.resources";
/**
* List of favorite {@link org.rhq.core.domain.resource.group.ResourceGroup} id's, delimited by '|' characters.
* Default is "".
*/
public static final String GROUP_HEALTH_GROUPS = ".dashContent.grouphealth.groups";
@EJB
private SubjectManagerLocal subjectManager;
@EJB
private ResourceManagerLocal resourceManager;
@GZIP
@GET
@Path("favorites/resource")
@ApiOperation(value = "Return a list of favorite resources of the caller", multiValueResponse = true, responseClass = "ResourceWithType")
public Response getFavorites(@Context
UriInfo uriInfo, @Context
HttpHeaders httpHeaders) {
Set<Integer> favIds = getResourceIdsForFavorites();
List<ResourceWithType> ret = new ArrayList<ResourceWithType>();
MediaType mediaType = httpHeaders.getAcceptableMediaTypes().get(0);
for (Integer id : favIds) {
try {
Resource res = resourceManager.getResource(caller, id);
ResourceWithType rwt = fillRWT(res, uriInfo);
ret.add(rwt);
} catch (Exception e) {
if (e instanceof ResourceNotFoundException) {
log.debug("Favorite resource with id " + id + " not found - not returning to the user");
}
else {
log.warn("Retrieving resource with id " + id + " failed: " + e.getLocalizedMessage());
}
}
}
Response.ResponseBuilder builder;
if (mediaType.equals(MediaType.TEXT_HTML_TYPE)) {
builder = Response.ok(renderTemplate("listResourceWithType", ret), mediaType);
} else {
GenericEntity<List<ResourceWithType>> list = new GenericEntity<List<ResourceWithType>>(ret) {
};
builder = Response.ok(list);
}
return builder.build();
}
@GZIP
@GET
@Path("favorites/group")
@ApiOperation(value = "Return a list of favorite groups of the caller", multiValueResponse = true, responseClass = "GroupRest")
public Response getGroupFavorites(@Context
UriInfo uriInfo, @Context
HttpHeaders httpHeaders) {
Set<Integer> favIds = getGroupIdsForFavorites();
List<GroupRest> ret = new ArrayList<GroupRest>();
MediaType mediaType = httpHeaders.getAcceptableMediaTypes().get(0);
for (Integer id : favIds) {
try {
/*
* In theory we should not have any bad group ids in favorites, but ...
*/
ResourceGroup res = resourceGroupManager.getResourceGroup(caller, id);
GroupRest rwt = fillGroup(res, uriInfo);
ret.add(rwt);
}
catch (ResourceGroupNotFoundException e) {
log.debug("Favorite group with id " + id + " not found - not returning to the user");
}
catch (Exception e) {
log.warn("Retrieving group with id " + id + " failed: " + e.getLocalizedMessage());
}
}
Response.ResponseBuilder builder;
if (mediaType.equals(MediaType.TEXT_HTML_TYPE)) {
builder = Response.ok(renderTemplate("listGroup", ret), mediaType);
} else {
GenericEntity<List<GroupRest>> list = new GenericEntity<List<GroupRest>>(ret) {
};
builder = Response.ok(list);
}
return builder.build();
}
@PUT
@Path("favorites/resource/{id}")
@ApiOperation(value = "Add a resource as favorite for the caller")
public void addFavoriteResource(@ApiParam(name = "id", value = "Id of the resource")
@PathParam("id")
int resourceId) {
// Check if the resource exists and throw an error if not
fetchResource(resourceId);
Set<Integer> favIds = getResourceIdsForFavorites();
if (!favIds.contains(resourceId)) {
favIds.add(resourceId);
updateResourceFavorites(favIds);
}
}
@PUT
@Path("favorites/group/{id}")
@ApiOperation(value = "Add a group as favorite for the caller")
public void addFavoriteResourceGroup(@ApiParam(name = "id", value = "Id of the group")
@PathParam("id")
int groupId) {
// Check if the resource exists and throw an error if not
fetchGroup(groupId, false);
Set<Integer> favIds = getGroupIdsForFavorites();
if (!favIds.contains(groupId)) {
favIds.add(groupId);
updateGroupFavorites(favIds);
}
}
@DELETE
@Path("favorites/resource/{id}")
@ApiOperation(value = "Remove a resource from favorites", notes = "This operation is by default idempotent, returning 204." +
"If you want to check if the resource was a favorite, you need to pass the 'validate' query parameter.")
@ApiErrors({
@ApiError(code = 204, reason = "Resource was removed or was no favorite with validation not set"),
@ApiError(code = 404, reason = "Resource was no favorite and validate was set")
})
public void removeResourceFromFavorites(
@ApiParam(name = "id", value = "Id of the resource") @PathParam("id") int id,
@ApiParam("Validate if the resource is a favorite") @QueryParam("validate") @DefaultValue("false") boolean validate) {
Set<Integer> favIds = getResourceIdsForFavorites();
if (favIds.contains(id)) {
favIds.remove(id);
updateResourceFavorites(favIds);
}
else {
if (validate) {
throw new StuffNotFoundException("Resource with id " + id + " in favorites ");
}
}
}
@DELETE
@Path("favorites/group/{id}")
@ApiOperation(value = "Remove a group from favorites", notes = "This operation is by default idempotent, returning 204." +
"If you want to check if the group was a favorite, you need to pass the 'validate' query parameter.")
@ApiErrors({
@ApiError(code = 204, reason = "Group was removed or was no favorite with validation not set"),
@ApiError(code = 404, reason = "Group was no favorite and validate was set")
})
public void removeResourceGroupFromFavorites(
@ApiParam(name = "id", value = "Id of the group") @PathParam("id") int id,
@ApiParam("Validate if the group is a favorite") @QueryParam("validate") @DefaultValue("false") boolean validate) {
Set<Integer> favIds = getGroupIdsForFavorites();
if (favIds.contains(id)) {
favIds.remove(id);
updateGroupFavorites(favIds);
}
else {
if (validate) {
throw new StuffNotFoundException("Group with id " + id + " in favorites ");
}
}
}
@GET
@Cache(maxAge = 600)
@Path("{id}")
@ApiOperation(value = "Get info about a user", responseClass = "UserRest")
public Response getUserDetails(@ApiParam(value = "Login of the user")
@PathParam("id")
String loginName, @Context
Request request, @Context
HttpHeaders headers) {
Subject subject = subjectManager.getSubjectByName(loginName);
if (subject == null) {
throw new StuffNotFoundException("User with login " + loginName);
}
EntityTag eTag = new EntityTag(Long.toOctalString(subject.hashCode()));
Response.ResponseBuilder builder = request.evaluatePreconditions(eTag);
if (builder == null) {
UserRest user = new UserRest(subject.getId(), subject.getName());
user.setFirstName(subject.getFirstName());
user.setLastName(subject.getLastName());
user.setEmail(subject.getEmailAddress());
user.setTel(subject.getPhoneNumber());
MediaType mediaType = headers.getAcceptableMediaTypes().get(0);
builder = Response.ok(user, mediaType);
builder.tag(eTag);
}
return builder.build();
}
private void updateResourceFavorites(Set<Integer> favIds) {
Configuration conf = caller.getUserConfiguration();
if (conf==null) {
conf = new Configuration();
}
StringBuilder builder = buildFavStringFromSet(favIds);
PropertySimple prop = conf.getSimple(RESOURCE_HEALTH_RESOURCES);
if (prop == null) {
conf.put(new PropertySimple(RESOURCE_HEALTH_RESOURCES, builder.toString()));
}
else {
prop.setStringValue(builder.toString());
}
caller.setUserConfiguration(conf);
subjectManager.updateSubject(caller, caller);
}
private void updateGroupFavorites(Set<Integer> favIds) {
Configuration conf = caller.getUserConfiguration();
if (conf==null) {
conf = new Configuration();
}
StringBuilder builder = buildFavStringFromSet(favIds);
PropertySimple prop = conf.getSimple(GROUP_HEALTH_GROUPS);
if (prop == null) {
conf.put(new PropertySimple(GROUP_HEALTH_GROUPS, builder.toString()));
}
else {
prop.setStringValue(builder.toString());
}
caller.setUserConfiguration(conf);
subjectManager.updateSubject(caller, caller);
}
private Set<Integer> getResourceIdsForFavorites() {
Configuration conf = caller.getUserConfiguration();
if (conf==null)
return new HashSet<Integer>();
String favsString = conf.getSimpleValue(RESOURCE_HEALTH_RESOURCES, "");
Set<Integer> favIds = getIdsFromFavString(favsString);
return favIds;
}
private Set<Integer> getGroupIdsForFavorites() {
Configuration conf = caller.getUserConfiguration();
if (conf==null) {
return new HashSet<Integer>();
}
String favsString = conf.getSimpleValue(GROUP_HEALTH_GROUPS, "");
Set<Integer> favIds = getIdsFromFavString(favsString);
return favIds;
}
/**
* Parse the String with favorites.
* The list of favorites is stored in the server as a list
* of ids separated by a pipe '|' character.
*
* @param favsString String as stored in for the user
* @return Set of ids of the favorites
*/
private Set<Integer> getIdsFromFavString(String favsString) {
Set<Integer> favIds = new TreeSet<Integer>();
if (!favsString.isEmpty()) {
String[] favStringArray = favsString.split("\\|");
for (String tmp : favStringArray) {
favIds.add(Integer.valueOf(tmp));
}
}
return favIds;
}
/**
* Create the String with favorites to store for the user
* The list of favorites is stored in the server as a list
* of ids separated by a pipe '|' character
* @param favIds Set of favorite ids
* @return String representation
*/
private StringBuilder buildFavStringFromSet(Set<Integer> favIds) {
StringBuilder builder = new StringBuilder();
Iterator<Integer> iter = favIds.iterator();
while (iter.hasNext()) {
builder.append(iter.next());
if (iter.hasNext()) {
builder.append('|');
}
}
return builder;
}
}