/*
* Copyright 2011 Research Studios Austria Forschungsgesellschaft mBH
*
* This file is part of easyrec.
*
* easyrec 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, either version 3 of the License, or
* (at your option) any later version.
*
* easyrec 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 easyrec. If not, see <http://www.gnu.org/licenses/>.
*/
package org.easyrec.controller.clusterManager;
import com.google.common.base.Strings;
import edu.uci.ics.jung.graph.DelegateTree;
import edu.uci.ics.jung.graph.Tree;
import org.codehaus.jettison.json.JSONException;
import org.codehaus.jettison.json.JSONStringer;
import org.codehaus.jettison.json.JSONWriter;
import org.easyrec.exception.core.ClusterException;
import org.easyrec.model.core.ClusterVO;
import org.easyrec.model.core.ItemAssocVO;
import org.easyrec.model.core.ItemVO;
import org.easyrec.model.core.web.RemoteTenant;
import org.easyrec.service.core.ClusterService;
import org.easyrec.service.web.IDMappingService;
import org.easyrec.service.web.ViewInitializationService;
import org.easyrec.store.dao.IDMappingDAO;
import org.easyrec.store.dao.core.ItemDAO;
import org.easyrec.store.dao.core.types.ItemTypeDAO;
import org.easyrec.store.dao.web.RemoteTenantDAO;
import org.easyrec.utils.servlet.ServletUtils;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.multiaction.MultiActionController;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Collection;
import java.util.List;
import java.util.Set;
/**
* This Controller is used to create the basic functionality of the cluster manager
* it handles create, displaying and deleting of clusters and adding and displaying items
* of the cluster.
*
* @author dmann
*/
public class ClusterManagerController extends MultiActionController {
private ClusterService clusterService;
private RemoteTenantDAO remoteTenantDAO;
private ItemDAO itemDAO;
private ItemTypeDAO itemTypeDAO;
private IDMappingDAO idMappingDAO;
private IDMappingService idMappingService;
private ViewInitializationService viewInitializationService;
public ClusterManagerController(ClusterService clusterService, RemoteTenantDAO remoteTenantDAO, ItemDAO itemDAO,
ItemTypeDAO itemTypeDAO, IDMappingDAO idMappingDAO,IDMappingService idMappingService, ViewInitializationService viewInitializationService) {
this.clusterService = clusterService;
this.remoteTenantDAO = remoteTenantDAO;
this.itemDAO = itemDAO;
this.itemTypeDAO = itemTypeDAO;
this.idMappingDAO = idMappingDAO;
this.idMappingService = idMappingService;
this.viewInitializationService = viewInitializationService;
}
/*
* This view is the cluster Manager overview. it combines the other views to the one you will se
* at the webapp at the Cluster Manager section. The View gets loaded via a JQUERY ajax load and will
* be displayed in a Modal Dialog.
*/
public ModelAndView clustermanager(HttpServletRequest request, HttpServletResponse httpServletResponse) {
ModelAndView mav = new ModelAndView("clustermanager/clustermanager");
RemoteTenant remoteTenant = viewInitializationService.initializeView(request, mav);
if (remoteTenant != null) {
Set<String> itemTypes = itemTypeDAO.getTypes(remoteTenant.getId(), true);
mav.addObject("availableItemTypes", itemTypes);
}
return mav;
}
/*
* This view shows a basic help text when you double click the CLUSTERS cluster in the tree
* (the one with the home symbol).
*/
public ModelAndView help(HttpServletRequest request, HttpServletResponse httpServletResponse) {
ModelAndView mav = new ModelAndView("clustermanager/help");
RemoteTenant remoteTenant = viewInitializationService.initializeView(request, mav);
return mav;
}
/*
* This view show the user the user the name of the cluster and includes the clusteritemtable view
* via AJAX so the user can view the contents of a cluster.
* This view gets loaded when a user double clicks a cluster name in the cluster explorer.
*/
public ModelAndView viewitems(HttpServletRequest request, HttpServletResponse httpServletResponse) {
ModelAndView mav = new ModelAndView("clustermanager/viewitems");
RemoteTenant remoteTenant = viewInitializationService.initializeView(request, mav);
String clusterId = ServletUtils.getSafeParameter(request, "clusterId", "");
ClusterVO cluster = clusterService.loadCluster(remoteTenant.getId(), clusterId);
mav.addObject("cluster", cluster);
return mav;
}
/*
* This view adds items to a given cluster and returns error messages if the Cluster service
* cant save the item to the cluster. It takes String ids as a Parameter and resolves them to
* Integer Id's for the clusterService which cant handle String Id's becouse it is in the easyrec
* core Project.
*/
public ModelAndView additemtocluster(HttpServletRequest request, HttpServletResponse httpServletResponse) {
ModelAndView mav = new ModelAndView("clustermanager/ajax/message");
RemoteTenant remoteTenant = viewInitializationService.initializeView(request, mav);
String clusterId = ServletUtils.getSafeParameter(request, "clusterId", "");
String itemId = ServletUtils.getSafeParameter(request, "itemId", "");
String itemType = ServletUtils.getSafeParameter(request, "itemType", "");
Integer itemTypeId = itemTypeDAO.getIdOfType(remoteTenant.getId(), itemType);
Integer itemIdInt = idMappingDAO.lookup(itemId);
try {
clusterService.addItemToCluster(remoteTenant.getId(), clusterId, itemIdInt, itemTypeId);
} catch (ClusterException e) {
// This exception can be thrown when the given item already is in this cluster
mav.addObject("text", e.getMessage());
return mav;
}
return mav;
}
public ModelAndView removeitemfromcluster(HttpServletRequest request, HttpServletResponse httpServletResponse) {
ModelAndView mav = new ModelAndView("clustermanager/ajax/message");
RemoteTenant remoteTenant = viewInitializationService.initializeView(request, mav);
String clusterId = ServletUtils.getSafeParameter(request, "clusterId", "");
String itemId = ServletUtils.getSafeParameter(request, "itemId", "");
String itemType = ServletUtils.getSafeParameter(request, "itemType", "");
Integer itemTypeId = itemTypeDAO.getIdOfType(remoteTenant.getId(), itemType);
Integer itemIdInt = idMappingDAO.lookup(itemId);
try {
clusterService.removeItemFromCluster(remoteTenant.getId(), clusterId, itemIdInt, itemTypeId);
} catch (ClusterException e) {
// This exception can be thrown when the given item already is in this cluster
mav.addObject("text", e.getMessage());
return mav;
}
return mav;
}
/*
* This view updates the cluster information when the user changes it via the cluster edit form.
*/
public ModelAndView updatecluster(HttpServletRequest request, HttpServletResponse httpServletResponse) {
ModelAndView mav = new ModelAndView("clustermanager/ajax/message");
RemoteTenant remoteTenant = viewInitializationService.initializeView(request, mav);
// I used a hidden string in the web form which contains the original ID (for the rename usecase)
String originalClusterId = ServletUtils.getSafeParameter(request, "originalClusterId", "");
String clusterId = ServletUtils.getSafeParameter(request, "clusterId", "");
String clusterDescription = ServletUtils.getSafeParameter(request, "clusterDescription", "");
// here i use the original cluster id to load the cluster and update the object values with the
// values from the webform.
ClusterVO cluster = clusterService.loadCluster(remoteTenant.getId(), originalClusterId);
cluster.setName(clusterId);
cluster.setDescription(clusterDescription);
try {
clusterService.updateClusterDescription(remoteTenant.getId(), originalClusterId, clusterDescription);
} catch (Exception e) {
mav.addObject("text", e.getMessage());
return mav;
}
if (!originalClusterId.equals(clusterId)) {
try {
clusterService.renameCluster(remoteTenant.getId(), originalClusterId, clusterId);
} catch (Exception e) {
mav.addObject("text", e.getMessage());
return mav;
}
}
return mav;
}
/*
* This view shows the user a table which contains all items of a cluster(which are stored as ITEM ASSOC's)
* The table is rendered via the DISPLAY TAGLIB and handles sorting and paging on his own.
*/
public ModelAndView clusteritemtable(HttpServletRequest request, HttpServletResponse httpServletResponse) {
ModelAndView mav = new ModelAndView("clustermanager/ajax/clusteritemtable");
final RemoteTenant remoteTenant = viewInitializationService.initializeView(request, mav);
String clusterId = ServletUtils.getSafeParameter(request, "clusterId", "");
ClusterVO cluster = clusterService.loadCluster(remoteTenant.getId(), clusterId);
List<ItemVO<Integer, Integer>> items = clusterService.getItemsOfCluster(cluster);
mav.addObject("cluster", cluster);
mav.addObject("items", idMappingService.mapListOfItemVOs(items,remoteTenant));
return mav;
}
/*
* This view stores a new parent for the given Node, its used via an Ajax call from the cluster manager
*/
public ModelAndView changeclusterparent(HttpServletRequest request, HttpServletResponse httpServletResponse) {
ModelAndView mav = new ModelAndView("clustermanager/ajax/message");
RemoteTenant remoteTenant = viewInitializationService.initializeView(request, mav);
String clusterId = ServletUtils.getSafeParameter(request, "clusterId", "");
String newParent = ServletUtils.getSafeParameter(request, "newParent", "");
if (remoteTenant == null) {
logger.warn("no tenantId supplied");
return mav;
}
if (clusterId.length() == 0) {
logger.warn("no clusterId supplied");
return mav;
}
clusterService.moveCluster(remoteTenant.getId(), clusterId, newParent);
return mav;
}
/*
* This view gets called after a user created a cluster within the JS tree.
* It saves the new cluster to the database.
*/
public ModelAndView createcluster(HttpServletRequest request, HttpServletResponse httpServletResponse) {
ModelAndView mav = new ModelAndView("clustermanager/ajax/message");
RemoteTenant remoteTenant = viewInitializationService.initializeView(request, mav);
String clusterId = ServletUtils.getSafeParameter(request, "clusterId", "");
String parent = ServletUtils.getSafeParameter(request, "parent", "");
if (clusterId.contains(" ")) {
mav.addObject("text", "You cannot use spaces in cluster names, sorry.");
return mav;
}
if (remoteTenant == null) {
logger.warn("no tenantId supplied");
return mav;
}
if (clusterId.length() == 0) {
logger.warn("no clusterId supplied");
return mav;
}
try {
clusterService.addCluster(remoteTenant.getId(), clusterId, "", parent);
} catch (ClusterException e) {
logger.warn("error occurred when adding cluster", e);
mav.addObject("text", e.getMessage());
return mav;
}
return mav;
}
/*
* This view gets called after a user clicked on the delete cluster button.
*/
public ModelAndView deletecluster(HttpServletRequest request, HttpServletResponse httpServletResponse) {
ModelAndView mav = new ModelAndView("clustermanager/ajax/message");
RemoteTenant remoteTenant = viewInitializationService.initializeView(request, mav);
String clusterId = ServletUtils.getSafeParameter(request, "clusterId", "");
String parent = ServletUtils.getSafeParameter(request, "parent", "");
if (clusterId.equals("CLUSTERS")) {
mav.addObject("text", "You cannot delete the main container.");
return mav;
}
if (clusterId.equals("")) {
mav.addObject("text", "No Cluster selected to delete.");
return mav;
}
if (remoteTenant == null) {
logger.warn("no tenantId supplied");
return mav;
}
if (clusterId.length() == 0) {
logger.warn("no clusterId supplied");
return mav;
}
clusterService.removeCluster(remoteTenant.getId(), clusterId);
return mav;
}
/*
* This view returns the contents of the tree view as an JSON string which will be requested at the loading
* of the cluster manager and when the user creates a new cluster.
*/
public ModelAndView loadtreedata(HttpServletRequest request, HttpServletResponse httpServletResponse) {
ModelAndView mav = new ModelAndView("clustermanager/ajax/loadtreedata");
RemoteTenant remoteTenant = viewInitializationService.initializeView(request, mav);
if (remoteTenant == null) {
logger.warn("no tenantId supplied");
return mav;
}
DelegateTree<ClusterVO, ItemAssocVO<Integer,Integer>>
clusterTree = clusterService.getClustersForTenant(remoteTenant.getId());
JsonTreeGraphWriter graphWriter = new JsonTreeGraphWriter();
JSONWriter jsonWriter = new JSONStringer();
try {
graphWriter.save(clusterTree, jsonWriter);
} catch (JSONException e) {
logger.warn("failed to serialize cluster tree to JSON", e);
return mav;
}
mav.addObject("treeJsonData", jsonWriter.toString());
return mav;
}
/**
* convert a JUNG tree to a format that can be handled by the jsonTree library
*
* @author pmarschik
*/
private static class JsonTreeGraphWriter {
public void save(Tree<ClusterVO, ItemAssocVO<Integer,Integer>> tree,
JSONWriter writer) throws JSONException {
ClusterVO root = tree.getRoot();
writeNode(tree, root, writer, root);
}
private void writeNode(Tree<ClusterVO, ItemAssocVO<Integer,Integer>> tree,
ClusterVO node, JSONWriter writer, ClusterVO root) throws JSONException {
writer.object()
.key("data").value(node.getName())
.key("attr")
.object()
.key("id")
.value(node.getName())
.key("title")
.value(node.getDescription() == null ? node.getName() : node.getDescription());
if (node == root)
writer.key("rel").value("root");
writer.endObject();
if (!Strings.isNullOrEmpty(node.getDescription()))
writer.key("description").value(node.getDescription());
Collection<ClusterVO> children = tree.getChildren(node);
if (children.size() > 0) {
writer.key("children")
.array();
for (ClusterVO child : children) {
writeNode(tree, child, writer, root);
}
writer.endArray();
}
writer.endObject();
}
}
}