// ============================================================================
//
// Copyright (C) 2006-2016 Talend Inc. - www.talend.com
//
// This source code is available under agreement available at
// %InstallDIR%\features\org.talend.rcp.branding.%PRODUCTNAME%\%PRODUCTNAME%license.txt
//
// You should have received a copy of the agreement
// along with this program; if not, write to Talend SA
// 9 rue Pages 92150 Suresnes, France
//
// ============================================================================
package org.talend.dataprofiler.core.ui.action.actions;
import java.text.DecimalFormat;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.eclipse.core.resources.WorkspaceJob;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.IJobChangeEvent;
import org.eclipse.core.runtime.jobs.JobChangeAdapter;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.dialogs.MessageDialogWithToggle;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.cheatsheets.ICheatSheetAction;
import org.eclipse.ui.cheatsheets.ICheatSheetManager;
import org.talend.commons.exception.BusinessException;
import org.talend.commons.exception.LoginException;
import org.talend.commons.exception.PersistenceException;
import org.talend.core.model.metadata.builder.connection.Connection;
import org.talend.core.model.metadata.builder.connection.DatabaseConnection;
import org.talend.core.model.metadata.builder.connection.DelimitedFileConnection;
import org.talend.core.repository.model.ProxyRepositoryFactory;
import org.talend.cwm.compare.exception.ReloadCompareException;
import org.talend.cwm.compare.factory.ComparisonLevelFactory;
import org.talend.cwm.db.connection.ConnectionUtils;
import org.talend.cwm.helper.TaggedValueHelper;
import org.talend.dataprofiler.core.CorePlugin;
import org.talend.dataprofiler.core.ImageLib;
import org.talend.dataprofiler.core.exception.ExceptionFactory;
import org.talend.dataprofiler.core.exception.ExceptionHandler;
import org.talend.dataprofiler.core.i18n.internal.DefaultMessagesImpl;
import org.talend.dataprofiler.core.ui.IRuningStatusListener;
import org.talend.dataprofiler.core.ui.editor.analysis.AbstractAnalysisMetadataPage;
import org.talend.dataprofiler.core.ui.editor.analysis.AnalysisEditor;
import org.talend.dataprofiler.core.ui.editor.analysis.AnalysisItemEditorInput;
import org.talend.dataprofiler.core.ui.editor.analysis.BusinessRuleAnalysisResultPage;
import org.talend.dataprofiler.core.ui.editor.analysis.ColumnAnalysisResultPage;
import org.talend.dataprofiler.core.ui.editor.analysis.DynamicAnalysisMasterPage;
import org.talend.dataprofiler.core.ui.events.EventEnum;
import org.talend.dataprofiler.core.ui.events.EventManager;
import org.talend.dataprofiler.service.ISemanticStudioService;
import org.talend.dataquality.analysis.Analysis;
import org.talend.dataquality.analysis.AnalysisType;
import org.talend.dataquality.analysis.ExecutionLanguage;
import org.talend.dataquality.helpers.AnalysisHelper;
import org.talend.dataquality.properties.TDQAnalysisItem;
import org.talend.dq.analysis.AnalysisExecutorSelector;
import org.talend.dq.analysis.AnalysisHandler;
import org.talend.dq.analysis.connpool.TdqAnalysisConnectionPool;
import org.talend.dq.helper.ProxyRepositoryManager;
import org.talend.dq.helper.RepositoryNodeHelper;
import org.talend.repository.model.RepositoryNode;
import org.talend.utils.sugars.ReturnCode;
import orgomg.cwm.foundation.softwaredeployment.DataManager;
import orgomg.cwm.objectmodel.core.TaggedValue;
/**
* Run Analysis Action.
*
*/
public class RunAnalysisAction extends Action implements ICheatSheetAction {
private static Logger log = Logger.getLogger(RunAnalysisAction.class);
public static final String ID = "org.talend.common.runTalendElement"; //$NON-NLS-1$
private static final DecimalFormat FORMAT_SECONDS = new DecimalFormat("0.00"); //$NON-NLS-1$
private IRuningStatusListener listener;
/**
* Important: keep using the Item, no need to used AnalysisRepNode in this class, remember this!!!
*
*/
private TDQAnalysisItem[] items;
private boolean isNeedUnlock = false;
/**
* RunAnalysisAction constructor.
*/
public RunAnalysisAction() {
super(DefaultMessagesImpl.getString("RunAnalysisAction.Run")); //$NON-NLS-1$
setImageDescriptor(ImageLib.getImageDescriptor(ImageLib.RUN_IMAGE));
}
public void setAnalysisItems(TDQAnalysisItem[] items) {
this.items = items;
}
public void setListener(IRuningStatusListener listener) {
this.listener = listener;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jface.action.Action#run()
*/
@Override
public void run() {
if (items != null) {
for (TDQAnalysisItem anaItem : items) {
runAnalysisForItem(anaItem);
}
}
}
/**
* DOC msjian Comment method "runAnalysisForItem".
*/
protected void runAnalysisForItem(final TDQAnalysisItem anaItem) {
try {
if (anaItem == null) {
log.error("Analysis item is null"); //$NON-NLS-1$
return;
}
if (ifLockByOthers(anaItem)) {
return;
}
// check if the analysis need to be saved or can run before real running by the event
if (!EventManager.getInstance().publish(anaItem.getAnalysis(), EventEnum.DQ_ANALYSIS_CHECK_BEFORERUN, null)) {
// if the analysis need save but can not be saved, return without continue;
// or the analysis can not run, return without continue
return;
}
// to do validate after save.
validateAnalysis(anaItem);
if (!isConnectedAvailable(anaItem)) {
return;
}
if (log.isInfoEnabled()) {
addTaggedVaLueIntoConnection(anaItem);
}
AnalysisType analysisType = anaItem.getAnalysis().getParameters().getAnalysisType();
if (AnalysisType.COLUMNS_COMPARISON.equals(analysisType)) {
// If the analysis type is column comparison, ask user to continue to run or not.
if (!isContinueRun()) {
return;
}
} else if (AnalysisType.CONNECTION.equals(analysisType)) {
// If the analysis type is overview analysis, reload the database.
// TODO check here the needed of reloading database
reloadConnection(anaItem);
}
// lock the analysis before running it if it is not locked yet.(whenever the editor is not opened)
// when the run comes from the button in editor, the listener is not null;
// when the run comes from context menu, the listener is null
if (this.listener == null && !ProxyRepositoryManager.getInstance().isLocked(anaItem)) {
if (!ProxyRepositoryFactory.getInstance().isEditableAndLockIfPossible(anaItem)) {
// if the analysis is not editable , return without running.
isNeedUnlock = false;
return;
} else {// it is locked here, means that it need the unlock too.
isNeedUnlock = true;
}
} else {
isNeedUnlock = false;
}
final WorkspaceJob job = new WorkspaceJob("Run Analysis") { //$NON-NLS-1$
@Override
public IStatus runInWorkspace(IProgressMonitor monitor) throws CoreException {
final boolean isSupportDynamicChart = isSupportDynamicChart(anaItem);
monitor.beginTask(
DefaultMessagesImpl.getString("RunAnalysisAction.running", anaItem.getAnalysis().getName()), 100); //$NON-NLS-1$
Display.getDefault().syncExec(new Runnable() {
public void run() {
if (listener != null) {
listener.fireRuningItemChanged(false, isSupportDynamicChart);
}
// register dynamic event for who supported dynamic chart
if (isSupportDynamicChart) {
EventManager.getInstance().publish(anaItem.getAnalysis(),
EventEnum.DQ_DYNAMIC_REGISTER_DYNAMIC_CHART, null);
}
}
});
ReturnCode executed = null;
try {
monitor.worked(10);
executed = AnalysisExecutorSelector.executeAnalysis(anaItem, monitor);
if (monitor.isCanceled()) {
TdqAnalysisConnectionPool.closeConnectionPool(anaItem.getAnalysis());
executed = new ReturnCode(DefaultMessagesImpl.getString("RunAnalysisAction.TaskCancel"), false); //$NON-NLS-1$
monitor.done();
if (isNeedUnlock) {
unlockAnalysis(anaItem);
}
return Status.CANCEL_STATUS;
}
if (isNeedUnlock) {
unlockAnalysis(anaItem);
}
monitor.subTask(DefaultMessagesImpl.getString("RunAnalysisAction.refresh.page")); //$NON-NLS-1$
} finally {// if any exception, still need to unregister dynamic events.
Display.getDefault().syncExec(new Runnable() {
public void run() {
// Added TDQ-8787 20140616 yyin: unregister all dynamic chart events after executing
// the analysis
if (isSupportDynamicChart) {
EventManager.getInstance().publish(anaItem.getAnalysis(),
EventEnum.DQ_DYNAMIC_UNREGISTER_DYNAMIC_CHART, null);
}
if (listener != null) {
listener.fireRuningItemChanged(true);
} else {
// TODO yyin publish the event from listener.
EventManager.getInstance().publish(anaItem.getAnalysis(),
EventEnum.DQ_ANALYSIS_RUN_FROM_MENU, null);
}
}
});
}
displayResultStatus(executed, anaItem);
// TODO move this code to the right place
addAnalysisToRef(anaItem.getAnalysis());
monitor.worked(20);
monitor.done();
return Status.OK_STATUS;
}
};
job.setUser(true);
job.schedule();
// TDQ-11433: fix the job name still show after run analysis for remote project.(maybe this is not the best
// way to fix this issue)
job.addJobChangeListener(new JobChangeAdapter() {
/*
* (non-Javadoc)
*
* @see
* org.eclipse.core.runtime.jobs.JobChangeAdapter#done(org.eclipse.core.runtime.jobs.IJobChangeEvent)
*/
@Override
public void done(IJobChangeEvent event) {
job.setName(""); //$NON-NLS-1$
}
});
// TDQ-11433~
} catch (BusinessException e) {
ExceptionHandler.process(e, Level.FATAL);
}
}
/**
* only sql mode, and column, table, dependency analysis support dynamic chart now.
*
* @return boolean
*/
private boolean isSupportDynamicChart(TDQAnalysisItem runItem) {
ExecutionLanguage executionEngine = AnalysisHelper.getExecutionEngine(runItem.getAnalysis());
if (ExecutionLanguage.SQL.equals(executionEngine)) {
if (listener == null) {// when run from context menu.
if (AnalysisType.MULTIPLE_COLUMN.equals(runItem.getAnalysis().getParameters().getAnalysisType())
|| AnalysisType.BUSINESS_RULE.equals(runItem.getAnalysis().getParameters().getAnalysisType())) {
return true;
}
return false;
} else {// run from the run button in the editor
return listener instanceof DynamicAnalysisMasterPage || listener instanceof ColumnAnalysisResultPage
|| listener instanceof BusinessRuleAnalysisResultPage;
}
} else {
return false;
}
}
private void addTaggedVaLueIntoConnection(TDQAnalysisItem runItem) {
DataManager datamanager = runItem.getAnalysis().getContext().getConnection();
if (datamanager instanceof DatabaseConnection) {
TaggedValue productName = TaggedValueHelper.getTaggedValue(TaggedValueHelper.DB_PRODUCT_NAME,
datamanager.getTaggedValue());
TaggedValue productVersion = TaggedValueHelper.getTaggedValue(TaggedValueHelper.DB_PRODUCT_VERSION,
datamanager.getTaggedValue());
log.info("DB Product Name: " + productName.getValue()); //$NON-NLS-1$
log.info("DB Product Version: " + productVersion.getValue()); //$NON-NLS-1$
} else if (datamanager instanceof DelimitedFileConnection) {
log.info("File Connection path: " + ((DelimitedFileConnection) datamanager).getFilePath()); //$NON-NLS-1$
}
}
private void addAnalysisToRef(Analysis analysis) {
ISemanticStudioService service = CorePlugin.getDefault().getSemanticStudioService();
if (service != null) {
service.enrichOntRepoWithAnalysisResult(analysis);
}
}
/**
* unlock analysis.
*/
private void unlockAnalysis(TDQAnalysisItem runItem) {
try {
ProxyRepositoryFactory.getInstance().unlock(runItem);
} catch (PersistenceException e) {
log.error(e, e);
} catch (LoginException e) {
log.error(e, e);
}
}
/**
* popup dialog isContinueRun.
*
* @return
*/
private Boolean isContinueRun() {
return MessageDialogWithToggle.openConfirm(null, DefaultMessagesImpl.getString("RunAnalysisAction.confirmTitle"), //$NON-NLS-1$
DefaultMessagesImpl.getString("RunAnalysisAction.confirmMSG")); //$NON-NLS-1$
}
/**
* reload analysis connection.
*/
private void reloadConnection(TDQAnalysisItem runItem) {
if (AnalysisHelper.getReloadDatabases(runItem.getAnalysis())) {
Connection conntion = (Connection) runItem.getAnalysis().getContext().getConnection();
if (conntion != null) {
try {
RepositoryNode connectionNode = RepositoryNodeHelper.recursiveFind(conntion);
ComparisonLevelFactory.creatComparisonLevel(connectionNode).reloadCurrentLevelElement();
CorePlugin.getDefault().refreshDQView(connectionNode);
} catch (ReloadCompareException e) {
log.error(e, e);
}
}
}
}
/**
* check whether the connection of analysis is available.
*
* @return true when the connection is well connected
*/
private boolean isConnectedAvailable(TDQAnalysisItem runItem) {
DataManager datamanager = runItem.getAnalysis().getContext().getConnection();
if (datamanager == null) {
log.error(DefaultMessagesImpl.getString("ColumnMasterDetailsPage.NoColumnAssigned", runItem.getAnalysis().getName())); //$NON-NLS-1$
MessageDialogWithToggle.openError(null, DefaultMessagesImpl.getString("RunAnalysisAction.runAnalysis"),//$NON-NLS-1$
DefaultMessagesImpl.getString("ColumnMasterDetailsPage.NoColumnAssigned", runItem.getAnalysis().getName()));//$NON-NLS-1$
return false;
}
return ConnectionUtils.checkConnection(datamanager, runItem.getAnalysis().getName());
}
/**
* check whether the item is locked by Others.
*
* @return
*/
private boolean ifLockByOthers(TDQAnalysisItem runItem) {
// MOD sizhaoliu TDQ-5452 verify the lock status before running an analysis
if (ProxyRepositoryManager.getInstance().isLockByOthers(runItem)) {
RepositoryNode node1 = RepositoryNodeHelper.recursiveFind(runItem.getProperty());
if (node1 != null) {
CorePlugin.getDefault().refreshDQView(node1.getParent());
}
MessageDialog.openError(PlatformUI.getWorkbench().getDisplay().getActiveShell(),
DefaultMessagesImpl.getString("RunAnalysisAction.runAnalysis"), //$NON-NLS-1$
DefaultMessagesImpl.getString("RunAnalysisAction.error.lockByOthers")); //$NON-NLS-1$
return true;
} // ~ TDQ-5452
return false;
}
/**
* validate analysis.
*
* @throws BusinessException
*/
private void validateAnalysis(TDQAnalysisItem runItem) throws BusinessException {
if (runItem.getAnalysis() == null || runItem.getAnalysis().getParameters() == null) {
throw ExceptionFactory.getInstance().createBusinessException(runItem.getFilename());
}
try {
// check whether the field is integer value
AnalysisHandler.createHandler(runItem.getAnalysis()).getNumberOfConnectionsPerAnalysis();
} catch (NumberFormatException nfe) {
BusinessException businessException = new BusinessException();
businessException.setAdditonalMessage(DefaultMessagesImpl.getString("ColumnMasterDetailsPage.mustBeNumber", //$NON-NLS-1$
DefaultMessagesImpl.getString("AnalysisTuningPreferencePage.NumberOfConnectionsPerAnalysis"))); //$NON-NLS-1$
throw businessException;
}
}
/*
* (non-Javadoc)
*
* @see org.eclipse.ui.cheatsheets.ICheatSheetAction#run(java.lang.String[], org.eclipse.ui.cheatsheets.ICheatSheetManager)
*/
public void run(String[] params, ICheatSheetManager manager) {
// ADD mzhao 2009-02-03 If there is no active editor opened, run
// analysis action from cheat sheets will do nothing.
IEditorPart activateEditor = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().getActiveEditor();
if (activateEditor == null || !(activateEditor instanceof AnalysisEditor)) {
return;
}
// MOD TDQ-8117 20131008 yyin: find the analysis item from the editor
IEditorInput editorInput = activateEditor.getEditorInput();
if (editorInput instanceof AnalysisItemEditorInput) {
setAnalysisItems(new TDQAnalysisItem[] { ((TDQAnalysisItem) ((AnalysisItemEditorInput) editorInput).getItem()) });
}
AbstractAnalysisMetadataPage masterPage = ((AnalysisEditor) activateEditor).getMasterPage();
listener = masterPage;
// ~
run();
}
/**
* display Result Status.
*
* @param executed
*/
private void displayResultStatus(final ReturnCode executed, final TDQAnalysisItem runItem) {
if (log.isInfoEnabled()) {
int executionDuration = runItem.getAnalysis().getResults().getResultMetadata().getExecutionDuration();
log.info(DefaultMessagesImpl
.getString(
"RunAnalysisAction.displayInformation", new Object[] { runItem.getAnalysis().getName(), executed, FORMAT_SECONDS.format(Double.valueOf(executionDuration) / 1000) })); //$NON-NLS-1$
}
if (!StringUtils.isBlank(executed.getMessage())) {
Display.getDefault().asyncExec(new Runnable() {
public void run() {
// use the ActiveWorkbenchWindow's shell, don't use the default shell, otherwize the error dialog
// will be cloed when the "Run Analysis" dialog (if user don't check "Always run in background", it
// will always show this dialog when run an analysis) close, just like don't show the error dialog
Shell shell = null;
if (PlatformUI.getWorkbench().getActiveWorkbenchWindow() != null) {
shell = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell();
}
String errorMessage = DefaultMessagesImpl.getString("RunAnalysisAction.failRunAnalysis",//$NON-NLS-1$
runItem.getAnalysis().getName(), executed.getMessage());
log.error(errorMessage);
MessageDialogWithToggle.openError(shell,
DefaultMessagesImpl.getString("RunAnalysisAction.runAnalysis"), errorMessage); //$NON-NLS-1$
}
});
}
}
}