/*******************************************************************************
* Copyright (c) 2007, 2010 The Planets Project Partners.
*
* All rights reserved. This program and the accompanying
* materials are made available under the terms of the
* Apache License, Version 2.0 which accompanies
* this distribution, and is available at
* http://www.apache.org/licenses/LICENSE-2.0
*
*******************************************************************************/
/**
*
*/
package eu.planets_project.tb.impl;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.VelocityEngine;
import org.apache.velocity.exception.VelocityException;
import eu.planets_project.ifr.core.common.conf.PlanetsServerConfig;
import eu.planets_project.ifr.core.common.mail.PlanetsMailMessage;
import eu.planets_project.ifr.core.security.api.model.User;
import eu.planets_project.tb.api.AdminManager;
import eu.planets_project.tb.api.TestbedManager;
import eu.planets_project.tb.api.model.Experiment;
import eu.planets_project.tb.api.model.ExperimentPhase;
import eu.planets_project.tb.api.model.ExperimentSetup;
import eu.planets_project.tb.gui.UserBean;
import eu.planets_project.tb.gui.util.JSFUtil;
import eu.planets_project.tb.impl.system.BackendProperties;
/**
* @author alindley
*
*/
public class AdminManagerImpl implements AdminManager {
private static Log log = LogFactory.getLog(AdminManagerImpl.class);
private static AdminManagerImpl instance;
public static final String IDENTIFY = "identify";
public static final String VALIDATE = "validate";
public static final String CHARACTERISE = "characterise";
public static final String MIGRATE = "migrate";
public static final String EMULATE = "emulate";
public static final String EXECUTABLEPP = "executablepp";
//e.g. key:"identify" -> "Identify"
private static HashMap<String,String> hmExperimentTypes;
// No longer read from XML, by statically coded:
static {
hmExperimentTypes = new HashMap<String,String>();
//hmExperimentTypes.put(IDENTIFY, "Identify");
hmExperimentTypes.put(MIGRATE, "Migrate");
//hmExperimentTypes.put(VALIDATE, "Validate");
//hmExperimentTypes.put(CHARACTERISE, "Characterise");
hmExperimentTypes.put(EMULATE, "View in Emulator");
hmExperimentTypes.put(EXECUTABLEPP, "Executable Preservation Plan");
}
private static HashMap<String,String> hmOldExperimentTypes;
static {
hmOldExperimentTypes = new HashMap<String,String>();
hmOldExperimentTypes.put("experimentType.simpleMigration","simple migration");
hmOldExperimentTypes.put("experimentType.simpleCharacterisation", "simple characterisation");
}
private AdminManagerImpl(){
// Also read basic properties:
BackendProperties bp = new BackendProperties();
APPROVAL_THRESHOLD_NUMBER_OF_INPUTS = bp.getExpAdminNoInputs();
log.info("Set number of inputs before admin approval required at: "+APPROVAL_THRESHOLD_NUMBER_OF_INPUTS);
}
/**
* This class is implemented following the Java Singleton Pattern.
* Use this method to retrieve the instance of this class.
* @return
*/
public static synchronized AdminManagerImpl getInstance(){
if (instance == null){
instance = new AdminManagerImpl();
}
return instance;
}
/* ----------------------------------------------------------- */
public Collection<String> getExperimentTypeIDs() {
return hmExperimentTypes.keySet();
}
public Collection<String> getExperimentTypesNames() {
return hmExperimentTypes.values();
}
public Map<String, String> getExperimentTypeIDsandNames() {
return hmExperimentTypes;
}
public String getExperimentTypeID(String expTypeName) {
if(hmExperimentTypes.containsValue(expTypeName)) {
return getKeyFromHashMap(expTypeName, hmExperimentTypes);
}
if(hmOldExperimentTypes.containsValue(expTypeName)) {
return getKeyFromHashMap(expTypeName, hmOldExperimentTypes);
}
return null;
}
public String getExperimentTypeName(String typeID) {
if(hmExperimentTypes.containsKey(typeID)){
return hmExperimentTypes.get(typeID);
}
if(hmOldExperimentTypes.containsKey(typeID)){
return hmOldExperimentTypes.get(typeID);
}
return null;
}
private String getKeyFromHashMap( String expTypeName, HashMap<String,String> experimentTypes ) {
Iterator<String> itKeys = experimentTypes.keySet().iterator();
while(itKeys.hasNext()){
String sKey = itKeys.next();
if(experimentTypes.get(sKey).equals(expTypeName)){
return sKey;
}
}
return null;
}
/**
* @param etype
* @return
*/
public boolean isDeprecated(String typeID) {
if(hmOldExperimentTypes.containsKey(typeID)){
return true;
}
return false;
}
/**
* Code for Experiment Approval:
*/
/**
* Threshold for the number of inputs before approval is required.
* Can be overridden from BackendProperties. See constructor.
*/
public static int APPROVAL_THRESHOLD_NUMBER_OF_INPUTS = 0;
/**
* Decision flags:
*/
public static final String APPROVAL_DECISION_AWAITING = null;
public static final String APPROVAL_DECISION_APPROVED = "Approved";
public static final String APPROVAL_DECISION_DENIED = "Denied";
public static final String APPROVAL_AUTOMATIC_USER = "{automatic}";
/**
* Does this experiment require administrator approval?
* @param exp The Experiment to evaluate.
* @return TRUE if experimental approval is required to execute it, false in all other cases.
*/
public static boolean experimentRequiresApproval(Experiment exp) {
if( exp == null ) return false;
if( exp.getExperimentExecutable() == null ) return false;
// Go thru reasons for requiring approval:
if( exp.getExperimentExecutable().getInputData() != null &&
exp.getExperimentExecutable().getInputData().size() > APPROVAL_THRESHOLD_NUMBER_OF_INPUTS ) {
// Requires Approval:
return true;
}
// Otherwise, approve the experiment:
return false;
}
public static void requestExperimentApproval(Experiment exp) {
// Check if approval is require:
if( ! experimentRequiresApproval(exp) ) return;
// Store the 'Asking for approval' status as a NULL decision:
exp.getExperimentApproval().setDecision(APPROVAL_DECISION_AWAITING);
TestbedManager testbedMan = (TestbedManager) JSFUtil.getManagedObject("TestbedManager");
testbedMan.updateExperiment(exp);
// Mail the administrator:
sendApprovalRequest(exp);
log.info("The experiment '"+exp.getExperimentSetup().getBasicProperties().getExperimentName()+"' requires administrator approval.");
}
public static void approveExperimentAutomatically(Experiment exp) {
if( exp == null ) return;
if( exp.getExperimentExecutable() == null );
exp.getExperimentApproval().setExplanation("Experiment was approved automatically.");
exp.getExperimentApproval().setDecision(APPROVAL_DECISION_APPROVED);
exp.getExperimentApproval().setGo(true);
exp.getExperimentApproval().setState(Experiment.STATE_COMPLETED);
exp.getExperimentExecution().setState(Experiment.STATE_IN_PROGRESS);
exp.getExperimentApproval().addApprovalUser(APPROVAL_AUTOMATIC_USER);
TestbedManager testbedMan = (TestbedManager) JSFUtil.getManagedObject("TestbedManager");
testbedMan.updateExperiment(exp);
log.info("The experiment '"+exp.getExperimentSetup().getBasicProperties().getExperimentName()+"' was automaticallu approved for execution.");
}
public static void approveExperimentManually(Experiment exp) {
if( exp == null ) return;
if( exp.getExperimentExecutable() == null ) return;
// Find out who is responsible:
UserBean currentUser = (UserBean) JSFUtil.getManagedObject("UserBean");
// Approve it
if( exp.getExperimentApproval().getExplanation() == null ||
"".equals(exp.getExperimentApproval().getExplanation()))
exp.getExperimentApproval().setExplanation("Experiment was approved for execution.");
exp.getExperimentApproval().setDecision(APPROVAL_DECISION_APPROVED);
exp.getExperimentApproval().setGo(true);
exp.getExperimentApproval().addApprovalUser(currentUser.getUserid());
exp.getExperimentApproval().setState(Experiment.STATE_COMPLETED);
exp.getExperimentExecution().setState(Experiment.STATE_IN_PROGRESS);
TestbedManager testbedMan = (TestbedManager) JSFUtil.getManagedObject("TestbedManager");
testbedMan.updateExperiment(exp);
// Mail the user:
sendApprovalNotice(exp);
log.info("The experiment '"+exp.getExperimentSetup().getBasicProperties().getExperimentName()+"' was approved for execution.");
}
public static void denyExperimentManually(Experiment exp) {
if( exp == null ) return;
if( exp.getExperimentExecutable() == null ) return;
// Find out who is responsible:
UserBean currentUser = (UserBean) JSFUtil.getManagedObject("UserBean");
// Deny approval
if( exp.getExperimentApproval().getExplanation() == null ||
"".equals(exp.getExperimentApproval().getExplanation()))
exp.getExperimentApproval().setExplanation("Experiment was denied approval for execution.");
exp.getExperimentApproval().setDecision(APPROVAL_DECISION_DENIED);
exp.getExperimentApproval().setGo(false);
exp.getExperimentApproval().addApprovalUser(currentUser.getUserid());
TestbedManager testbedMan = (TestbedManager) JSFUtil.getManagedObject("TestbedManager");
testbedMan.updateExperiment(exp);
// Mail the user:
sendDenialNotice(exp);
log.info("The experiment '"+exp.getExperimentSetup().getBasicProperties().getExperimentName()+"' was denied approval for execution.");
}
public static boolean experimentAwaitingApproval( Experiment exp ) {
if( exp.getCurrentPhasePointer() != ExperimentPhase.PHASE_EXPERIMENTAPPROVAL ) return false;
if( exp.getExperimentApproval().getDecision() == null ) return true;
if( "".equals(exp.getExperimentApproval().getDecision()) ) return true;
return false;
}
public static boolean experimentWasApproved( Experiment exp ) {
if( exp.getCurrentPhasePointer() <= ExperimentPhase.PHASE_EXPERIMENTAPPROVAL ) return false;
if( APPROVAL_DECISION_APPROVED.equals( exp.getExperimentApproval().getDecision()) ) return true;
return false;
}
public static boolean experimentWasDenied( Experiment exp ) {
if( exp.getCurrentPhasePointer() != ExperimentPhase.PHASE_EXPERIMENTAPPROVAL ) return false;
return !experimentWasApproved(exp);
}
public static void toEditFromDenied(Experiment exp) {
if( exp == null ) return;
if( exp.getExperimentExecutable() == null );
exp.getExperimentApproval().setExplanation("");
exp.getExperimentApproval().setDecision("");
exp.getExperimentApproval().setGo(false);
exp.getExperimentSetup().setState(Experiment.STATE_IN_PROGRESS);
exp.getExperimentSetup().setSubStage(ExperimentSetup.SUBSTAGE3);
exp.getExperimentApproval().setState(Experiment.STATE_NOT_STARTED);
exp.getExperimentExecution().setState(Experiment.STATE_NOT_STARTED);
List<String> approvalUsers = exp.getExperimentApproval().getApprovalUsersIDs();
// Need to clone to avoid a 'java.util.ConcurrentModificationException':
List<String> usersToRemove = new ArrayList<String>();
for( String user : approvalUsers ) usersToRemove.add(user);
exp.getExperimentApproval().removeApprovalUsers(usersToRemove);
TestbedManager testbedMan = (TestbedManager) JSFUtil.getManagedObject("TestbedManager");
testbedMan.updateExperiment(exp);
log.info("The experiment '"+exp.getExperimentSetup().getBasicProperties().getExperimentName()+"' was made editable again.");
}
/**
* Code for emails related to Approval.
*
*/
private static void sendNotification(String username, String templateName, Experiment exp ) {
VelocityEngine velocityEngine = new VelocityEngine();
Properties props = new Properties();
props.setProperty("resource.loader", "class");
props.setProperty("class.resource.loader.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader");
props.setProperty("velocimacro.library", "");
try {
velocityEngine.init(props);
} catch( Exception e ) {
log.error("Failed to initialise the Velocity engine. :: " + e );
}
// Look up the user.
User user = UserBean.getUser(username);
if( user == null ) return;
// Create a message:
PlanetsMailMessage message = new PlanetsMailMessage();
// Add the recipient in properly.
message.addRecipient(user.getFullName() + "<" + user.getEmail() + ">");
// Determine the Testbed URL:
// FIXME This is a HACK for the TB Service URL in emails:
String testbedURL = "http://testbed.planets-project.eu/testbed/";
// String testbedURL = "http://"+AdminManagerImpl.getAuthority()+"/testbed/";
Map<String,Object> model = new HashMap<String,Object>();
model.put("user", user);
model.put("exp", exp);
model.put("expName", exp.getExperimentSetup().getBasicProperties().getExperimentName());
model.put("applicationURL", testbedURL);
VelocityContext velocityContext;
StringWriter result = new StringWriter();
try {
velocityContext = new VelocityContext(model);
velocityEngine.mergeTemplate("eu/planets_project/tb/"+templateName+".vm", velocityContext, result);
} catch (VelocityException ex) {
log.error("Mailing failed! :: "+ex);
return;
} catch (RuntimeException ex) {
log.error("Mailing failed! :: "+ex);
return;
} catch (Exception ex) {
log.error("Mailing failed! :: "+ex);
return;
}
message.setSubject(velocityContext.get("subject").toString());
message.setBody(result.toString());
try {
message.send();
} catch( Exception e ) {
log.error("An error occured while trying to send an email to "+user.getFullName()+"! :: "+e);
e.printStackTrace();
}
}
private static void sendApprovalRequest(Experiment exp) {
sendNotification("admin", "RequestApproval", exp);
}
private static void sendApprovalNotice(Experiment exp) {
if( exp.getExperimentSetup().getBasicProperties().getInvolvedUserIds() == null ) return;
String username = exp.getExperimentSetup().getBasicProperties().getInvolvedUserIds().get(0);
if( username == null ) return;
sendNotification(username, "ApprovalGranted", exp );
}
private static void sendDenialNotice(Experiment exp) {
if( exp.getExperimentSetup().getBasicProperties().getInvolvedUserIds() == null ) return;
String username = exp.getExperimentSetup().getBasicProperties().getInvolvedUserIds().get(0);
if( username == null ) return;
sendNotification(username, "ApprovalDenied", exp);
}
/**
* Helper function that looks up the actual authority for this server.
* Could also be done via the DR I think.
* @return The authority in the form 'server:port'.
*/
public static String getAuthority() {
return PlanetsServerConfig.getHostname() + ":" + PlanetsServerConfig.getPort();
}
/**
* @param selectedExperiment
* @return
*/
public static boolean isExperimentDeprecated(Experiment exp) {
log.info("Checking if "+exp.getExperimentSetup().getExperimentTypeID()+" is a deprecated type");
if( AdminManagerImpl.hmOldExperimentTypes.containsKey(
exp.getExperimentSetup().getExperimentTypeID() ) ) {
return true;
}
return false;
}
}