/*******************************************************************************
* Copyright (c) 2012 AGETO Service GmbH and others.
* 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.
*
* Contributors:
* Gunnar Wagenknecht - initial API and implementation
*******************************************************************************/
package org.eclipse.gyrex.admin.ui.jobs.internal;
import org.eclipse.gyrex.admin.ui.internal.helper.SwtUtil;
import org.eclipse.gyrex.admin.ui.internal.widgets.AdminPageWithTree;
import org.eclipse.gyrex.admin.ui.internal.widgets.Infobox;
import org.eclipse.gyrex.admin.ui.internal.widgets.NonBlockingMessageDialogs;
import org.eclipse.gyrex.context.definitions.ContextDefinition;
import org.eclipse.gyrex.context.definitions.IRuntimeContextDefinitionManager;
import org.eclipse.gyrex.jobs.JobState;
import org.eclipse.gyrex.jobs.internal.manager.JobHungDetectionHelper;
import org.eclipse.gyrex.jobs.internal.schedules.ScheduleImpl;
import org.eclipse.gyrex.jobs.internal.schedules.ScheduleStore;
import org.eclipse.gyrex.jobs.internal.storage.CloudPreferncesJobStorage;
import org.eclipse.gyrex.jobs.internal.worker.WorkerEngine;
import org.eclipse.gyrex.jobs.manager.IJobManager;
import org.eclipse.gyrex.jobs.schedules.ISchedule;
import org.eclipse.gyrex.server.Platform;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.layout.GridLayoutFactory;
import org.eclipse.jface.util.Policy;
import org.eclipse.jface.viewers.IOpenListener;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.OpenEvent;
import org.eclipse.jface.window.Window;
import org.eclipse.rap.rwt.widgets.DialogCallback;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Label;
import org.osgi.service.prefs.BackingStoreException;
import org.apache.commons.lang.StringUtils;
public class BackgroundTasksPage extends AdminPageWithTree {
public static final String ID = "background-tasks";
private static final int COLUMN_ID = 0;
private static final int COLUMN_TIMEZONE = 1;
private static final int COLUMN_QUEUE = 2;
private Button addButton;
private Button removeButton;
private Button enableButton;
private Button disableButton;
private Button showEntriesButton;
private Label schedulesMetricLabel;
private Label jobsRunningLabel;
private Label jobsWaitingMetricLabel;
private Label processingStateMetricLabel;
public BackgroundTasksPage() {
super(3);
setTitle("Background Tasks");
setTitleToolTip("Browse and manage schedules for executing background tasks.");
}
void addButtonPressed() {
final AddScheduleDialog dialog = new AddScheduleDialog(SwtUtil.getShell(addButton));
dialog.openNonBlocking(new DialogCallback() {
/** serialVersionUID */
private static final long serialVersionUID = 1L;
@Override
public void dialogClosed(final int returnCode) {
if (returnCode == Window.OK) {
refresh();
}
}
});
}
@Override
protected void createButtons(final Composite parent) {
addButton = createButton(parent, "Add");
addButton.addSelectionListener(new SelectionAdapter() {
/** serialVersionUID */
private static final long serialVersionUID = 1L;
@Override
public void widgetSelected(final SelectionEvent event) {
addButtonPressed();
}
});
removeButton = createButton(parent, "Remove");
removeButton.setEnabled(false);
removeButton.addSelectionListener(new SelectionAdapter() {
/** serialVersionUID */
private static final long serialVersionUID = 1L;
@Override
public void widgetSelected(final SelectionEvent event) {
removeButtonPressed();
}
});
createButtonSeparator(parent);
enableButton = createButton(parent, "Enable");
enableButton.addSelectionListener(new SelectionAdapter() {
/** serialVersionUID */
private static final long serialVersionUID = 1L;
@Override
public void widgetSelected(final SelectionEvent event) {
enableButtonPressed();
}
});
disableButton = createButton(parent, "Disable");
disableButton.addSelectionListener(new SelectionAdapter() {
/** serialVersionUID */
private static final long serialVersionUID = 1L;
@Override
public void widgetSelected(final SelectionEvent event) {
disableButtonPressed();
}
});
createButtonSeparator(parent);
showEntriesButton = createButton(parent, "Edit Schedule");
showEntriesButton.setEnabled(false);
showEntriesButton.addSelectionListener(new SelectionAdapter() {
/** serialVersionUID */
private static final long serialVersionUID = 1L;
@Override
public void widgetSelected(final SelectionEvent event) {
showEntriesButtonPressed();
}
});
getTreeViewer().addOpenListener(new IOpenListener() {
@Override
public void open(final OpenEvent event) {
final ScheduleImpl schedule = getFirstSelectedSchedule(event.getSelection());
if (schedule != null) {
openScheduleEntriesPage(schedule);
}
}
});
}
@Override
protected ITreeContentProvider createContentProvider() {
return new SchedulesContentProvider();
}
@Override
protected Control createHeader(final Composite parent) {
final Composite composite = new Composite(parent, SWT.NONE);
composite.setLayout(GridLayoutFactory.fillDefaults().create());
createMetricInfoArea(composite);
final Infobox infobox = new Infobox(composite);
infobox.addHeading("Schedules");
infobox.addParagraph("Background tasks in Gyrex are organized into schedules. A schedule is associated to a context and defines common properties (such as timezone) for all background tasks.");
infobox.addParagraph("Schedules define the jobs to execute as schedule entries. Schedule entries can be triggered using cron expressions or by the successful execution of a preceding entry within the same schedule. Both a schedule as well as individual schedule entries can be enabled or disabled.");
return composite;
}
private void createMetricInfoArea(final Composite parent) {
final Composite area = new Composite(parent, SWT.NONE);
area.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
area.setLayout(GridLayoutFactory.fillDefaults().spacing(0, 0).numColumns(8).create());
schedulesMetricLabel = createMetricText(area, "Schedules");
schedulesMetricLabel.setText("0");
createMetricSeparator(area);
jobsRunningLabel = createMetricText(area, "Running Jobs");
jobsRunningLabel.setText("0");
createMetricSeparator(area);
jobsWaitingMetricLabel = createMetricText(area, "Waiting Jobs");
jobsWaitingMetricLabel.setText("0");
createMetricSeparator(area);
processingStateMetricLabel = createMetricText(area, "Worker Engine");
processingStateMetricLabel.setText("?");
}
void disableButtonPressed() {
final ScheduleImpl schedule = getSelectedSchedule();
if ((schedule == null) || !schedule.isEnabled())
return;
try {
schedule.setEnabled(false);
ScheduleStore.flush(schedule.getStorageKey(), schedule);
schedule.load();
} catch (final BackingStoreException e) {
Policy.getStatusHandler().show(new Status(IStatus.ERROR, JobsUiActivator.SYMBOLIC_NAME, "Unable to activate schedule.", e), "Error");
}
getTreeViewer().refresh(schedule, true);
updateButtons();
}
void enableButtonPressed() {
final ScheduleImpl schedule = getSelectedSchedule();
if ((schedule == null) || schedule.isEnabled())
return;
try {
schedule.setEnabled(true);
ScheduleStore.flush(schedule.getStorageKey(), schedule);
schedule.load();
} catch (final BackingStoreException e) {
Policy.getStatusHandler().show(new Status(IStatus.ERROR, JobsUiActivator.SYMBOLIC_NAME, "Unable to activate schedule.", e), "Error");
}
getTreeViewer().refresh(schedule, true);
updateButtons();
}
@Override
protected String getColumnLabel(final int column) {
switch (column) {
case COLUMN_ID:
return "Schedule";
case COLUMN_TIMEZONE:
return "Time Zone";
case COLUMN_QUEUE:
return "Queue";
default:
return StringUtils.EMPTY;
}
}
@Override
protected Image getElementImage(final Object element, final int column) {
if ((element instanceof ISchedule) && (column == COLUMN_ID)) {
if (((ISchedule) element).isEnabled())
return JobsUiImages.getImage(JobsUiImages.IMG_OBJ_SCHEDULE);
else
return JobsUiImages.getImage(JobsUiImages.IMG_OBJ_SCHEDULE_DISABLED);
}
return null;
}
@Override
protected String getElementLabel(final Object element, final int column) {
if ((element instanceof ContextDefinition) && (column == 0))
return ((ContextDefinition) element).getPath().toString();
if (element instanceof ISchedule) {
final ISchedule schedule = (ISchedule) element;
switch (column) {
case COLUMN_ID:
return schedule.getId();
case COLUMN_TIMEZONE:
return schedule.getTimeZone().getID();
case COLUMN_QUEUE:
return getQueueName(schedule);
default:
break;
}
}
return StringUtils.EMPTY;
};
ScheduleImpl getFirstSelectedSchedule(final ISelection selection) {
if (selection instanceof IStructuredSelection) {
final IStructuredSelection structuredSelection = (IStructuredSelection) selection;
if (!selection.isEmpty() && (structuredSelection.getFirstElement() instanceof ScheduleImpl))
return (ScheduleImpl) structuredSelection.getFirstElement();
}
return null;
}
private String getQueueName(final ISchedule schedule) {
if (schedule.getQueueId() == null)
return "Default Queue";
switch (schedule.getQueueId()) {
case IJobManager.DEFAULT_QUEUE:
return "Default Queue";
case IJobManager.PRIORITY_QUEUE:
return "Priority Queue";
default:
return schedule.getQueueId();
}
}
private ScheduleImpl getSelectedSchedule() {
return getFirstSelectedSchedule(getTreeViewer().getSelection());
}
@Override
protected Object getViewerInput() {
return JobsUiActivator.getInstance().getService(IRuntimeContextDefinitionManager.class);
}
@Override
protected boolean isColumnSortable(final int column) {
return column == COLUMN_ID;
}
void openScheduleEntriesPage(final ScheduleImpl schedule) {
getAdminUi().openPage(ScheduleEntriesPage.ID, schedule.getContextPath().toString(), schedule.getId());
}
@Override
protected void refresh() {
getTreeViewer().setInput(getViewerInput());
}
void removeButtonPressed() {
final ScheduleImpl schedule = getSelectedSchedule();
if (schedule == null)
return;
NonBlockingMessageDialogs.openQuestion(SwtUtil.getShell(getTreeViewer().getTree()), "Remove selected Schedule", String.format("Do you really want to delete schedule %s?", schedule.getId()), new DialogCallback() {
/** serialVersionUID */
private static final long serialVersionUID = 1L;
@Override
public void dialogClosed(final int returnCode) {
if (returnCode != Window.OK)
return;
try {
ScheduleStore.remove(schedule.getStorageKey(), schedule.getId());
} catch (final BackingStoreException e) {
e.printStackTrace();
}
refresh();
}
});
}
void showEntriesButtonPressed() {
final ScheduleImpl schedule = getSelectedSchedule();
if (schedule == null)
return;
openScheduleEntriesPage(schedule);
}
@Override
protected void updateButtons() {
final int selectedElementsCount = ((IStructuredSelection) getTreeViewer().getSelection()).size();
if (selectedElementsCount == 0) {
addButton.setEnabled(true);
removeButton.setEnabled(false);
enableButton.setEnabled(false);
disableButton.setEnabled(false);
showEntriesButton.setEnabled(false);
} else {
final ScheduleImpl selectedSchedule = getSelectedSchedule();
addButton.setEnabled(true);
removeButton.setEnabled((selectedElementsCount == 1) && (selectedSchedule != null) && !selectedSchedule.isEnabled());
showEntriesButton.setEnabled((selectedElementsCount == 1) && (selectedSchedule != null) && !selectedSchedule.isEnabled());
if (selectedSchedule != null) {
if (selectedSchedule.isEnabled()) {
enableButton.setEnabled(false);
disableButton.setEnabled(true);
} else {
enableButton.setEnabled(true);
disableButton.setEnabled(false);
}
}
}
updateMetrics();
}
private void updateMetrics() {
try {
schedulesMetricLabel.setText(String.valueOf(ScheduleStore.getSchedules().length));
} catch (final Exception e) {
schedulesMetricLabel.setText("n/a");
}
try {
jobsRunningLabel.setText(String.valueOf(JobHungDetectionHelper.getNumberOfActiveJobs()));
} catch (final Exception e) {
jobsRunningLabel.setText("n/a");
}
try {
jobsWaitingMetricLabel.setText(String.valueOf(CloudPreferncesJobStorage.getAllJobStorageKeysByState(JobState.WAITING).size()));
} catch (final Exception e) {
jobsWaitingMetricLabel.setText("n/a");
}
try {
processingStateMetricLabel.setText(WorkerEngine.isSuspended() ? "Off" : "On");
} catch (final Exception e) {
processingStateMetricLabel.setText("n/a");
}
}
}