com.nextep.designer.synch.ui.services.impl.SynchronizationUIService.java Source code

Java tutorial

Introduction

Here is the source code for com.nextep.designer.synch.ui.services.impl.SynchronizationUIService.java

Source

/*******************************************************************************
 * Copyright (c) 2011 neXtep Software and contributors.
 * All rights reserved.
 *
 * This file is part of neXtep designer.
 *
 * NeXtep designer is free software: you can redistribute it 
 * and/or modify it under the terms of the GNU General Public 
 * License as published by the Free Software Foundation, either 
 * version 3 of the License, or any later version.
 *
 * NeXtep designer 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Foobar.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Contributors:
 *     neXtep Softwares - initial API and implementation
 *******************************************************************************/
package com.nextep.designer.synch.ui.services.impl;

import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;

import org.apache.commons.collections.map.MultiValueMap;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.eclipse.core.commands.Command;
import org.eclipse.core.commands.State;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.core.runtime.jobs.IJobChangeEvent;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.core.runtime.jobs.JobChangeAdapter;
import org.eclipse.core.runtime.preferences.InstanceScope;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.window.Window;
import org.eclipse.jface.wizard.WizardDialog;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IMemento;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.WorkbenchException;
import org.eclipse.ui.XMLMemento;
import org.eclipse.ui.commands.ICommandService;
import org.eclipse.ui.handlers.RegistryToggleState;
import org.osgi.service.prefs.BackingStoreException;

import com.nextep.datadesigner.dbgm.model.IBasicTable;
import com.nextep.datadesigner.exception.CancelException;
import com.nextep.datadesigner.exception.ErrorException;
import com.nextep.datadesigner.exception.UnresolvedItemException;
import com.nextep.datadesigner.impl.Reference;
import com.nextep.datadesigner.model.IElementType;
import com.nextep.datadesigner.model.IReference;
import com.nextep.datadesigner.model.IReferenceable;
import com.nextep.datadesigner.model.IReferencer;
import com.nextep.datadesigner.model.UID;
import com.nextep.datadesigner.sqlgen.impl.SQLWrapperScript;
import com.nextep.datadesigner.sqlgen.model.ISQLScript;
import com.nextep.datadesigner.vcs.services.VersionHelper;
import com.nextep.designer.core.CorePlugin;
import com.nextep.designer.core.model.DBVendor;
import com.nextep.designer.core.model.IConnection;
import com.nextep.designer.core.model.IReferenceManager;
import com.nextep.designer.dbgm.ui.editors.DomainEditorComponent;
import com.nextep.designer.sqlgen.ui.SQLScriptEditorInput;
import com.nextep.designer.sqlgen.ui.editors.SQLEditor;
import com.nextep.designer.sqlgen.ui.editors.SQLMultiEditor;
import com.nextep.designer.sqlgen.ui.model.SubmitionManager;
import com.nextep.designer.sqlgen.ui.services.SQLEditorUIServices;
import com.nextep.designer.synch.model.ISynchronizationListener;
import com.nextep.designer.synch.model.ISynchronizationResult;
import com.nextep.designer.synch.services.ISynchronizationService;
import com.nextep.designer.synch.ui.SynchUIMessages;
import com.nextep.designer.synch.ui.SynchUIPlugin;
import com.nextep.designer.synch.ui.dialogs.TableSelectionDialog;
import com.nextep.designer.synch.ui.services.ISynchronizationUIService;
import com.nextep.designer.ui.dialogs.ComponentWizard;
import com.nextep.designer.ui.dialogs.TitleAreaDialogWrapper;
import com.nextep.designer.ui.helpers.BlockingJob;
import com.nextep.designer.ui.helpers.UIHelper;
import com.nextep.designer.ui.model.IUIComponent;
import com.nextep.designer.ui.model.base.RunnableWithReturnedValue;
import com.nextep.designer.vcs.model.ComparedElement;
import com.nextep.designer.vcs.model.ComparisonScope;
import com.nextep.designer.vcs.model.DifferenceType;
import com.nextep.designer.vcs.model.IComparisonItem;
import com.nextep.designer.vcs.model.IVersionContainer;
import com.nextep.designer.vcs.model.IVersionable;
import com.nextep.designer.vcs.model.IWorkspace;
import com.nextep.designer.vcs.services.IWorkspaceService;

/**
 * Default implementation of the {@link ISynchronizationUIService}
 * 
 * @author Christophe Fondacci
 */
public class SynchronizationUIService implements ISynchronizationUIService, ISynchronizationListener {

    private final static String PREF_DATASYNC_TABLES_KEY = "com.neXtep.designer.synch.ui.synchedTables";
    private final static String DATASYNC_TABLES_ROOT = "synchedTables";
    private final static String DATASYNC_TABLE_REFID = "ref";
    private final Set<ISynchronizationListener> listeners;
    private ISynchronizationResult lastResult;
    private static final Log log = LogFactory.getLog(SynchronizationUIService.class);
    private ISynchronizationService synchronizationService;
    private IWorkspaceService workspaceService;

    // private final static Log LOGGER =
    // LogFactory.getLog(SynchronizationUIService.class);

    @Override
    public ISynchronizationResult buildSynchronizationResult(IVersionContainer container, IConnection connection,
            ComparisonScope scope, IProgressMonitor monitor) {
        return synchronizationService.buildSynchronizationResult(container, connection, scope, monitor);
    }

    public void setSynchronizationService(ISynchronizationService synchronizationService) {
        this.synchronizationService = synchronizationService;
        synchronizationService.addSynchronizationListener(this);
    }

    public void unsetSynchronizationService(ISynchronizationService synchronizationService) {
        synchronizationService.removeSynchronizationListener(this);
        this.synchronizationService = null;
    }

    public SynchronizationUIService() {
        listeners = new HashSet<ISynchronizationListener>();
    }

    @Override
    public void addSynchronizationListener(final ISynchronizationListener listener) {
        listeners.add(listener);
    }

    @Override
    public void removeSynchronizationListener(ISynchronizationListener listener) {
        listeners.remove(listener);
    }

    @Override
    public void synchronize(final IVersionContainer container, final IConnection connection,
            IProgressMonitor monitor) {
        // Checking whether we are generating for current vendor
        final DBVendor currentVendor = workspaceService.getCurrentWorkspace().getDBVendor();
        if (connection.getDBVendor() != currentVendor) {
            final IUIComponent domainDialog = new DomainEditorComponent();

            // Initializing dialog
            ComponentWizard wiz = new ComponentWizard(SynchUIMessages.getString("wizard.synch.crossvendor"), //$NON-NLS-1$
                    Arrays.asList(domainDialog));
            WizardDialog dlg = new WizardDialog(UIHelper.getShell(), wiz) {
                @Override
                protected Point getInitialSize() {
                    return new Point(600, 540);
                }

            };
            dlg.setBlockOnOpen(true);
            dlg.open();
            // Checking cancel state
            if (wiz.isCanceled()) {
                return;
            }
        }
        Job j = new BlockingJob(SynchUIMessages.getString("synch.job.name")) { //$NON-NLS-1$

            @Override
            protected IStatus run(IProgressMonitor monitor) {
                try {
                    synchronizationService.synchronize(container, connection, monitor);
                } catch (final ErrorException e) {
                    log.error(SynchUIMessages.getString("synch.job.error"), e); //$NON-NLS-1$
                    this.addJobChangeListener(new JobChangeAdapter() {

                        @Override
                        public void done(IJobChangeEvent event) {
                            PlatformUI.getWorkbench().getDisplay().syncExec(new Runnable() {

                                @Override
                                public void run() {
                                    MessageDialog.openWarning(null, SynchUIMessages.getString("synch.job.error"), e //$NON-NLS-1$
                                            .getMessage());
                                }
                            });

                        }

                    });
                } catch (CancelException e) {
                    return Status.CANCEL_STATUS;
                }
                return Status.OK_STATUS;
            }
        };
        j.setUser(true);
        j.setPriority(Job.BUILD);
        j.schedule();
    }

    @Override
    public void buildScript(final ISynchronizationResult synchResult) {
        Job j = new BlockingJob(SynchUIMessages.getString("synch.job.generateScript")) { //$NON-NLS-1$

            @Override
            protected IStatus run(IProgressMonitor parentMonitor) {
                buildScript(synchResult, parentMonitor);
                return Status.OK_STATUS;
            }
        };
        j.setUser(true);
        j.schedule();
    }

    @Override
    public void buildScript(ISynchronizationResult synchResult, IProgressMonitor parentMonitor) {
        final SubMonitor monitor = SubMonitor.convert(parentMonitor,
                SynchUIMessages.getString("synch.job.generateScript"), 30); //$NON-NLS-1$
        synchronizationService.buildScript(synchResult, monitor.newChild(10));
        final IEditorPart part = showScript(synchResult);
        if (part != null) {
            Display.getDefault().syncExec(new Runnable() {

                @Override
                public void run() {
                    part.doSave(monitor.newChild(20));
                }
            });
        }
        monitor.done();
    }

    @Override
    public IEditorPart showScript(final ISynchronizationResult synchResult) {
        // Opening in the UI thread every generated script
        if (synchResult.getGeneratedScript() != null) {
            RunnableWithReturnedValue<IEditorPart> runnable = new RunnableWithReturnedValue<IEditorPart>() {

                @Override
                public void run() {
                    try {
                        final ISQLScript s = synchResult.getGeneratedScript();
                        final String editorId = s instanceof SQLWrapperScript ? SQLMultiEditor.EDITOR_ID
                                : SQLEditor.EDITOR_ID;
                        returnedValue = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage()
                                .openEditor(new SQLScriptEditorInput(s), editorId);

                    } catch (PartInitException e) {
                        throw new ErrorException(MessageFormat
                                .format(SynchUIMessages.getString("synch.openEditorFailed"), e.getMessage()), e); //$NON-NLS-1$
                    }
                }
            };
            Display.getDefault().syncExec(runnable);
            return runnable.returnedValue;
        }
        return null;
    }

    @Override
    public void adjustParents(IComparisonItem item) {
        if (item == null)
            return;
        boolean childChecked = false;
        boolean allChildChecked = item.getSubItems().size() > 0;
        for (IComparisonItem subItem : item.getSubItems()) {
            if (subItem.getMergeInfo().getMergeProposal() != subItem.getTarget()) {
                if (subItem.getDifferenceType() != DifferenceType.EQUALS) {
                    if (!childChecked) {
                        childChecked = true;
                    }
                }
            } else {
                allChildChecked = false;
            }
        }
        if (allChildChecked) {
            // Avoiding recursive loops when nothing has changed
            if (item.getMergeInfo().getMergeProposal() == item.getSource()) {
                return;
            }
            // Applying new value
            item.getMergeInfo().setMergeProposal(item.getSource());
        } else if (childChecked) {
            // Avoiding recursive loops when nothing has changed
            if (item.getMergeInfo().getMergeProposal() == null) {
                return;
            }
            // Applying change
            item.getMergeInfo().setMergeProposal(null);
        } else {
            // Avoiding recursive loops when nothing has changed
            if (item.getMergeInfo().getMergeProposal() == item.getTarget()) {
                return;
            }
            // Applying new value
            item.getMergeInfo().setMergeProposal(item.getTarget());
        }
        adjustParents(item.getParent());
    }

    @Override
    public void selectProposal(IComparisonItem item, ComparedElement selection, boolean recurseDependentElements) {
        MultiValueMap invRefMap = null;
        if (recurseDependentElements) {
            invRefMap = CorePlugin.getService(IReferenceManager.class).getReverseDependenciesMap();
        } else {
            invRefMap = new MultiValueMap();
        }
        selectProposal(item, selection, recurseDependentElements, invRefMap);
    }

    @SuppressWarnings("unchecked")
    private void selectProposal(IComparisonItem item, ComparedElement selection, boolean recurseDependentElements,
            MultiValueMap reverseDependencyMap) {
        if (item == null || item.getDifferenceType() == DifferenceType.EQUALS)
            return;
        final IReferenceable toSelectItem = selection.get(item);
        if (item.getMergeInfo().getMergeProposal() == toSelectItem) {
            return;
        }
        item.getMergeInfo().setMergeProposal(toSelectItem);
        for (IComparisonItem subItem : item.getSubItems()) {
            if (subItem.getDifferenceType() != DifferenceType.EQUALS) {
                selectProposal(subItem, selection, false);
            }
        }

        if (recurseDependentElements) {
            // When we unselect an element, we recursively unselect any element
            // which depend on it
            if (selection.isTarget()) {
                Collection<IReferencer> dependentRefs = (Collection<IReferencer>) reverseDependencyMap
                        .get(item.getReference());
                if (dependentRefs != null) {
                    for (IReferencer r : dependentRefs) {
                        if (r instanceof IReferenceable) {
                            final IComparisonItem dependentItem = lastResult
                                    .getComparisonItemFor(((IReferenceable) r).getReference());
                            selectProposal(dependentItem, selection, true);
                        }
                    }
                }
            }
            // When we select an element, we recursively select elements whose
            // current element is
            // depending
            if (selection.isSource()) {
                if (item.getSource() instanceof IReferencer) {
                    final IReferencer referencer = (IReferencer) item.getSource();
                    Collection<IReference> dependencies = referencer.getReferenceDependencies();
                    for (IReference d : dependencies) {
                        final IComparisonItem dependentItem = lastResult.getComparisonItemFor(d);
                        if (dependentItem != null
                                && dependentItem.getMergeInfo().getMergeProposal() != dependentItem.getSource()) {
                            selectProposal(dependentItem, selection, true);
                        }
                    }
                }
            }
        }
        // Adjusting the dirty flag
        if (lastResult != null) {
            lastResult.setDirty(true);
        }
    }

    @Override
    public void selectProposal(IComparisonItem item, ComparedElement selection) {
        MultiValueMap invRefMap = CorePlugin.getService(IReferenceManager.class).getReverseDependenciesMap();
        selectProposal(item, selection, invRefMap);
    }

    @Override
    public void selectProposal(IComparisonItem item, ComparedElement selection,
            MultiValueMap reverseDependenciesMap) {
        boolean importDependencies = false;
        final ICommandService service = (ICommandService) PlatformUI.getWorkbench().getActiveWorkbenchWindow()
                .getService(ICommandService.class);
        final Command cmd = service.getCommand("com.neXtep.designer.synch.ui.toggleDependencies"); //$NON-NLS-1$
        final State state = cmd.getState(RegistryToggleState.STATE_ID);
        importDependencies = (Boolean) state.getValue();
        selectProposal(item, selection, importDependencies, reverseDependenciesMap);
    }

    @Override
    public void changeSynchronizationScope(final ComparisonScope scope, final ISynchronizationResult result,
            final IProgressMonitor monitor) {
        Job j = new BlockingJob("") { //$NON-NLS-1$

            @Override
            protected IStatus run(IProgressMonitor monitor) {
                synchronizationService.changeSynchronizationScope(scope, result, monitor);
                return Status.OK_STATUS;
            }
        };
        j.setUser(true);
        j.setPriority(Job.BUILD);
        j.schedule();
    }

    @Override
    public void clearSynchronization() {
        synchronizationService.clearSynchronization();
    }

    @Override
    public void submit(final ISynchronizationResult result) {
        // If result has not yet been generated we generate and submit without
        // notice
        if (result.getGenerationResult() == null) {
            synchronizationService.buildScript(result, null);
        }
        // If result is dirty we need to regenerate it so we prompt
        if (result.isDirty()) {
            final boolean confirmed = MessageDialog.openConfirm(Display.getCurrent().getActiveShell(),
                    SynchUIMessages.getString("service.synch.needsSqlGenerationTitle"), //$NON-NLS-1$
                    SynchUIMessages.getString("service.synch.needsSqlGenerationMsg")); //$NON-NLS-1$
            if (confirmed) {
                buildScript(result);
            }
        } else {
            // Building UI scripts
            final String jobName = MessageFormat.format(SynchUIMessages.getString("synch.job.submitScript"), //$NON-NLS-1$
                    result.getConnection().getName());
            Job j = new Job(jobName) {

                @Override
                protected IStatus run(IProgressMonitor monitor) {
                    try {
                        monitor.beginTask(jobName, 1);
                        monitor.subTask(""); //$NON-NLS-1$
                        final ISQLScript script = result.getGeneratedScript();
                        SubmitionManager.submit(result.getConnection(), script, result.getGenerationResult(),
                                monitor);
                        SQLEditorUIServices.getInstance().annotateVisibleEditor();

                        showScript(result);
                        return Status.OK_STATUS;
                    } catch (CancelException e) {
                        return Status.CANCEL_STATUS;
                    }
                }
            };
            j.setUser(true);
            j.schedule();
        }
    }

    @Override
    public void newSynchronization(final ISynchronizationResult synchronizationResult) {
        this.lastResult = synchronizationResult;
        Display.getDefault().syncExec(new Runnable() {

            @Override
            public void run() {
                for (ISynchronizationListener listener : new ArrayList<ISynchronizationListener>(listeners)) {
                    listener.newSynchronization(synchronizationResult);
                }
            }
        });
    }

    @Override
    public void scopeChanged(final ComparisonScope scope) {
        Display.getDefault().syncExec(new Runnable() {

            @Override
            public void run() {
                for (ISynchronizationListener listener : listeners) {
                    listener.scopeChanged(scope);
                }
            }
        });
    }

    @Override
    public void synchronizeData(final IConnection conn, IProgressMonitor monitor,
            IVersionable<?>... synchronizedItems) {
        // Retrieving every table from workspace
        final Collection<IVersionable<?>> vTables = VersionHelper.getAllVersionables(
                workspaceService.getCurrentWorkspace(), IElementType.getInstance(IBasicTable.TYPE_ID));
        // Initializing our editor
        final Collection<IVersionable<?>> preselectedTables = getDataSynchronizationTables();
        TableSelectionDialog dlg = new TableSelectionDialog(preselectedTables, vTables);
        TitleAreaDialogWrapper wrapper = new TitleAreaDialogWrapper(
                PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(), dlg,
                SWT.CLOSE | SWT.RESIZE | SWT.TITLE | SWT.BORDER);
        wrapper.setBlockOnOpen(true);
        int result = wrapper.open();
        if (result == Window.OK) {
            final Collection<IVersionable<?>> selectedTables = dlg.getSelection();
            saveDataSynchronizationTables(selectedTables);
            Job j = new Job("Synchronizing data...") {

                @Override
                protected IStatus run(IProgressMonitor monitor) {
                    synchronizationService.synchronizeData(conn, monitor,
                            selectedTables.toArray(new IVersionable<?>[selectedTables.size()]));
                    return Status.OK_STATUS;
                }
            };
            j.setUser(true);
            j.schedule();
        }
    }

    @Override
    public Collection<IVersionable<?>> getDataSynchronizationTables() {
        Collection<IVersionable<?>> tables = new ArrayList<IVersionable<?>>();
        IPreferenceStore store = SynchUIPlugin.getDefault().getPreferenceStore();
        final String prefKey = buildPreferenceKey(PREF_DATASYNC_TABLES_KEY);
        String preference = store.getString(prefKey);
        boolean needsResave = false;
        if (preference != null && !"".equals(preference)) {
            StringReader reader = new StringReader(preference);
            try {
                IMemento memento = XMLMemento.createReadRoot(reader);
                for (IMemento m : memento.getChildren(DATASYNC_TABLE_REFID)) {
                    final String refId = m.getID();
                    if (refId != null) {
                        IReference r = new Reference(IElementType.getInstance(IBasicTable.TYPE_ID), null, null);
                        r.setUID(new UID(Long.valueOf(refId)));
                        try {
                            IReferenceable table = VersionHelper.getReferencedItem(r);
                            tables.add(VersionHelper.getVersionable(table));
                        } catch (UnresolvedItemException e) {
                            log.warn(
                                    "Could not retrieve all tables to synchronized, removing element from data synchronization, reason is : "
                                            + e.getMessage(),
                                    e);
                            needsResave = true;
                        }
                    }
                }
            } catch (WorkbenchException e) {
                log.warn("Unable to retrieve predefined tables to synchronize", e); //$NON-NLS-1$
            }
        }
        // Resaving tables if we lost some references
        if (needsResave) {
            saveDataSynchronizationTables(tables);
        }
        return tables;
    }

    private void saveDataSynchronizationTables(Collection<IVersionable<?>> synchronizedTables) {
        XMLMemento memento = XMLMemento.createWriteRoot(DATASYNC_TABLES_ROOT);
        for (IVersionable<?> synchedTable : synchronizedTables) {
            memento.createChild(DATASYNC_TABLE_REFID, synchedTable.getReference().getUID().toString());
        }
        final String prefKey = buildPreferenceKey(PREF_DATASYNC_TABLES_KEY);
        StringWriter writer = new StringWriter();
        try {
            memento.save(writer);
        } catch (IOException e) {
            log.warn("Error occurred while saving synchronized tables: " + e.getMessage(), e);
        }
        IPreferenceStore store = SynchUIPlugin.getDefault().getPreferenceStore();
        store.putValue(prefKey, writer.toString());
        try {
            new InstanceScope().getNode(SynchUIPlugin.PLUGIN_ID).flush();
        } catch (BackingStoreException e) {
            log.warn("Error occurred while saving synchronized tables: " + e.getMessage(), e);
        }
    }

    private String buildPreferenceKey(String key) {
        StringBuilder buf = new StringBuilder();
        IConnection repositoryConnection = CorePlugin.getRepositoryService().getRepositoryConnection();
        if (repositoryConnection != null) {
            buf.append(repositoryConnection.getDatabase() + "." + repositoryConnection.getServerIP() //$NON-NLS-1$
                    + "." + repositoryConnection.getLogin() + "."); ////$NON-NLS-1$ //$NON-NLS-2$ 
        }
        final IWorkspace currentView = workspaceService.getCurrentWorkspace();
        if (currentView != null) {
            buf.append(currentView.getId());
            buf.append('.');
        }
        buf.append(key);
        return buf.toString();
    }

    public void setWorkspaceService(IWorkspaceService workspaceService) {
        this.workspaceService = workspaceService;
    }
}