/* * ProActive Parallel Suite(TM): * The Open Source library for parallel and distributed * Workflows & Scheduling, Orchestration, Cloud Automation * and Big Data Analysis on Enterprise Grids & Clouds. * * Copyright (c) 2007 - 2017 ActiveEon * Contact: contact@activeeon.com * * This library is free software: you can redistribute it and/or * modify it under the terms of the GNU Affero General Public License * as published by the Free Software Foundation: version 3 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * * If needed, contact us to obtain a release under GPL Version 2 or 3 * or a different license than the AGPL. */ package org.ow2.proactive.scheduler.core; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.Timer; import java.util.TimerTask; import java.util.Vector; import org.apache.log4j.Logger; import org.objectweb.proactive.api.PAActiveObject; import org.objectweb.proactive.core.UniqueID; import org.objectweb.proactive.core.mop.MOP; import org.ow2.proactive.authentication.crypto.Credentials; import org.ow2.proactive.permissions.MethodCallPermission; import org.ow2.proactive.scheduler.common.NotificationData; import org.ow2.proactive.scheduler.common.SchedulerEvent; import org.ow2.proactive.scheduler.common.SchedulerEventListener; import org.ow2.proactive.scheduler.common.SchedulerState; import org.ow2.proactive.scheduler.common.SchedulerStatus; import org.ow2.proactive.scheduler.common.exception.AlreadyConnectedException; import org.ow2.proactive.scheduler.common.exception.JobAlreadyFinishedException; import org.ow2.proactive.scheduler.common.exception.JobCreationException; import org.ow2.proactive.scheduler.common.exception.NotConnectedException; import org.ow2.proactive.scheduler.common.exception.PermissionException; import org.ow2.proactive.scheduler.common.exception.SchedulerException; import org.ow2.proactive.scheduler.common.exception.SubmissionClosedException; import org.ow2.proactive.scheduler.common.exception.UnknownJobException; import org.ow2.proactive.scheduler.common.exception.UnknownTaskException; import org.ow2.proactive.scheduler.common.job.Job; import org.ow2.proactive.scheduler.common.job.JobId; import org.ow2.proactive.scheduler.common.job.JobInfo; import org.ow2.proactive.scheduler.common.job.JobPriority; import org.ow2.proactive.scheduler.common.job.JobState; import org.ow2.proactive.scheduler.common.job.UserIdentification; import org.ow2.proactive.scheduler.common.task.TaskId; import org.ow2.proactive.scheduler.common.task.TaskInfo; import org.ow2.proactive.scheduler.common.task.TaskState; import org.ow2.proactive.scheduler.core.jmx.SchedulerJMXHelper; import org.ow2.proactive.scheduler.core.properties.PASchedulerProperties; import org.ow2.proactive.scheduler.job.ClientJobState; import org.ow2.proactive.scheduler.job.IdentifiedJob; import org.ow2.proactive.scheduler.job.InternalJob; import org.ow2.proactive.scheduler.job.InternalJobFactory; import org.ow2.proactive.scheduler.job.SchedulerUserInfo; import org.ow2.proactive.scheduler.job.UserIdentificationImpl; import org.ow2.proactive.scheduler.permissions.ChangePolicyPermission; import org.ow2.proactive.scheduler.permissions.ChangePriorityPermission; import org.ow2.proactive.scheduler.permissions.ConnectToResourceManagerPermission; import org.ow2.proactive.scheduler.permissions.HandleOnlyMyJobsPermission; import org.ow2.proactive.scheduler.util.JobLogger; import org.ow2.proactive.scheduler.util.TaskLogger; class SchedulerFrontendState implements SchedulerStateUpdate { public static final String YOU_DO_NOT_HAVE_PERMISSION_TO_GET_THE_STATUS = "You do not have permission to get the status !"; public static final String YOU_DO_NOT_HAVE_PERMISSION_TO_ADD_A_LISTENER = "You do not have permission to add a listener !"; public static final String YOU_DO_NOT_HAVE_PERMISSION_TO_CHANGE_THE_PRIORITY_OF_THIS_JOB = "You do not have permission to change the priority of this job !"; public static final String YOU_DO_NOT_HAVE_PERMISSION_TO_GET_THE_STATE = "You do not have permission to get the state !"; public static final String YOU_DO_NOT_HAVE_PERMISSION_TO_GET_THE_STATE_OF_THIS_TASK = "You do not have permission to get the state of this task !"; public static final String YOU_DO_NOT_HAVE_PERMISSION_TO_GET_THE_STATE_OF_THIS_JOB = "You do not have permission to get the state of this job !"; public static final String YOU_DO_NOT_HAVE_PERMISSION_TO_GET_THE_TASK_LOGS_OF_THIS_JOB = "You do not have permission to get the task logs of this job !"; public static final String YOU_DO_NOT_HAVE_PERMISSION_TO_RESTART_THIS_TASK = "You do not have permission to restart this task !"; public static final String YOU_DO_NOT_HAVE_PERMISSION_TO_PREEMPT_THIS_TASK = "You do not have permission to preempt this task !"; public static final String YOU_DO_NOT_HAVE_PERMISSION_TO_KILL_THIS_JOB = "You do not have permission to kill this job !"; public static final String YOU_DO_NOT_HAVE_PERMISSION_TO_REMOVE_THIRD_PARTY_CREDENTIALS_FROM_THE_SCHEDULER = "You do not have permission to remove third-party credentials from the scheduler !"; public static final String YOU_DO_NOT_HAVE_PERMISSION_TO_KILL_THIS_TASK = "You do not have permission to kill this task !"; public static final String YOU_DO_NOT_HAVE_PERMISSION_TO_SUBMIT_A_JOB = "You do not have permission to submit a job !"; public static final String YOU_DO_NOT_HAVE_PERMISSION_TO_STOP_THE_SCHEDULER = "You do not have permission to stop the scheduler !"; public static final String YOU_DO_NOT_HAVE_PERMISSION_TO_START_THE_SCHEDULER = "You do not have permission to start the scheduler !"; public static final String YOU_DO_NOT_HAVE_PERMISSION_TO_SHUTDOWN_THE_SCHEDULER = "You do not have permission to shutdown the scheduler !"; public static final String YOU_DO_NOT_HAVE_PERMISSION_TO_PAUSE_THIS_JOB = "You do not have permission to pause this job !"; public static final String YOU_DO_NOT_HAVE_PERMISSION_TO_PUT_THIRD_PARTY_CREDENTIALS_IN_THE_SCHEDULER = "You do not have permission to put third-party credentials in the scheduler !"; public static final String YOU_DO_NOT_HAVE_PERMISSION_TO_RESTART_IN_ERROR_TASKS_IN_THIS_JOB = "You do not have permission to restart in error tasks in this job !"; public static final String YOU_DO_NOT_HAVE_PERMISSION_TO_GET_THE_TASK_RESULT_OF_THIS_JOB = "You do not have permission to get the task result of this job !"; public static final String YOU_DO_NOT_HAVE_PERMISSION_TO_REMOVE_THIS_JOB = "You do not have permission to remove this job !"; public static final String YOU_DO_NOT_HAVE_PERMISSION_TO_RESUME_THIS_JOB = "You do not have permission to resume this job !"; public static final String YOU_DO_NOT_HAVE_PERMISSION_TO_LISTEN_THE_LOG_OF_THIS_JOB = "You do not have permission to listen the log of this job !"; public static final String YOU_DO_NOT_HAVE_PERMISSION_TO_GET_THE_RESULT_OF_THIS_JOB = "You do not have permission to get the result of this job !"; public static final String YOU_DO_NOT_HAVE_PERMISSIONS_TO_GET_THE_LOGS_OF_THIS_JOB = "You do not have permissions to get the logs of this job !"; public static final String YOU_DO_NOT_HAVE_PERMISSION_TO_PAUSE_THE_SCHEDULER = "You do not have permission to pause the scheduler !"; public static final String YOU_DO_NOT_HAVE_PERMISSION_TO_RESUME_THE_SCHEDULER = "You do not have permission to resume the scheduler !"; public static final String YOU_DO_NOT_HAVE_PERMISSION_TO_FREEZE_THE_SCHEDULER = "You do not have permission to freeze the scheduler !"; public static final String YOU_DO_NOT_HAVE_PERMISSION_TO_LIST_THIRD_PARTY_CREDENTIALS_IN_THE_SCHEDULER = "You do not have permission to list third-party credentials in the scheduler !"; public static final String YOU_DO_NOT_HAVE_PERMISSION_TO_RELOAD_POLICY_CONFIGURATION = "You do not have permission to reload policy configuration !"; public static final String YOU_DO_NOT_HAVE_PERMISSION_TO_KILL_THE_SCHEDULER = "You do not have permission to kill the scheduler !"; public static final String YOU_DO_NOT_HAVE_PERMISSION_TO_FINISH_THIS_TASK = "You do not have permission to finish this task!"; private static final String USERS_UPDATED_EVENT_METHOD = "usersUpdatedEvent"; private static final String TASK_STATE_UPDATED_EVENT_METHOD = "taskStateUpdatedEvent"; private static final String JOB_UPDATED_FULL_DATA_EVENT_METHOD = "jobUpdatedFullDataEvent"; private static final String JOB_STATE_UPDATED_EVENT_METHOD = "jobStateUpdatedEvent"; private static final String JOB_SUBMITTED_EVENT_METHOD = "jobSubmittedEvent"; private static final String SCHEDULER_STATE_UPDATED_EVENT_METHOD = "schedulerStateUpdatedEvent"; /** Scheduler logger */ private static final Logger logger = Logger.getLogger(SchedulingService.class); private static final TaskLogger tlogger = TaskLogger.getInstance(); private static final JobLogger jlogger = JobLogger.getInstance(); /** A repeated warning message */ private static final String ACCESS_DENIED = "Access denied! You are not connected or your session has expired!"; /** Maximum duration of a session for a useless client */ private static final long USER_SESSION_DURATION = PASchedulerProperties.SCHEDULER_USER_SESSION_TIME.getValueAsInt() * 1000; /** Stores methods that will be called on clients */ private static final Map<String, Method> eventMethods; static { eventMethods = new HashMap<>(); for (Method m : SchedulerEventListener.class.getMethods()) { eventMethods.put(m.getName(), m); } } /** * Mapping on the UniqueId of the sender and the user/admin identifications */ private final Map<UniqueID, ListeningUser> identifications; /** Map that link uniqueID to user credentials */ private final Map<UniqueID, Credentials> credentials; /** List used to mark the user that does not respond anymore */ private final Set<UniqueID> dirtyList; /** Job identification management */ private final Map<JobId, IdentifiedJob> jobs; /** Session timer */ private final Timer sessionTimer; /** JMX Helper reference */ private final SchedulerJMXHelper jmxHelper; /** * Scheduler state maintains by this class : avoid charging the core from * some request */ private final SchedulerStateImpl sState; private final Map<JobId, JobState> jobsMap; SchedulerFrontendState(SchedulerStateImpl sState, SchedulerJMXHelper jmxHelper) { this.identifications = new HashMap<>(); this.credentials = new HashMap<>(); this.dirtyList = new HashSet<>(); this.jmxHelper = jmxHelper; this.jobsMap = new HashMap<>(); this.jobs = new HashMap<>(); this.sessionTimer = new Timer("SessionTimer"); this.sState = sState; recover(sState); } /** * Called to recover the front end state. This method may have to rebuild * the different list of userIdentification and job/user association. */ private void recover(SchedulerStateImpl sState) { Vector<JobState> pendingJobs = sState.getPendingJobs(); Vector<JobState> runningJobs = sState.getRunningJobs(); Vector<JobState> finishedJobs = sState.getFinishedJobs(); // default state = started Set<JobState> jobStates = new HashSet<>(pendingJobs.size() + runningJobs.size() + finishedJobs.size()); if (logger.isInfoEnabled()) { logger.info("#Pending jobs: " + pendingJobs.size() + " #Running jobs: " + runningJobs.size() + " #Finished jobs: " + finishedJobs.size()); } for (JobState js : pendingJobs) { prepare(jobStates, js, false); } for (JobState js : runningJobs) { prepare(jobStates, js, false); } for (JobState js : finishedJobs) { prepare(jobStates, js, true); } } /** * Prepare the job in the frontend * * @param jobStates * a temporary set of jobs * @param js * the current job to be prepared * @param finished * if the job is finished or not */ private void prepare(Set<JobState> jobStates, JobState js, boolean finished) { jobStates.add(js); UserIdentificationImpl uIdent = new UserIdentificationImpl(js.getOwner()); IdentifiedJob ij = new IdentifiedJob(js.getId(), uIdent); jobs.put(js.getId(), ij); jobsMap.put(js.getId(), js); ij.setFinished(finished); } /** * Connect a new user on the scheduler. This user can interact with the * scheduler according to his right. * * @param sourceBodyID * the source ID of the connected object representing a user * @param identification * the identification of the connected user * @throws SchedulerException * If an error occurred during connection with the front-end. */ synchronized void connect(UniqueID sourceBodyID, UserIdentificationImpl identification, Credentials cred) throws AlreadyConnectedException { if (identifications.containsKey(sourceBodyID)) { logger.warn("Active object already connected for this user :" + identification.getUsername()); throw new AlreadyConnectedException("This active object is already connected to the scheduler !"); } logger.info(identification.getUsername() + " successfully connected !"); identifications.put(sourceBodyID, new ListeningUser(identification)); credentials.put(sourceBodyID, cred); renewUserSession(sourceBodyID, identification); // add this new user in the list of connected user sState.getUsers().update(identification); // send events usersUpdated(new NotificationData<UserIdentification>(SchedulerEvent.USERS_UPDATE, identification)); } /** * Create or renew the session (timer task) for the given user * identification. A call to this method will cancel the previous session * (timerTask), create and schedule a new one and purge the timer. * * @param id * The unique ID of the user * @param identification * the user on which to renew the session */ private void renewUserSession(final UniqueID id, UserIdentificationImpl identification) { if (identifications.get(id).isListening()) { // if this id has a listener, do not renew user session return; } final String userName = identification.getUsername(); TimerTask session = identification.getSession(); if (session != null) { session.cancel(); } identification.setSession(new TimerTask() { @Override public void run() { logger.info("End of session for user " + userName + ", id=" + id); disconnect(id); } }); sessionTimer.purge(); sessionTimer.schedule(identification.getSession(), USER_SESSION_DURATION); } synchronized SchedulerStatus getStatus() throws NotConnectedException, PermissionException { // checking permissions checkPermission("getStatus", YOU_DO_NOT_HAVE_PERMISSION_TO_GET_THE_STATUS); return sState.getStatus(); } synchronized SchedulerState getState() throws NotConnectedException, PermissionException { return getState(false); } synchronized SchedulerState getState(boolean myJobsOnly) throws NotConnectedException, PermissionException { // checking permissions checkPermission("getState", YOU_DO_NOT_HAVE_PERMISSION_TO_GET_THE_STATE); ListeningUser ui = identifications.get(PAActiveObject.getContext().getCurrentRequest().getSourceBodyID()); return myJobsOnly ? sState.filterOnUser(ui.getUser().getUsername()) : sState; } /** * Check if the given user can get the state as it is demanded (full or user * only) * * @param myOnly * true, if the user wants only its events or jobs, false if user * want the full state * @param ui * the user identification * @throws PermissionException * if permission is denied */ synchronized void handleOnlyMyJobsPermission(boolean myOnly, UserIdentificationImpl ui, String errorMessage) throws PermissionException { ui.checkPermission(new HandleOnlyMyJobsPermission(myOnly), ui.getUsername() + " does not have permissions to handle other users jobs (" + errorMessage + ")"); } synchronized void addEventListener(SchedulerEventListener sel, boolean myEventsOnly, SchedulerEvent... events) throws NotConnectedException, PermissionException { addEventListener(sel, myEventsOnly, false, events); } synchronized SchedulerState addEventListener(SchedulerEventListener sel, boolean myEventsOnly, boolean getCurrentState, SchedulerEvent... events) throws NotConnectedException, PermissionException { // checking permissions ListeningUser uIdent = checkPermissionReturningListeningUser("addEventListener", YOU_DO_NOT_HAVE_PERMISSION_TO_ADD_A_LISTENER); // check if listener is not null if (sel == null) { String msg = "Scheduler listener must be not null"; logger.info(msg); throw new IllegalArgumentException(msg); } // check if the listener is a reified remote object if (!MOP.isReifiedObject(sel)) { String msg = "Scheduler listener must be a remote object"; logger.info(msg); throw new IllegalArgumentException(msg); } // get the scheduler State SchedulerState currentState = null; if (getCurrentState) { // check get state permission is checked in getState method currentState = getState(myEventsOnly); } else { // check get state permission handleOnlyMyJobsPermission(myEventsOnly, uIdent.getUser(), YOU_DO_NOT_HAVE_PERMISSION_TO_ADD_A_LISTENER); } // prepare user for receiving events uIdent.getUser().setUserEvents(events); // set if the user wants to get its events only or every events uIdent.getUser().setMyEventsOnly(myEventsOnly); // add the listener to the list of listener for this user. UniqueID id = PAActiveObject.getContext().getCurrentRequest().getSourceBodyID(); uIdent.setListener(new ClientRequestHandler(this, id, sel)); // cancel timer for this user : session is now managed by events uIdent.getUser().getSession().cancel(); // return to the user return currentState; } synchronized void removeEventListener() throws NotConnectedException, PermissionException { // Remove the listener on that user designated by its given UniqueID, // then renew its user session as it is no more managed by the listener. UniqueID id = checkAccess(); ListeningUser uIdent = identifications.get(id); uIdent.clearListener(); // recreate the session for this user which is no more managed by // listener renewUserSession(id, uIdent.getUser()); } private UniqueID checkAccess() throws NotConnectedException { UniqueID id = PAActiveObject.getContext().getCurrentRequest().getSourceBodyID(); if (!identifications.containsKey(id)) { logger.info(ACCESS_DENIED); throw new NotConnectedException(ACCESS_DENIED); } return id; } synchronized InternalJob createJob(Job userJob, UserIdentificationImpl ident) throws NotConnectedException, PermissionException, SubmissionClosedException, JobCreationException { UniqueID id = checkAccess(); // get the internal job. InternalJob job = InternalJobFactory.createJob(userJob, this.credentials.get(id)); // setting job informations if (job.getTasks().size() == 0) { String msg = "Job " + job.getId().value() + " contains no task. You need to insert at least one task before submitting job"; logger.info(msg); throw new JobCreationException(msg); } // verifying that the user has right to set the given priority to his // job. try { ident.checkPermission(new ChangePriorityPermission(job.getPriority().ordinal()), ident.getUsername() + " does not have rights to set job priority " + job.getPriority()); } catch (PermissionException ex) { logger.info(ex.getMessage()); throw ex; } // setting the job properties job.setOwner(ident.getUsername()); return job; } synchronized void jobSubmitted(InternalJob job, UserIdentificationImpl ident) throws NotConnectedException, PermissionException, SubmissionClosedException, JobCreationException { // put the job inside the frontend management list jobs.put(job.getId(), new IdentifiedJob(job.getId(), ident)); // increase number of submit for this user ident.addSubmit(); // send update user event usersUpdated(new NotificationData<UserIdentification>(SchedulerEvent.USERS_UPDATE, ident)); jlogger.info(job.getId(), "submitted: name '" + job.getName() + "', tasks '" + job.getTotalNumberOfTasks() + "', owner '" + job.getOwner() + "'"); try { jlogger.info(job.getId(), job.display()); } catch (Exception e) { jlogger.error(job.getId(), "Error while displaying the job :", e); } } synchronized ListeningUser checkPermissionReturningListeningUser(String methodName, String permissionMsg) throws NotConnectedException, PermissionException { UniqueID id = checkAccess(); ListeningUser ident = identifications.get(id); // renew session for this user renewUserSession(id, ident.getUser()); final String fullMethodName = SchedulerFrontend.class.getName() + "." + methodName; final MethodCallPermission methodCallPermission = new MethodCallPermission(fullMethodName); try { ident.getUser().checkPermission(methodCallPermission, permissionMsg); } catch (PermissionException ex) { logger.warn(permissionMsg); throw ex; } return ident; } synchronized UserIdentificationImpl checkPermission(String methodName, String permissionMsg) throws NotConnectedException, PermissionException { return checkPermissionReturningListeningUser(methodName, permissionMsg).getUser(); } synchronized void disconnect() throws NotConnectedException, PermissionException { UniqueID id = checkAccess(); disconnect(id); } /** * Disconnect a user, remove and clean user dependent lists and objects * * @param id * the uniqueID of the user */ private synchronized void disconnect(UniqueID id) { credentials.remove(id); ListeningUser ident = identifications.remove(id); if (ident != null) { // remove listeners if needed ident.clearListener(); // remove this user to the list of connected user if it has not // already been removed ident.getUser().setToRemove(); sState.getUsers().update(ident.getUser()); // cancel the timer ident.getUser().getSession().cancel(); // log and send events String user = ident.getUser().getUsername(); logger.info("User '" + user + "' has disconnect the scheduler !"); dispatchUsersUpdated(new NotificationData<UserIdentification>(SchedulerEvent.USERS_UPDATE, ident.getUser()), false); } } synchronized boolean isConnected() { try { checkAccess(); return true; } catch (NotConnectedException nce) { return false; } } synchronized void renewSession() throws NotConnectedException { UniqueID id = checkAccess(); UserIdentificationImpl ident = identifications.get(id).getUser(); // renew session for this user renewUserSession(id, ident); } synchronized IdentifiedJob getIdentifiedJob(JobId jobId) throws UnknownJobException { IdentifiedJob ij = jobs.get(jobId); if (ij == null) { String msg = "The job represented by this ID '" + jobId + "' is unknown !"; logger.info(msg); throw new UnknownJobException(msg); } return ij; } synchronized void checkChangeJobPriority(JobId jobId, JobPriority priority) throws NotConnectedException, UnknownJobException, PermissionException, JobAlreadyFinishedException { checkPermissions("changeJobPriority", getIdentifiedJob(jobId), YOU_DO_NOT_HAVE_PERMISSION_TO_CHANGE_THE_PRIORITY_OF_THIS_JOB); UserIdentificationImpl ui = identifications.get(PAActiveObject.getContext() .getCurrentRequest() .getSourceBodyID()) .getUser(); try { ui.checkPermission(new ChangePriorityPermission(priority.getPriority()), ui.getUsername() + " does not have permissions to set job priority to " + priority); } catch (PermissionException ex) { logger.info(ex.getMessage()); throw ex; } if (jobs.get(jobId).isFinished()) { String msg = " is already finished"; jlogger.info(jobId, msg); throw new JobAlreadyFinishedException("Job " + jobId + msg); } } synchronized void checkPermissions(String methodName, IdentifiedJob identifiedJob, String errorMessage) throws NotConnectedException, UnknownJobException, PermissionException { try { checkJobOwner(methodName, identifiedJob, errorMessage); } catch (PermissionException pe) { UserIdentificationImpl ident = checkPermission(methodName, errorMessage); handleOnlyMyJobsPermission(false, ident, errorMessage); } } synchronized void checkJobOwner(String methodName, IdentifiedJob IdentifiedJob, String permissionMsg) throws NotConnectedException, UnknownJobException, PermissionException { ListeningUser ident = checkPermissionReturningListeningUser(methodName, permissionMsg); if (!IdentifiedJob.hasRight(ident.getUser())) { throw new PermissionException(permissionMsg); } } synchronized Set<TaskId> getJobTasks(JobId jobId) { JobState jobState = jobsMap.get(jobId); if (jobState == null) { return Collections.emptySet(); } else { Set<TaskId> tasks = new HashSet<>(jobState.getTasks().size()); for (TaskState task : jobState.getTasks()) { tasks.add(task.getId()); } return tasks; } } synchronized JobState getJobState(JobId jobId) throws NotConnectedException, UnknownJobException, PermissionException { checkPermissions("getJobState", getIdentifiedJob(jobId), YOU_DO_NOT_HAVE_PERMISSION_TO_GET_THE_STATE_OF_THIS_JOB); return jobsMap.get(jobId); } synchronized TaskState getTaskState(JobId jobId, TaskId taskId) throws NotConnectedException, UnknownJobException, UnknownTaskException, PermissionException { checkPermissions("getJobState", getIdentifiedJob(jobId), YOU_DO_NOT_HAVE_PERMISSION_TO_GET_THE_STATE_OF_THIS_TASK); if (jobsMap.get(jobId) == null) { throw new UnknownJobException(jobId); } TaskState ts = jobsMap.get(jobId).getHMTasks().get(taskId); if (ts == null) { throw new UnknownTaskException(taskId, jobId); } return ts; } synchronized TaskState getTaskState(JobId jobId, String taskName) throws NotConnectedException, UnknownJobException, UnknownTaskException, PermissionException { checkPermissions("getJobState", getIdentifiedJob(jobId), YOU_DO_NOT_HAVE_PERMISSION_TO_GET_THE_STATE_OF_THIS_TASK); if (jobsMap.get(jobId) == null) { throw new UnknownJobException(jobId); } TaskId taskId = null; for (TaskId t : getJobTasks(jobId)) { if (t.getReadableName().equals(taskName)) { taskId = t; } } if (taskId == null) { throw new UnknownTaskException(taskName, jobId); } TaskState ts = jobsMap.get(jobId).getHMTasks().get(taskId); if (ts == null) { throw new UnknownTaskException(taskId, jobId); } return ts; } synchronized TaskId getTaskId(JobId jobId, String taskName) throws UnknownTaskException, UnknownJobException { if (jobsMap.get(jobId) == null) { throw new UnknownJobException(jobId); } TaskId taskId = null; for (TaskId t : getJobTasks(jobId)) { if (t.getReadableName().equals(taskName)) { taskId = t; } } if (taskId == null) { throw new UnknownTaskException(taskName, jobId); } return taskId; } synchronized void checkChangePolicy() throws NotConnectedException, PermissionException { UniqueID id = checkAccess(); UserIdentificationImpl ident = identifications.get(id).getUser(); // renew session for this user renewUserSession(id, ident); try { ident.checkPermission(new ChangePolicyPermission(), ident.getUsername() + " does not have permissions to change the policy of the scheduler"); } catch (PermissionException ex) { logger.info(ex.getMessage()); throw ex; } } synchronized void checkLinkResourceManager() throws NotConnectedException, PermissionException { UniqueID id = checkAccess(); UserIdentificationImpl ident = identifications.get(id).getUser(); // renew session for this user renewUserSession(id, ident); try { ident.checkPermission(new ConnectToResourceManagerPermission(), ident.getUsername() + " does not have permissions to change RM in the scheduler"); } catch (PermissionException ex) { logger.info(ex.getMessage()); throw ex; } } /* * ########################################################################################### */ /* */ /* * ################################## LISTENER DISPATCHER #################################### */ /* */ /* * ########################################################################################### */ /** * Clear every dirty listeners that are no more responding */ private void clearListeners() { Set<UniqueID> toRemove; synchronized (dirtyList) { if (dirtyList.isEmpty()) { return; } toRemove = new HashSet<>(dirtyList); dirtyList.clear(); } for (UniqueID uId : toRemove) { disconnect(uId); } } /** * Put this is to be removed in the dirty list. * * @param id * the id of the user to be removed. */ void markAsDirty(UniqueID id) { synchronized (dirtyList) { dirtyList.add(id); } } /** * Dispatch the scheduler state updated event * * @param eventType * the type of the concrete event */ private void dispatchSchedulerStateUpdated(SchedulerEvent eventType) { try { if (logger.isDebugEnabled()) { logger.debug("event [" + eventType.toString() + "]"); } for (ListeningUser userId : identifications.values()) { // if this user has a listener if (userId.isListening()) { // if there is no specified event OR if the specified event // is allowed if ((userId.getUser().getUserEvents() == null) || userId.getUser().getUserEvents().contains(eventType)) { userId.getListener().addEvent(eventMethods.get(SCHEDULER_STATE_UPDATED_EVENT_METHOD), eventType); } } } clearListeners(); } catch (SecurityException e) { logger.error("", e); } } /** * Dispatch the job submitted event * * @param job * the new submitted job */ private void dispatchJobSubmitted(JobState job) { try { if (logger.isDebugEnabled()) { jlogger.debug(job.getJobInfo().getJobId(), " event [" + SchedulerEvent.JOB_SUBMITTED + "]"); } for (ListeningUser listeningUserId : identifications.values()) { // if this user has a listener if (listeningUserId.isListening()) { UserIdentificationImpl userId = listeningUserId.getUser(); // if there is no specified event OR if the specified // event is allowed if ((userId.getUserEvents() == null) || userId.getUserEvents().contains(SchedulerEvent.JOB_SUBMITTED)) { // if this userId have the myEventOnly=false or // (myEventOnly=true and it is its event) if (!userId.isMyEventsOnly() || (userId.isMyEventsOnly() && userId.getUsername().equals(job.getOwner()))) { listeningUserId.getListener().addEvent(eventMethods.get(JOB_SUBMITTED_EVENT_METHOD), job); } } } } clearListeners(); } catch (SecurityException e) { logger.error("", e); } } /** * Dispatch the job state updated event * * @param owner * the owner of this job * @param notification * the data to send to every client */ private void dispatchJobStateUpdated(String owner, NotificationData<JobInfo> notification) { try { if (logger.isDebugEnabled()) { // if in process of job removal do not use jlogger as job log // file // was already removed and it will create it again if (notification.getEventType() == SchedulerEvent.JOB_REMOVE_FINISHED) { logger.debug("job " + notification.getData().getJobId() + " event [" + notification.getEventType() + "]"); } else { jlogger.debug(notification.getData().getJobId(), " event [" + notification.getEventType() + "]"); } } for (ListeningUser listeningUserId : identifications.values()) { // if this user has a listener if (listeningUserId.isListening()) { UserIdentificationImpl userId = listeningUserId.getUser(); // if there is no specified event OR if the specified event // is allowed if ((userId.getUserEvents() == null) || userId.getUserEvents().contains(notification.getEventType())) { // if this userId have the myEventOnly=false or // (myEventOnly=true and it is its event) if (!userId.isMyEventsOnly() || (userId.isMyEventsOnly() && userId.getUsername().equals(owner))) { listeningUserId.getListener().addEvent(eventMethods.get(JOB_STATE_UPDATED_EVENT_METHOD), notification); } } } } clearListeners(); } catch (SecurityException e) { logger.error("", e); } } /** * Dispatch the job state updated event * * @param job * the job state */ private void dispatchJobUpdatedFullData(JobState job) { try { if (logger.isDebugEnabled()) { jlogger.debug(job.getJobInfo().getJobId(), " event [" + SchedulerEvent.JOB_UPDATED + "]"); } for (ListeningUser listeningUserId : identifications.values()) { // if this user has a listener if (listeningUserId.isListening()) { UserIdentificationImpl userId = listeningUserId.getUser(); // if there is no specified event OR if the specified // event is allowed if ((userId.getUserEvents() == null) || userId.getUserEvents().contains(SchedulerEvent.JOB_UPDATED)) { // if this userId have the myEventOnly=false or // (myEventOnly=true and it is its event) if (!userId.isMyEventsOnly() || (userId.isMyEventsOnly() && userId.getUsername().equals(job.getOwner()))) { listeningUserId.getListener().addEvent(eventMethods.get(JOB_UPDATED_FULL_DATA_EVENT_METHOD), job); } } } } clearListeners(); } catch (SecurityException e) { logger.error("", e); } } /** * Dispatch the task state updated event * * @param owner * the owner of this task * @param notification * the data to send to every client */ private void dispatchTaskStateUpdated(String owner, NotificationData<TaskInfo> notification) { try { if (logger.isDebugEnabled()) { tlogger.debug(notification.getData().getTaskId(), "event [" + notification.getEventType() + "]"); } for (ListeningUser listeningUserId : identifications.values()) { // if this user has a listener if (listeningUserId.isListening()) { UserIdentificationImpl userId = listeningUserId.getUser(); // if there is no specified event OR if the specified event // is allowed if ((userId.getUserEvents() == null) || userId.getUserEvents().contains(notification.getEventType())) { // if this userId have the myEventOnly=false or // (myEventOnly=true and it is its event) if (!userId.isMyEventsOnly() || (userId.isMyEventsOnly() && userId.getUsername().equals(owner))) { listeningUserId.getListener().addEvent(eventMethods.get(TASK_STATE_UPDATED_EVENT_METHOD), notification); } } } } clearListeners(); } catch (SecurityException e) { logger.error("", e); } } /** * Dispatch the users updated event * * @param notification * the data to send to every client */ private void dispatchUsersUpdated(NotificationData<UserIdentification> notification, boolean checkForDownUser) { try { if (logger.isDebugEnabled()) { logger.debug("event [" + notification.getEventType() + "]"); } for (ListeningUser listeningUserId : identifications.values()) { // if this user has a listener if (listeningUserId.isListening()) { UserIdentificationImpl userId = listeningUserId.getUser(); // if there is no specified event OR if the specified event // is allowed if ((userId.getUserEvents() == null) || userId.getUserEvents().contains(notification.getEventType())) { // if this userId have the myEventOnly=false or // (myEventOnly=true and it is its event) if (!userId.isMyEventsOnly() || (userId.isMyEventsOnly() && userId.getUsername().equals(notification.getData().getUsername()))) { listeningUserId.getListener().addEvent(eventMethods.get(USERS_UPDATED_EVENT_METHOD), notification); } } } } // Important condition to avoid recursive checks if (checkForDownUser) { clearListeners(); } } catch (SecurityException e) { logger.error("", e); } this.jmxHelper.getSchedulerRuntimeMBean().usersUpdatedEvent(notification); } @Override public synchronized void schedulerStateUpdated(SchedulerEvent eventType) { switch (eventType) { case STARTED: sState.setState(SchedulerStatus.STARTED); break; case STOPPED: sState.setState(SchedulerStatus.STOPPED); break; case PAUSED: sState.setState(SchedulerStatus.PAUSED); break; case FROZEN: sState.setState(SchedulerStatus.FROZEN); break; case RESUMED: sState.setState(SchedulerStatus.STARTED); break; case SHUTTING_DOWN: sState.setState(SchedulerStatus.SHUTTING_DOWN); break; case SHUTDOWN: sState.setState(SchedulerStatus.STOPPED); break; case KILLED: sState.setState(SchedulerStatus.KILLED); break; case DB_DOWN: sState.setState(SchedulerStatus.DB_DOWN); break; case RM_DOWN: case RM_UP: case POLICY_CHANGED: break; default: logger.warn("**WARNING** - Unconsistent update type received from Scheduler Core : " + eventType); return; } // send the event for all case, except default dispatchSchedulerStateUpdated(eventType); this.jmxHelper.getSchedulerRuntimeMBean().schedulerStateUpdatedEvent(eventType); } @Override public synchronized void jobSubmitted(JobState job) { ClientJobState storedJobState = new ClientJobState(job); jobsMap.put(job.getId(), storedJobState); sState.getPendingJobs().add(storedJobState); dispatchJobSubmitted(job); } @Override public synchronized void jobStateUpdated(String owner, NotificationData<JobInfo> notification) { JobState js = jobsMap.get(notification.getData().getJobId()); js.update(notification.getData()); switch (notification.getEventType()) { case JOB_PENDING_TO_RUNNING: sState.getPendingJobs().remove(js); sState.getRunningJobs().add(js); break; case JOB_PAUSED: case JOB_IN_ERROR: case JOB_RESUMED: case JOB_RESTARTED_FROM_ERROR: case JOB_CHANGE_PRIORITY: case TASK_REPLICATED: case TASK_SKIPPED: break; case JOB_PENDING_TO_FINISHED: sState.getPendingJobs().remove(js); sState.getFinishedJobs().add(js); // set this job finished, user can get its result jobs.get(notification.getData().getJobId()).setFinished(true); break; case JOB_RUNNING_TO_FINISHED: sState.getRunningJobs().remove(js); sState.getFinishedJobs().add(js); // set this job finished, user can get its result jobs.get(notification.getData().getJobId()).setFinished(true); break; case JOB_REMOVE_FINISHED: // removing jobs from the global list : this job is no more managed sState.getFinishedJobs().remove(js); jobsMap.remove(js.getId()); jobs.remove(notification.getData().getJobId()); break; default: logger.warn("**WARNING** - Unconsistent update type received from Scheduler Core : " + notification.getEventType()); return; } dispatchJobStateUpdated(owner, notification); new JobEmailNotification(js, notification).checkAndSend(); } @Override public synchronized void jobUpdatedFullData(JobState jobstate) { ClientJobState storedJobState = new ClientJobState(jobstate); dispatchJobUpdatedFullData(storedJobState); } @Override public synchronized void taskStateUpdated(String owner, NotificationData<TaskInfo> notification) { jobsMap.get(notification.getData().getJobId()).update(notification.getData()); switch (notification.getEventType()) { case TASK_PENDING_TO_RUNNING: case TASK_RUNNING_TO_FINISHED: case TASK_WAITING_FOR_RESTART: case TASK_IN_ERROR: case TASK_SKIPPED: case TASK_REPLICATED: case TASK_IN_ERROR_TO_FINISHED: dispatchTaskStateUpdated(owner, notification); break; case TASK_PROGRESS: // this event can be sent while task is already finished, // as it is not a correct behavior, event is dropped if task is // already finished. // so if task is not finished, send event if (notification.getData().getFinishedTime() <= 0) { dispatchTaskStateUpdated(owner, notification); } break; default: logger.warn("**WARNING** - Unconsistent update type received from Scheduler Core : " + notification.getEventType()); } } @Override public synchronized void usersUpdated(NotificationData<UserIdentification> notification) { switch (notification.getEventType()) { case USERS_UPDATE: dispatchUsersUpdated(notification, true); break; default: logger.warn("**WARNING** - Unconsistent update type received from Scheduler Core : " + notification.getEventType()); } } public String getCurrentUser() throws NotConnectedException { UniqueID id = checkAccess(); UserIdentificationImpl ident = identifications.get(id).getUser(); // renew session for this user renewUserSession(id, ident); return ident.getUsername(); } synchronized List<SchedulerUserInfo> getUsers() { List<SchedulerUserInfo> users = new ArrayList<>(identifications.size()); for (ListeningUser listeningUser : identifications.values()) { UserIdentificationImpl user = listeningUser.getUser(); users.add(new SchedulerUserInfo(user.getHostName(), user.getUsername(), user.getConnectionTime(), user.getLastSubmitTime(), user.getSubmitNumber())); } return users; } }