/**
* Copyright 2014 SAP AG
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.spotter.eclipse.ui.view;
import java.net.ConnectException;
import java.util.Set;
import org.eclipse.core.resources.IProject;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.ViewerCell;
import org.eclipse.swt.SWT;
import org.eclipse.swt.SWTException;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.part.ViewPart;
import org.lpe.common.util.system.LpeSystemUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.spotter.eclipse.ui.Activator;
import org.spotter.eclipse.ui.ServiceClientWrapper;
import org.spotter.eclipse.ui.editors.HierarchyEditor;
import org.spotter.eclipse.ui.jobs.DynamicSpotterRunJob;
import org.spotter.eclipse.ui.jobs.JobsContainer;
import org.spotter.eclipse.ui.model.IExtensionItem;
import org.spotter.eclipse.ui.model.IExtensionItemFactory;
import org.spotter.eclipse.ui.model.ImmutableExtensionItemFactory;
import org.spotter.eclipse.ui.providers.RunExtensionsImageProvider;
import org.spotter.eclipse.ui.providers.SpotterExtensionsLabelProvider;
import org.spotter.eclipse.ui.util.WidgetUtils;
import org.spotter.eclipse.ui.viewers.ExtensionsGroupViewer;
import org.spotter.shared.hierarchy.model.XPerformanceProblem;
import org.spotter.shared.status.SpotterProgress;
/**
* A view to display the progress of the current diagnosis run of
* DynamicSpotter.
*
* @author Denis Knoepfle
*
*/
public class ActiveRunView extends ViewPart {
public static final String VIEW_ID = "org.spotter.eclipse.ui.view.activeRunView";
private static final long SLEEP_TIME_MILLIS = 1000;
private static final Logger LOGGER = LoggerFactory.getLogger(ActiveRunView.class);
private static final String ACTIVE_RUN_VIEW_TITLE = "Active Run";
private static final String ACTIVE_RUN_EMPTY_CONTENT_DESC = "No project selected.";
private static final String ACTIVE_RUN_CONTENT_DESC_TEMPLATE = "DynamicSpotter diagnosis of project '%s'";
private static final String ACTIVE_RUN_MULTI_CONTENT_DESC = "Selected multiple projects.";
private final IExtensionItemFactory extensionItemFactory;
private boolean isDisposed;
private Label label;
private TreeViewer treeViewer;
private long currentViewerInputJobId;
private SpotterProgress spotterProgress;
private RunExtensionsImageProvider runExtensionsImageProvider;
private class ViewUpdater implements Runnable {
@Override
public void run() {
while (!isDisposed) {
try {
Thread.sleep(SLEEP_TIME_MILLIS);
} catch (InterruptedException e) {
LOGGER.warn("ViewUpdater was interrupted");
}
try {
updateView();
} catch (SWTException e) {
LOGGER.debug("stop view updater as view is already disposed");
break;
}
}
}
}
/**
* The constructor.
*/
public ActiveRunView() {
this.extensionItemFactory = new ImmutableExtensionItemFactory(null);
this.isDisposed = false;
this.spotterProgress = null;
this.currentViewerInputJobId = 0;
}
@Override
public void createPartControl(Composite parent) {
setPartName(ACTIVE_RUN_VIEW_TITLE);
setContentDescription(ACTIVE_RUN_EMPTY_CONTENT_DESC);
// ensure that the parent's layout is a FillLayout
if (!(parent.getLayout() instanceof FillLayout)) {
parent.setLayout(new FillLayout());
}
Composite composite = new Composite(parent, SWT.NONE);
composite.setLayout(WidgetUtils.createGridLayout(1));
label = new Label(composite, SWT.WRAP);
label.setLayoutData(new GridData(SWT.TOP, SWT.FILL, true, false));
createTreeViewer(composite);
DisposeListener disposeListener = new DisposeListener() {
@Override
public void widgetDisposed(DisposeEvent e) {
isDisposed = true;
}
};
label.addDisposeListener(disposeListener);
treeViewer.getTree().addDisposeListener(disposeListener);
LpeSystemUtils.submitTask(new ViewUpdater());
}
private void createTreeViewer(Composite parent) {
treeViewer = ExtensionsGroupViewer.createTreeViewer(parent, extensionItemFactory.createExtensionItem(), null,
false);
SpotterExtensionsLabelProvider labelProvider = new SpotterExtensionsLabelProvider() {
@Override
public void update(ViewerCell cell) {
super.update(cell);
Object element = cell.getElement();
if (element instanceof IExtensionItem) {
String suffix = createSpotterProgressSuffix(element);
if (!suffix.isEmpty()) {
cell.setText(cell.getText() + suffix);
}
}
}
private String createSpotterProgressSuffix(Object element) {
String suffix = "";
if (spotterProgress != null) {
Object xmlModel = ((IExtensionItem) element).getModelWrapper().getXMLModel();
if (xmlModel instanceof XPerformanceProblem) {
String problemId = ((XPerformanceProblem) xmlModel).getUniqueId();
String progressString = DynamicSpotterRunJob.createProgressString(spotterProgress, problemId,
true);
if (progressString != null) {
suffix = progressString;
}
}
}
return suffix;
}
};
runExtensionsImageProvider = new RunExtensionsImageProvider();
labelProvider.setImageProvider(runExtensionsImageProvider);
treeViewer.setLabelProvider(labelProvider);
}
@Override
public void setFocus() {
treeViewer.getControl().setFocus();
}
@Override
public void dispose() {
this.isDisposed = true;
}
private void updateView() {
if (isDisposed) {
return;
}
Set<IProject> selected = Activator.getDefault().getSelectedProjects();
final String description;
if (selected.isEmpty()) {
description = ACTIVE_RUN_EMPTY_CONTENT_DESC;
clear();
} else if (selected.size() == 1) {
IProject project = selected.iterator().next();
description = String.format(ACTIVE_RUN_CONTENT_DESC_TEMPLATE, project.getName());
updateContent(project);
} else {
description = ACTIVE_RUN_MULTI_CONTENT_DESC;
clear();
}
Shell shell = getSite() == null ? null : getSite().getShell();
WidgetUtils.submitSyncExecIgnoreDisposed(shell, new Runnable() {
@Override
public void run() {
setContentDescription(description);
}
});
}
private void updateContent(IProject project) {
final String projectName = project.getName();
ServiceClientWrapper client = Activator.getDefault().getClient(projectName);
boolean hasClientConnection = client.testConnection(false);
boolean hasConnectionErrorOccured = false;
Long jobId = null;
if (hasClientConnection) {
try {
jobId = JobsContainer.readCurrentJob(client, project);
} catch (ConnectException e) {
hasConnectionErrorOccured = true;
}
}
if (!hasClientConnection || hasConnectionErrorOccured) {
clear();
WidgetUtils.submitSyncExecIgnoreDisposed(label, new Runnable() {
@Override
public void run() {
label.setText("No connection to DS service. Try again later.");
}
});
} else if (jobId == null) {
clear();
WidgetUtils.submitSyncExecIgnoreDisposed(label, new Runnable() {
@Override
public void run() {
label.setText("Currently no running diagnosis.");
}
});
} else {
updateDiagnosisData(jobId, client, projectName);
}
WidgetUtils.submitSyncExecIgnoreDisposed(label, new Runnable() {
@Override
public void run() {
label.getParent().layout();
}
});
}
private void updateDiagnosisData(final long jobId, ServiceClientWrapper client, String projectName) {
if (jobId != currentViewerInputJobId) {
runExtensionsImageProvider.setSpotterProgress(null);
XPerformanceProblem rootProblem = client.getCurrentRootProblem(false);
if (rootProblem == null) {
WidgetUtils.submitSyncExecIgnoreDisposed(label, new Runnable() {
@Override
public void run() {
label.setText("Cannot fetch root problem!");
treeViewer.setInput(null);
}
});
return;
}
final IExtensionItem input = HierarchyEditor.createPerformanceProblemHierarchy(projectName,
extensionItemFactory, rootProblem);
WidgetUtils.submitSyncExecIgnoreDisposed(treeViewer.getControl(), new Runnable() {
@Override
public void run() {
treeViewer.setInput(input);
treeViewer.expandAll();
}
});
currentViewerInputJobId = jobId;
}
spotterProgress = client.getCurrentProgressReport();
runExtensionsImageProvider.setSpotterProgress(spotterProgress);
WidgetUtils.submitSyncExecIgnoreDisposed(label, new Runnable() {
@Override
public void run() {
label.setText("Diagnosis with job id '" + jobId + "' is in progress!");
treeViewer.refresh();
}
});
}
private void clear() {
WidgetUtils.submitSyncExecIgnoreDisposed(label, new Runnable() {
@Override
public void run() {
label.setText("");
treeViewer.setInput(null);
currentViewerInputJobId = 0;
}
});
}
}