/*******************************************************************************
* Copyright (c) 2007 g-Eclipse consortium All rights reserved. This program and
* the accompanying materials are made available under the terms of the Eclipse
* Public License v1.0 which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html Initial development of the original
* code was made for project g-Eclipse founded by European Union project number:
* FP6-IST-034327 http://www.geclipse.eu/ Contributor(s): UCY
* (http://www.cs.ucy.ac.cy) - Harald Gjermundrod (harald@cs.ucy.ac.cy)
******************************************************************************/
package eu.geclipse.batch.ui.internal;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.ArrayList;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.draw2d.geometry.Dimension;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.swt.widgets.Shell;
import eu.geclipse.batch.IBatchService;
import eu.geclipse.batch.IBatchJobInfo;
import eu.geclipse.batch.IQueueInfo;
import eu.geclipse.batch.IWorkerNodeInfo;
import eu.geclipse.batch.ui.internal.model.BatchDiagram;
import eu.geclipse.batch.ui.internal.model.BatchResource;
import eu.geclipse.batch.ui.internal.model.ComputingElement;
import eu.geclipse.batch.ui.internal.model.Box;
import java.util.Collections;
import eu.geclipse.batch.ui.internal.model.Connection;
import eu.geclipse.batch.ui.internal.model.Queue;
import eu.geclipse.batch.ui.internal.model.WorkerNode;
import eu.geclipse.batch.ui.dialogs.*;
import eu.geclipse.batch.ui.editors.BatchEditor;
import eu.geclipse.core.reporting.ProblemException;
/**
* Class that runs a job to synchronize the current view of the batch service
* with the actual state of the batch service.
*/
public class BatchUpdate {
protected Job updateJob;
private final int nMaxElements = 6; // how many queues or nodes to draw at each line
private List<BatchResource> Qlist = new ArrayList<BatchResource>();
private List<BatchResource> Nlist = new ArrayList<BatchResource>();
private ScheduledExecutorService executor;
private Runnable updateRunnable;
private int updateInterval;
private IBatchService batchWrapper;
private BatchDiagram diagram;
private String batchName;
private String batchType;
private BatchJobManager jobManager;
private ComputingElement computingElement;
private Box box_queue;
private Box box_nodes;
private LinkedHashMap<String, WorkerNode> workerNodes = new LinkedHashMap<String, WorkerNode>();
private LinkedHashMap<String, Queue> queues = new LinkedHashMap<String, Queue>();
private List<BatchResource> removedResources = new ArrayList<BatchResource>();
private boolean firstTime;
private ProgressDialog initProgress;
private int xCE=0;
private int maxX = 0;
private int maxY = 0;
private int count = 0;
private int EnableQ = 0;
private int DisabledQ = 0;
private int freeN = 0;
private int job_exclusiveN = 0;
private int busyN = 0;
private int downN = 0;
private int addNewQueue =89;
private BatchEditor editor;
private int[] queue_dim = { // queue_dim[0] keeps the first x coordinate of
// the fist child
-1, -1, -1
// queue_dim[1] keeps the max x coordinate
}; // queue_dim[2] keeps the max y coordinate
private int[] nodes_dim = {
-1, -1, -1
// nodes_dim[0] keeps the first x coordinate of the fist child
}; // nodes_dim[1] keeps the max x coordinate
// nodes_dim[2] keeps the max y coordinate
/**
* Default constructor.
*
* @param shell The parent shell
* @param diagram Reference to the diagram that holds the figures.
* @param batchWrapper Interface to the batch service.
* @param batchName The name FQDN of the machine hosting the batch service
* @param batchType The type of the batch service.
* @param updateInterval The interval between each update.
* @param editor The editor that connects the sorting order
*/
public BatchUpdate( final Shell shell,
final BatchDiagram diagram,
final IBatchService batchWrapper,
final String batchName,
final String batchType,
final int updateInterval,
final BatchEditor editor )
{
this.diagram = diagram;
this.batchWrapper = batchWrapper;
this.batchName = batchName;
this.batchType = batchType;
this.updateInterval = updateInterval;
this.updateRunnable = null;
this.updateJob = null;
this.jobManager = new BatchJobManager();
this.executor = Executors.newSingleThreadScheduledExecutor();
this.firstTime = true;
this.editor = editor;
this.initProgress = new ProgressDialog( shell );
String[] description = new String[]{
"Worker Nodes", //$NON-NLS-1$
"Queues", //$NON-NLS-1$
"Sort Queue", //$NON-NLS-1$
"Computing Element", //$NON-NLS-1$
"Sort Worker Nodes", //$NON-NLS-1$
"Connections"}; //$NON-NLS-1$
this.initProgress.initInformation( 6,
description,
Messages.getString( "BatchUpdate.ProgressDialogTitle" ) ); //$NON-NLS-1$
}
/**
* Updates (synchronizes the Batch service with the viewed Figure) the
* attributes of the Figure. IMPORTANT: Pay attention to the semantic of the
* return part
*
* @param wni The current state of the worker node at the Batch service.
* @return Returns <code>null</code> if the worker node is merged. A
* <code>WorkerNode</code> if the worker node was unknown, in this
* case it is added to the view.
*/
private WorkerNode updateWN( final IWorkerNodeInfo wni ) {
WorkerNode wn;
String name;
name = wni.getWnFQN();
wn = this.workerNodes.get( name );
// Check if any of the fields have been modified
if( null != wn ) {
wn.updateState( wni );
wn = null;
} else {
final Dimension dimWN = new Dimension( 100, 40 );
wn = new WorkerNode( this.jobManager, this.editor );
wn.setFQDN( wni.getWnFQN() );
wn.setKernelVersion( wni.getKernelVersion() );
wn.setNumProcessors( wni.getNp() );
wn.setTotalMem( wni.getTotalMem() );
wn.setState( wni.getState() );
wn.setJobIds( wni.getJobs() );
wn.setSize( dimWN );
this.workerNodes.put( wni.getWnFQN(), wn );
}
return wn;
}
/**
* Updates (synchronizes the Batch service with the viewed Figure) the
* attributes of the Figure. IMPORTANT: Pay attention to the semantic of the
* return part
*
* @param queuei The current state of the queue at the Batch service.
* @param jobis All the job in the batch service.
* @return Returns <code>null</code> if the queue is merged. A
* <code>Queue</code> if the queue was unknown, in this case it is
* added to the view.
*/
private Queue updateQueue( final IQueueInfo queuei,
final List<IBatchJobInfo> jobis )
{
Queue queue;
String name;
name = queuei.getQueueName();
queue = this.queues.get( name );
// Check if any of the fields have been modified
if( null != queue ) {
queue.updateState( queuei );
queue = null;
} else {
final Dimension dimQ = new Dimension( 100, 55 );//
queue = new Queue( this.jobManager, this.editor );
queue.setQueueName( queuei.getQueueName() );
queue.setState( queuei.getState() );
queue.setRunState( queuei.getRunState() );
queue.setMemory( queuei.getMemory() );
queue.setTimeCPU( queuei.getTimeCPU() );
queue.setTimeWall( queuei.getTimeWall() );
queue.setRun( queuei.getRun() );
queue.setLm( queuei.getLm() );
queue.setQue( queuei.getQue() );
queue.setNode( queuei.getNode() );
queue.setSize( dimQ );
this.queues.put( queuei.getQueueName(), queue );
}
return queue;
}
/**
* Stops this execution service (thread) from periodically updating the state
* of the Batch service.
*/
public synchronized void stopUpdate() {
if( null != this.updateJob ) {
this.executor.shutdownNow();
this.updateJob.cancel();
}
if( null != this.jobManager ) {
this.jobManager.removeAll();
}
}
/**
* Starts the execution of the update "thread" that will run at the specified
* interval.
*/
public synchronized void startUpdate() {
if( null == this.updateJob ) {
// Draw the update dialog
this.initProgress.open();
this.updateJob = new Job( this.batchName ) {
@Override
protected IStatus run( final IProgressMonitor monitor ) {
Status status = null;
try {
drawBatchInfo();
status = new Status( IStatus.OK,
Activator.PLUGIN_ID,
Messages.getString( "BatchUpdate.Ok.StatusUpdate" ) ); //$NON-NLS-1$
} catch( Exception exc ) {
Activator.logException( exc );
status = new Status( IStatus.ERROR,
Activator.PLUGIN_ID,
Messages.getString( "BatchUpdate.Error.StatusUpdate" ) ); //$NON-NLS-1$
}
return status;
}
};
this.updateRunnable = new Runnable() {
public void run() {
if( Job.NONE == BatchUpdate.this.updateJob.getState() ) {
BatchUpdate.this.updateJob.schedule();
}
}
};
// The execution starts
this.executor.scheduleWithFixedDelay( this.updateRunnable,
3,
this.updateInterval,
TimeUnit.SECONDS );
}
}
/**
* Merges what is currently displayed with the current state of the batch
* service. If a component doesn't exist, them it is created.
*/
public void drawBatchInfo() {
Queue queue;
IQueueInfo queuei;
List<IQueueInfo> queueis = null;
WorkerNode wn;
IWorkerNodeInfo wni;
List<IWorkerNodeInfo> wnis = null;
List<BatchResource> newReses = null;
List<IBatchJobInfo> jobis = null;
Dimension dimCE;
Point pointCE;
Point pointWN, pointQ;
Dimension dimBox_queue = null;
Dimension dimBox_nodes = null;
Point pointBox_queue, pointBox_nodes;
dimCE = new Dimension( 140, 110 );
pointBox_queue = new Point( 100, 55 );
pointBox_nodes = new Point( 100, 400 );
pointCE = new Point( 200, 100 );
pointWN = new Point( 400, 10 );
pointQ = new Point( 100, 1000 );
// Do the worker nodes
try {
wnis = this.batchWrapper.getWorkerNodes();
} catch( ProblemException exc ) {
Activator.logException( exc );
}
if( null != wnis ) {
if( this.firstTime )
this.initProgress.moveNextMajorTask( wnis.size() );
int loc_y = 0;
int newfreeN = 0;
int newjob_exclusiveN = 0;
int newbusyN = 0;
int newdownN = 0;
boolean changeN = false;
boolean flag_notes = true;
int j = 0;
for( int i = 0; i < wnis.size(); ++i ) {
wni = wnis.get( i );
wn = this.updateWN( wni );
// checking if some of the nodes have changed their state
if( wni.getState().toString().equals( "free" ) ) //$NON-NLS-1$
{
newfreeN++;
} else if( wni.getState().toString().equals( "job_exclusive" ) ) //$NON-NLS-1$
{
newjob_exclusiveN++;
} else if( wni.getState().toString().equals( "busy" ) ) //$NON-NLS-1$
{
newbusyN++;
} else {
newdownN++;
}
if( this.firstTime ) {
this.freeN = newfreeN;
this.job_exclusiveN = newjob_exclusiveN;
this.busyN = newbusyN;
this.downN = newdownN;
}
if( ( this.freeN != newfreeN
|| this.job_exclusiveN != newjob_exclusiveN
|| this.busyN != newbusyN || this.downN != newdownN )
&& i == wnis.size() - 1 )
{
changeN = true;
this.freeN = newfreeN;
this.job_exclusiveN = newjob_exclusiveN;
this.busyN = newbusyN;
this.downN = newdownN;
}
// drawing algorithm
if( null != wn ) {
int loc = 10 + ( 120 * ( j ) );
j++;
if( i == 0 )
this.nodes_dim[ 0 ] = loc;
if( i % this.nMaxElements == 0 && i > 0 ) {
loc_y = loc_y + 45;
loc = this.nodes_dim[ 0 ];
this.nodes_dim[ 2 ] = loc_y;
j = 0;
}
if( i == this.nMaxElements - 1 ) {
this.nodes_dim[ 1 ] = loc + 40;
flag_notes = false;
}
if( i == wnis.size() - 1 && flag_notes ) {
this.nodes_dim[ 1 ] = loc + 100;
}
pointWN = pointWN.setLocation( loc, loc_y );
wn.setLocation( pointWN );
this.Nlist.add( wn );
if( this.firstTime )
this.initProgress.moveNextMinor();
}
}
// Sorting because at least one node changed its state
if( changeN && this.editor.workerNodeByName != 1 ) {
this.editor.sortedN = 2;
this.editor.workerNodeByState = 2;
changeN = false;
}
}
try {
// Do the jobs
this.batchWrapper.getJobs( this.jobManager );
// Do the queues
queueis = this.batchWrapper.getQueues();
} catch( ProblemException exc ) {
Activator.logException( exc );
}
int loc_y = 0;
int newenabledQ = 0;
int newdisabledQ = 0;
boolean changeQ = false;
if( null != queueis ) {
if( this.firstTime )
this.initProgress.moveNextMajorTask( queueis.size() );
boolean flag_queue = true;
int j = 0;
boolean add = false;
for( int i = 0; i < queueis.size(); ++i ) {
queuei = queueis.get( i );
queue = this.updateQueue( queuei, jobis );
// checking if some of the queues have changed their state
if( queuei.getState().toString().equals( "enabled" ) ) //$NON-NLS-1$
{
newenabledQ++;
} else {
newdisabledQ++;
}
if( this.firstTime ) {
this.EnableQ = newenabledQ;
this.DisabledQ = newdisabledQ;
}
if( ( this.EnableQ != newenabledQ || this.DisabledQ != newdisabledQ )
&& i == queueis.size() - 1 )
{
changeQ = true;
this.EnableQ = newenabledQ;
this.DisabledQ = newdisabledQ;
}
// drawing algorithm
if( null != queue ) {
this.count = queueis.size();
if( !this.firstTime ) {
add = true;
}
int loc_x = 50 + ( 120 * ( j ) );
j++;
if( i == 0 )
this.queue_dim[ 0 ] = loc_x;
if( i % this.nMaxElements == 0 && i > 0 ) {
loc_y = loc_y + 70;
this.addNewQueue =this.addNewQueue +70;
loc_x = this.queue_dim[ 0 ];
this.queue_dim[ 2 ] = loc_y;
j = 0;
}
if( this.maxX < loc_x )
this.maxX = loc_x;
if( this.maxY <= loc_y )
this.maxY = loc_y;
if( i == this.nMaxElements - 1 ) {
this.queue_dim[ 1 ] = loc_x + 50;
flag_queue = false;
}
if( i == queueis.size() - 1 && flag_queue ) {
this.queue_dim[ 1 ] = loc_x + 50;
}
pointQ = pointQ.setLocation( loc_x, loc_y );
queue.setLocation( pointQ );
this.Qlist.add( queue );
if( this.firstTime )
this.initProgress.moveNextMinor();
}
}
boolean values = true;
// Sorting because at least one node changed its state
if( changeQ && this.editor.queueByName != 1 ) {
this.editor.queueByState = 2;
this.editor.sortedQ = 2;
// this.Sort( this.editor.sortedQ, this.Qlist, this.box_queue, true );
changeQ = false;
values = false;
}
// case of add a new queue
if( !this.firstTime ) {
try {
if( add ) {
addQueue(values,dimBox_queue,pointCE,pointBox_nodes );
}
} catch( Exception Z ) {
// No code needed
}
}
// Check if any of the queues were removed
Iterator<String> iter = this.queues.keySet().iterator();
while( iter.hasNext() ) {
String queueName = iter.next();
boolean found = false;
for( int i = 0; i < queueis.size(); ++i ) {
queuei = queueis.get( i );
if( queueName.equals( queuei.getQueueName() ) )
found = true;
}
// Queue is removed
if( !found ) {
this.removedResources.add( this.queues.get( queueName ) );
}
}
}
// sorting methods
Sort( this.editor.sortedQ, this.Qlist, this.box_queue, true );
Sort( this.editor.sortedN, this.Nlist, this.box_nodes, false );
// create at first time a queue box
if( null == this.box_queue ) {
int startx = this.queue_dim[ 1 ] - this.queue_dim[ 0 ];
int starty = this.queue_dim[ 2 ];
if( this.firstTime )
this.initProgress.moveNextMajorTask( 1 );
dimBox_queue = new Dimension( startx, starty + 95 );
this.box_queue = new Box( this.jobManager ); // new object
this.box_queue.setSize( dimBox_queue );
this.box_queue.setName( Messages.getString( "BoxElementQueues" ) ); //$NON-NLS-1$
pointBox_queue = pointBox_queue.setLocation( 25, 25 );
this.box_queue.setLocation( pointBox_queue );
this.box_queue.addChildren( this.Qlist );
newReses = new ArrayList<BatchResource>();
newReses.add( this.box_queue );
if( this.firstTime )
this.initProgress.moveNextMinor();
}
int X = this.queue_dim[ 0 ];
int Y = this.queue_dim[ 2 ] + 200;//
this.xCE = X;
if( null == this.computingElement ) // Create the ce
{
if( this.firstTime )
this.initProgress.moveNextMajorTask( 1 );
this.computingElement = new ComputingElement( this.jobManager );
this.computingElement.setSize( dimCE );
this.computingElement.setFQDN( this.batchName );
this.computingElement.setType( this.batchType );
pointCE = pointCE.setLocation( X + 205, Y );
this.computingElement.setLocation( pointCE );
if( null == newReses )
newReses = new ArrayList<BatchResource>();
newReses.add( this.computingElement );
if( this.firstTime )
this.initProgress.moveNextMinor();
}
// case of nodes box
if( null == this.box_nodes ) {
if( this.firstTime )
this.initProgress.moveNextMajorTask( 1 );
int startx = this.nodes_dim[ 1 ] - this.nodes_dim[ 0 ];
int starty = this.nodes_dim[ 2 ];
dimBox_nodes = new Dimension( startx, starty + 70 );
this.box_nodes = new Box( this.jobManager );
this.box_nodes.setSize( dimBox_nodes );
this.box_nodes.setName( Messages.getString( "BoxElementNodes" ) );//$NON-NLS-1$
this.box_nodes.setIsNodes( true );
pointBox_nodes = pointBox_nodes.setLocation( X, Y + 200 );
this.box_nodes.setLocation( pointBox_nodes );
if( null == newReses )
newReses = new ArrayList<BatchResource>();
newReses.add( this.box_nodes );
this.box_nodes.addChildren( this.Nlist );
if( this.firstTime )
this.initProgress.moveNextMinor();
if( this.firstTime )
this.initProgress.moveNextMajorTask( 2 );
Connection conn = new Connection( this.computingElement,
this.box_nodes,
Connection.SOLID_CONNECTION );
conn.reconnect();
Connection conn1 = new Connection( this.box_queue,
this.computingElement,
Connection.DASHED_CONNECTION );
conn1.reconnect();
}
// Draw all the new elements at the same time
if( null != newReses ) {
this.diagram.addChildren( newReses );
}
// See if the dynamic information about the ce have changed
if( null != queueis
&& queueis.size() != this.computingElement.getNumQueues() )
this.computingElement.setNumQueues( queueis.size() );
else if( null == queueis && 0 < this.computingElement.getNumQueues() )
this.computingElement.setNumQueues( 0 );
if( null != wnis && wnis.size() != this.computingElement.getNumWNs() )
this.computingElement.setNumWNs( wnis.size() );
else if( null == wnis && 0 < this.computingElement.getNumWNs() )
this.computingElement.setNumWNs( 0 );
if( this.jobManager.getJobCount() != this.computingElement.getNumJobs() )
this.computingElement.setNumJobs( this.jobManager.getJobCount() );
if( !this.removedResources.isEmpty() ) {
for( BatchResource resource : this.removedResources ) {
this.box_queue.removeChildren( this.Qlist );
this.Qlist.remove( resource );
if( resource instanceof WorkerNode ) {
// No nothing yet
} else if( resource instanceof Queue ) {
Queue q = ( Queue )resource;
this.queues.remove( q.getQueneName() );
} else
assert false;
}
this.diagram.removeChild( this.box_queue );
this.box_queue.addChildren( this.Qlist );
this.diagram.addChild( this.box_queue );
this.removedResources.clear();
this.editor.sortedQ = 0;
}
// Only show the progress bar the first time
if( this.firstTime ) {
this.initProgress.close();
this.firstTime = false;
}
}
private void addQueue(final boolean values,final Dimension dimBox_queue,final
Point pointCE,final Point pointBoxnodes)
{
this.box_queue.removeChildren( this.Qlist );
this.diagram.removeChild( this.box_queue );
this.box_queue.addChildren( this.Qlist );
boolean newsize = false;
if( ( this.count ) % ( this.nMaxElements ) == 1 ) {
this.count = 0;
Dimension dimBox_new = new Dimension( this.maxX, this.addNewQueue );
this.box_queue.setSize( dimBox_new );
this.diagram.addChild( this.box_queue );
int temp = this.addNewQueue+100;
pointCE.setLocation( this.xCE+205,temp );
this.computingElement.setLocation( pointCE );
pointBoxnodes.setLocation( this.xCE, temp+150 );
this.box_nodes.setLocation( pointBoxnodes );
newsize = true;
}
if( !newsize ) {
this.box_queue.setSize( dimBox_queue );
this.diagram.addChild( this.box_queue );
}
if( this.editor.queueByName == 1 && values ) {
this.editor.sortedQ = 1;
this.editor.queueByName = 0;
} else if( this.editor.queueByState == 2 && values ) {
this.editor.sortedQ = 2;
this.editor.queueByName = 0;
} else {
this.editor.sortedQ = 0;
}
}
/*
* Making the sorting in queues or nodes. if cont is 1 it sorts by name else
* by state, if x is true it sorts the Queues box else the Nodes box
*/
@SuppressWarnings("unchecked")
private void Sort( final int cont,
final List list,
final Box box,
final boolean x )
{
if( cont == 1 ) {
Collections.sort( list );
if( x ) {
box.setName( Messages.getString( "BoxQueue.SortedByName" ) );//$NON-NLS-1$
} else {
box.setName( Messages.getString( "BoxNode.SortedByName" ) );//$NON-NLS-1$
}
Refresh( box, list );
ResetValues();
} else if( cont == 2 ) {
Collections.sort( list );
if( x ) {
box.setName( Messages.getString( "BoxQueue.SortedByState" ) );//$NON-NLS-1$
} else {
box.setName( Messages.getString( "BoxNode.SortedByState" ) );//$NON-NLS-1$
}
Refresh( box, list );
ResetValues();
}
}
// refreshing the display
@SuppressWarnings("unchecked")
private void Refresh( final Box box, final List list ) {
box.removeChildren( list );
this.diagram.removeChild( box );
box.addChildren( list );
this.diagram.addChild( box );
}
// reseting values
private void ResetValues() {
this.editor.sortedQ = 0;
this.editor.sortedN = 0;
}
}