org.pentaho.big.data.kettle.plugins.oozie.OozieJobExecutorJobEntryController.java Source code

Java tutorial

Introduction

Here is the source code for org.pentaho.big.data.kettle.plugins.oozie.OozieJobExecutorJobEntryController.java

Source

/*******************************************************************************
 *
 * Pentaho Big Data
 *
 * Copyright (C) 2002-2017 by Hitachi Vantara : http://www.pentaho.com
 *
 *******************************************************************************
 *
 * 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.pentaho.big.data.kettle.plugins.oozie;

import com.google.common.annotations.VisibleForTesting;
import org.apache.commons.vfs2.FileObject;
import org.eclipse.swt.widgets.Shell;
import org.pentaho.big.data.api.cluster.NamedCluster;
import org.pentaho.big.data.kettle.plugins.job.AbstractJobEntryController;
import org.pentaho.big.data.kettle.plugins.job.BlockableJobConfig;
import org.pentaho.big.data.kettle.plugins.job.JobEntryMode;
import org.pentaho.big.data.kettle.plugins.job.PropertyEntry;
import org.pentaho.big.data.plugins.common.ui.HadoopClusterDelegateImpl;
import org.pentaho.di.core.exception.KettleFileException;
import org.pentaho.di.core.util.StringUtil;
import org.pentaho.di.core.vfs.KettleVFS;
import org.pentaho.di.i18n.BaseMessages;
import org.pentaho.di.job.JobMeta;
import org.pentaho.metastore.api.exceptions.MetaStoreException;
import org.pentaho.ui.xul.XulDomContainer;
import org.pentaho.ui.xul.binding.Binding;
import org.pentaho.ui.xul.binding.BindingConvertor;
import org.pentaho.ui.xul.binding.BindingFactory;
import org.pentaho.ui.xul.components.XulMenuList;
import org.pentaho.ui.xul.containers.XulDialog;
import org.pentaho.ui.xul.containers.XulTree;
import org.pentaho.ui.xul.stereotype.Bindable;
import org.pentaho.ui.xul.util.AbstractModelList;
import org.pentaho.vfs.ui.VfsFileChooserDialog;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Properties;

/**
 * User: RFellows Date: 6/4/12
 */
public class OozieJobExecutorJobEntryController
        extends AbstractJobEntryController<OozieJobExecutorConfig, OozieJobExecutorJobEntry> {

    public static final String OOZIE_JOB_EXECUTOR = "oozie-job-executor";
    private static final String VALUE = "value";
    public static final String ERROR_BROWSING_DIRECTORY = "ErrorBrowsingDirectory";
    public static final String FILE_FILTER_NAMES_PROPERTIES = "FileFilterNames.Properties";
    public static final String MODE_TOGGLE_LABEL = "mode-toggle-label";
    public static final String ADVANCED_TABLE = "advanced-table";
    public static final String CHILDREN = "children";
    public static final String ELEMENTS = "elements";
    private final HadoopClusterDelegateImpl hadoopClusterDelegate;

    protected AbstractModelList<PropertyEntry> advancedArguments;
    private transient boolean advancedArgumentsChanged = false;
    protected XulTree variablesTree = null;

    private Binding namedClustersBinding = null;

    /**
     * The text for the Quick Setup/Advanced Options mode toggle (label)
     */
    private String modeToggleLabel;

    public OozieJobExecutorJobEntryController(JobMeta jobMeta, XulDomContainer container,
            OozieJobExecutorJobEntry jobEntry, BindingFactory bindingFactory,
            HadoopClusterDelegateImpl hadoopClusterDelegate) {
        super(jobMeta, container, jobEntry, bindingFactory);
        advancedArguments = new AbstractModelList<PropertyEntry>();
        this.hadoopClusterDelegate = hadoopClusterDelegate;

        if (jobEntry.getJobConfig().getWorkflowProperties().size() > 0) {
            advancedArguments.addAll(jobEntry.getJobConfig().getWorkflowProperties());
        }
        populateNamedClusters();
    }

    @Override
    protected void beforeInit() {
        setMode(jobEntry.getJobConfig().getModeAsEnum());
        variablesTree = (XulTree) container.getDocumentRoot().getElementById(ADVANCED_TABLE);
    }

    @Override
    protected void syncModel() {

        if (!shouldUseAdvancedProperties()) {
            // sync properties to advanced args
            advancedArguments.clear();
            if (config.getWorkflowProperties() != null) {
                config.getWorkflowProperties().clear();
            }
        } else {
            if (advancedArguments.size() == 0 && !StringUtil.isEmpty(config.getOozieWorkflowConfig())) {
                preFillAdvancedArgs();
            }
            // advanced mode was used to modify/create properties
            // save the args out...
            ArrayList<PropertyEntry> m = new ArrayList<PropertyEntry>(advancedArguments);
            config.setWorkflowProperties(m);
        }

        config.setMode(jobEntryMode);
    }

    private void preFillAdvancedArgs() {
        try {
            if (jobEntry != null && config != null) {
                Properties props = jobEntry.getProperties(config);
                for (Map.Entry<Object, Object> prop : props.entrySet()) {
                    if (prop.getKey() instanceof String && prop.getValue() instanceof String) {
                        PropertyEntry pEntry = new PropertyEntry((prop.getKey()).toString(),
                                prop.getValue().toString());
                        advancedArguments.add(pEntry);
                    }
                }
            }
        } catch (Exception e) {
            // could not read in the props...
        }
    }

    /**
     * Determines if the advanced properties should be used instead of the quick-setup defined workflow properties file
     *
     * @return
     */
    protected boolean shouldUseAdvancedProperties() {
        return jobEntryMode == JobEntryMode.ADVANCED_LIST;
    }

    /**
     * make this available for unit testing
     *
     * @param mode
     */
    protected void setJobEntryMode(JobEntryMode mode) {
        this.jobEntryMode = mode;
    }

    @Override
    protected void createBindings(final OozieJobExecutorConfig config, XulDomContainer container,
            BindingFactory bindingFactory, Collection<Binding> bindings) {
        bindingFactory.setBindingType(Binding.Type.BI_DIRECTIONAL);
        bindings.add(bindingFactory.createBinding(config, BlockableJobConfig.JOB_ENTRY_NAME,
                BlockableJobConfig.JOB_ENTRY_NAME, VALUE));

        //config.setRepository( rep );
        String clusterName = config.getClusterName();

        namedClustersBinding = bindingFactory.createBinding(config.getNamedClusters(), "children", "named-clusters",
                "elements");
        try {
            namedClustersBinding.fireSourceChanged();
        } catch (Throwable ignored) {
            // Ignore
        }
        bindings.add(namedClustersBinding);
        Binding selectedNamedClusterBinding = bindingFactory.createBinding("named-clusters", "selectedIndex",
                config, "namedCluster", new BindingConvertor<Integer, NamedCluster>() {
                    public NamedCluster sourceToTarget(final Integer index) {
                        List<NamedCluster> clusters = config.getNamedClusters();
                        if (index == -1 || clusters.isEmpty()) {
                            return null;
                        }
                        return clusters.get(index);
                    }

                    public Integer targetToSource(final NamedCluster value) {
                        return config.getNamedClusters().indexOf(value);
                    }
                });
        try {
            selectedNamedClusterBinding.fireSourceChanged();
        } catch (Throwable ignored) {
            // Ignore
        }
        bindings.add(selectedNamedClusterBinding);

        selectNamedCluster(clusterName);

        bindings.add(bindingFactory.createBinding(config, OozieJobExecutorConfig.OOZIE_WORKFLOW_CONFIG,
                OozieJobExecutorConfig.OOZIE_WORKFLOW_CONFIG, VALUE));

        bindings.add(bindingFactory.createBinding(config, BlockableJobConfig.BLOCKING_POLLING_INTERVAL,
                BlockableJobConfig.BLOCKING_POLLING_INTERVAL, VALUE));

        BindingConvertor<String, Boolean> string2BooleanConvertor = new BindingConvertor<String, Boolean>() {
            @Override
            public String targetToSource(Boolean aBoolean) {
                String val = aBoolean.toString();
                return val;
            }

            @Override
            public Boolean sourceToTarget(String s) {
                Boolean val = Boolean.valueOf(s);
                return val;
            }
        };
        bindings.add(bindingFactory.createBinding(config, BlockableJobConfig.BLOCKING_EXECUTION,
                BlockableJobConfig.BLOCKING_EXECUTION, "checked", string2BooleanConvertor));

        bindingFactory.setBindingType(Binding.Type.ONE_WAY);
        bindings.add(bindingFactory.createBinding(this, "modeToggleLabel", getModeToggleLabelElementId(), VALUE));

        // only enable the polling interval text box is blocking is checked
        bindings.add(bindingFactory.createBinding(config, BlockableJobConfig.BLOCKING_EXECUTION,
                BlockableJobConfig.BLOCKING_POLLING_INTERVAL, "!disabled", string2BooleanConvertor));

        BindingConvertor<AbstractModelList<PropertyEntry>, Collection<PropertyEntry>> propsChangedBindingConvertor = new BindingConvertor<AbstractModelList<PropertyEntry>, Collection<PropertyEntry>>() {
            @Override
            public Collection<PropertyEntry> sourceToTarget(AbstractModelList<PropertyEntry> propertyEntries) {
                // user has modified the properties in advanced mode, set the flag...
                advancedArgumentsChanged = true;
                return propertyEntries;
            }

            @Override
            public AbstractModelList<PropertyEntry> targetToSource(Collection<PropertyEntry> propertyEntries) {
                // one-way convertor, don't need this
                return null;
            }
        };

        bindings.add(bindingFactory.createBinding(advancedArguments, CHILDREN, variablesTree, ELEMENTS,
                propsChangedBindingConvertor));

    }

    @VisibleForTesting
    List<NamedCluster> getNamedClusters() {
        try {
            return jobEntry.getNamedClusterService().list(jobMeta.getMetaStore());
        } catch (MetaStoreException e) {
            jobEntry.logError(e.getMessage(), e);
            return Collections.emptyList();
        }
    }

    public void selectNamedCluster(String configName) {
        @SuppressWarnings("unchecked")
        XulMenuList<NamedCluster> namedConfigMenu = (XulMenuList<NamedCluster>) container.getDocumentRoot()
                .getElementById("named-clusters"); //$NON-NLS-1$
        for (NamedCluster nc : getNamedClusters()) {
            if (configName != null && configName.equals(nc.getName())) {
                namedConfigMenu.setSelectedItem(nc);
            }
        }
    }

    public void editNamedCluster() {
        XulDialog xulDialog = (XulDialog) getXulDomContainer().getDocumentRoot()
                .getElementById("oozie-job-executor");
        Shell shell = (Shell) xulDialog.getRootObject();

        String clusterName = hadoopClusterDelegate.editNamedCluster(null, config.getNamedCluster(), shell);
        if (clusterName != null) {
            //cancel button on editing pressed, clusters not changed
            populateNamedClusters();
            selectNamedCluster(clusterName);
        }
    }

    protected void populateNamedClusters() {
        config.getNamedClusters().clear();
        config.getNamedClusters().addAll(getNamedClusters());
    }

    public void newNamedCluster() {
        XulDialog xulDialog = (XulDialog) getXulDomContainer().getDocumentRoot()
                .getElementById("oozie-job-executor");
        Shell shell = (Shell) xulDialog.getRootObject();
        String newNamedCluster = hadoopClusterDelegate.newNamedCluster(jobMeta, null, shell);
        if (newNamedCluster != null) {
            //cancel button on editing pressed, clusters not changed
            populateNamedClusters();
            selectNamedCluster(newNamedCluster);
        }
    }

    @Bindable
    public void addNewProperty() {
        advancedArgumentsChanged = true;
        try {
            advancedArguments.add(new PropertyEntry("key", "value"));
        } catch (Exception e) {
            // set elements manually to workaround a failure with adding new item when there is a cell in edit mode
            variablesTree.setElements(advancedArguments);
        }
    }

    @Bindable
    public void removeProperty() {
        advancedArgumentsChanged = true;
        Collection<PropertyEntry> selected = variablesTree.getSelectedItems();
        for (PropertyEntry pe : selected) {
            try {
                advancedArguments.remove(pe);
            } catch (Exception e) {
                // The implementation of the SwtTree selection model is buggy. if you have an item (row) selected
                // but a field is in edit mode and try to remove the item, we get a failure (sometimes).
                // just set the children manually in this case to make sure we are in sync.
                variablesTree.setElements(advancedArguments);
            }
        }
    }

    /**
     * Accept and apply the changes made in the dialog. Also, close the dialog
     */
    @Override
    @Bindable
    public void accept() {
        syncModel();

        List<String> warnings = jobEntry.getValidationWarnings(getConfig(), false);
        if (!warnings.isEmpty()) {
            StringBuilder sb = new StringBuilder();
            for (String warning : warnings) {
                sb.append(warning).append("\n");
            }
            showErrorDialog(BaseMessages.getString(OozieJobExecutorJobEntry.class, "ValidationError.Dialog.Title"),
                    sb.toString());
            return;
        }

        super.accept();
    }

    public AbstractModelList<PropertyEntry> getAdvancedArguments() {
        return advancedArguments;
    }

    public void setAdvancedArguments(AbstractModelList<PropertyEntry> advancedArguments) {
        advancedArgumentsChanged = true;
        this.advancedArguments = advancedArguments;
    }

    @Bindable
    public boolean isAdvancedArgumentsChanged() {
        return advancedArgumentsChanged;
    }

    @Override
    protected String getDialogElementId() {
        return OOZIE_JOB_EXECUTOR;
    }

    /**
     * @return the id of the element responsible for toggling between "Quick Setup" and "Advanced Options" modes
     */
    @Bindable
    public String getModeToggleLabelElementId() {
        return MODE_TOGGLE_LABEL;
    }

    @Bindable
    public String getModeToggleLabel() {
        return modeToggleLabel;
    }

    @Bindable
    public void setModeToggleLabel(String modeToggleLabel) {
        String prev = this.modeToggleLabel;
        this.modeToggleLabel = modeToggleLabel;
        firePropertyChange("modeToggleLabel", prev, modeToggleLabel);
    }

    @Override
    protected void setModeToggleLabel(JobEntryMode mode) {
        switch (mode) {
        case ADVANCED_LIST:
            setModeToggleLabel(
                    BaseMessages.getString(OozieJobExecutorJobEntry.class, "Oozie.AdvancedOptions.Button.Text"));
            break;
        case QUICK_SETUP:
            setModeToggleLabel(
                    BaseMessages.getString(OozieJobExecutorJobEntry.class, "Oozie.BasicOptions.Button.Text"));
            break;
        default:
            throw new RuntimeException("unsupported JobEntryMode");
        }
    }

    /**
     * Make sure everything required is entered and valid
     */
    @Bindable
    public void testSettings() {
        syncModel();
        try {
            List<String> warnings = jobEntry.getValidationWarnings(getConfig());
            if (!warnings.isEmpty()) {
                StringBuilder sb = new StringBuilder();
                for (String warning : warnings) {
                    sb.append(warning).append("\n");
                }
                showErrorDialog(
                        BaseMessages.getString(OozieJobExecutorJobEntry.class, "ValidationError.Dialog.Title"),
                        sb.toString());
                return;
            }
        } catch (RuntimeException re) {
            showErrorDialog(BaseMessages.getString(OozieJobExecutorJobEntry.class, "ValidationError.Dialog.Title"),
                    re.getMessage());
            throw re;
        }
        showInfoDialog(BaseMessages.getString(OozieJobExecutorJobEntry.class, "Info.Dialog.Title"),
                BaseMessages.getString(OozieJobExecutorJobEntry.class, "ValidationMsg.OK"));
    }

    /**
     * Open the VFS file browser to allow for selection of the workflow job properties configuration file.
     */
    @Bindable
    public void browseWorkflowConfig() {
        FileObject path = null;
        try {
            path = KettleVFS.getFileObject(
                    jobEntry.getVariableSpace().environmentSubstitute(getConfig().getOozieWorkflowConfig()));
        } catch (Exception e) {
            // Ignore, use null (default VFS browse path)
        }
        try {
            FileObject exportDir = browseVfs(null, path, VfsFileChooserDialog.VFS_DIALOG_OPEN_DIRECTORY, null, true,
                    "file");
            if (exportDir != null) {
                getConfig().setOozieWorkflowConfig(exportDir.getName().getURI());
            }
        } catch (KettleFileException e) {
            getJobEntry().logError(BaseMessages.getString(OozieJobExecutorJobEntry.class, ERROR_BROWSING_DIRECTORY),
                    e);
        }
    }

    @Override
    protected String[] getFileFilters() {
        return new String[] { "*.properties" };
    }

    @Override
    protected String[] getFileFilterNames() {
        return new String[] {
                BaseMessages.getString(OozieJobExecutorJobEntry.class, FILE_FILTER_NAMES_PROPERTIES) };
    }

}