All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.datacleaner.windows.HdfsUrlChooser Maven / Gradle / Ivy

/**
 * DataCleaner (community edition)
 * Copyright (C) 2014 Free Software Foundation, Inc.
 *
 * This copyrighted material is made available to anyone wishing to use, modify,
 * copy, or redistribute it subject to the terms and conditions of the GNU
 * Lesser General Public License, as published by the Free Software Foundation.
 *
 * This program 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 Lesser General Public License
 * for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this distribution; if not, write to:
 * Free Software Foundation, Inc.
 * 51 Franklin Street, Fifth Floor
 * Boston, MA  02110-1301  USA
 */
package org.datacleaner.windows;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.HeadlessException;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;

import javax.swing.AbstractListModel;
import javax.swing.ComboBoxModel;
import javax.swing.DefaultListCellRenderer;
import javax.swing.Icon;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.UIManager;
import javax.swing.WindowConstants;
import javax.ws.rs.core.UriBuilder;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileStatus;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.datacleaner.bootstrap.DCWindowContext;
import org.datacleaner.bootstrap.WindowContext;
import org.datacleaner.configuration.ServerInformation;
import org.datacleaner.configuration.ServerInformationCatalog;
import org.datacleaner.configuration.ServerInformationCatalogImpl;
import org.datacleaner.panels.DCPanel;
import org.datacleaner.server.DirectConnectionHadoopClusterInformation;
import org.datacleaner.server.DirectoryBasedHadoopClusterInformation;
import org.datacleaner.server.EnvironmentBasedHadoopClusterInformation;
import org.datacleaner.server.HadoopClusterInformation;
import org.datacleaner.util.HadoopResource;
import org.datacleaner.util.HdfsUtils;
import org.datacleaner.util.LookAndFeelManager;
import org.datacleaner.util.WidgetFactory;
import org.datacleaner.util.WidgetUtils;
import org.datacleaner.widgets.DCComboBox;
import org.datacleaner.widgets.DCLabel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class HdfsUrlChooser extends JComponent {
    public enum OpenType {
        LOAD("Open"), SAVE("Save");

        private final String _title;

        OpenType(final String title) {
            _title = title;
        }

        public String getTitle() {
            return _title;
        }
    }

    static class HdfsFileListRenderer extends DefaultListCellRenderer {

        private static final long serialVersionUID = 1L;

        public Component getListCellRendererComponent(final JList list, final Object value, final int index,
                final boolean isSelected, final boolean cellHasFocus) {

            super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);

            if (value == null) {
                setText("");
                return this;
            }

            final FileStatus fileStatus = (FileStatus) value;

            final String name;
            if (fileStatus.isDirectory()) {
                setIcon(DIRECTORY_ICON);
                name = fileStatus.getPath().getName();
            } else if (fileStatus.isFile()) {
                setIcon(FILE_ICON);
                name = fileStatus.getPath().getName();
            } else {
                name = "..";

                setIcon(LEVEL_UP_ICON);
            }

            setText(name);

            return this;
        }
    }

    static class IndentIcon implements Icon {
        Icon _icon = DIRECTORY_ICON;
        int _depth = 0;

        public void paintIcon(final Component c, final Graphics graphics, final int x, final int y) {

            if (c.getComponentOrientation().isLeftToRight()) {
                _icon.paintIcon(c, graphics, x + _depth * SPACE, y);
            } else {
                _icon.paintIcon(c, graphics, x, y);
            }
        }

        public int getIconWidth() {
            return _icon.getIconWidth() + _depth * SPACE;
        }

        public int getIconHeight() {
            return _icon.getIconHeight();
        }
    }

    class ServerComboBoxRenderer extends DefaultListCellRenderer {

        private static final long serialVersionUID = 1L;

        IndentIcon ii = new IndentIcon();

        public Component getListCellRendererComponent(final JList list, final Object value, final int index,
                final boolean isSelected, final boolean cellHasFocus) {

            super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);

            if (value == null) {
                setText("");
                return this;
            }

            final Path directory = (Path) value;
            final String directoryName = directory.getName();
            if (directoryName.isEmpty()) {
                final URI uri = directory.toUri();
                setText(uri.toString());
            } else {
                setText(directoryName);
            }

            final int depth = _directoryComboBoxModel.getDepth(index);
            ii._depth = depth;
            if (depth == 0) {
                ii._icon = COMPUTER_ICON;
            }
            setIcon(ii);

            return this;
        }
    }

    class HdfsComboBoxModel extends AbstractListModel implements ComboBoxModel {

        private static final long serialVersionUID = 1L;


        LinkedList directories = new LinkedList<>();
        int[] depths = null;

        Path selectedDirectory = null;

        public HdfsComboBoxModel() {
            updateDirectories();
        }

        public void updateDirectories() {
            final Path dir = getCurrentDirectory();
            if (dir != null) {
                addItem(dir);
            }
        }

        private void addItem(final Path directory) {
            if (directory == null) {
                return;
            }

            directories.clear();
            directories.add(getRoot());

            Path path = directory;
            final List paths = new ArrayList<>(10);
            do {
                paths.add(path);
            } while ((path = path.getParent()) != null);

            final int pathCount = paths.size();

            for (int i = 0; i < pathCount; i++) {
                path = paths.get(i);
                if (directories.contains(path)) {
                    final int topIndex = directories.indexOf(path);
                    for (int j = i - 1; j >= 0; j--) {
                        directories.add(topIndex + i - j, paths.get(j));
                    }
                    break;
                }
            }
            calculateDepths();
            setSelectedItem(directory);
        }

        private void calculateDepths() {
            depths = new int[directories.size()];
            for (int i = 0; i < depths.length; i++) {
                final Path dir = directories.get(i);
                final Path parent = dir.getParent();
                depths[i] = 0;
                if (parent != null) {
                    for (int j = i - 1; j >= 0; j--) {
                        if (parent.equals(directories.get(j))) {
                            depths[i] = depths[j] + 1;
                            break;
                        }
                    }
                }
            }
        }

        public int getDepth(final int index) {
            if (depths == null || index < 0 || index > depths.length) {
                return 0;
            }

            return depths[index];
        }

        @Override
        public Path getSelectedItem() {
            return selectedDirectory;
        }

        @Override
        public void setSelectedItem(final Object selectedDirectory) {
            this.selectedDirectory = (Path) selectedDirectory;
            fireContentsChanged(this, -1, -1);
        }

        @Override
        public int getSize() {
            return directories.size();
        }

        @Override
        public Path getElementAt(final int index) {
            return directories.get(index);
        }

    }

    public class HdfsDirectoryModel extends AbstractListModel implements PropertyChangeListener {

        private static final long serialVersionUID = 1L;

        private FileStatus[] _files;

        public HdfsDirectoryModel() {
            updateFileList();
        }

        private void updateFileList() {
            if (_fileSystem == null || _currentDirectory == null) {
                _files = new FileStatus[0];
                return;
            }

            FileStatus[] fileStatuses;
            try {
                fileStatuses = _fileSystem.listStatus(_currentDirectory);
                // Natural ordering is the URL
                Arrays.sort(fileStatuses);

                // Add pointer to the parent directory
                if (!_currentDirectory.isRoot()) {
                    final FileStatus[] newFileStatuses = new FileStatus[fileStatuses.length + 1];
                    final FileStatus levelUp = new FileStatus();
                    levelUp.setSymlink(_currentDirectory.getParent());
                    newFileStatuses[0] = levelUp;

                    System.arraycopy(fileStatuses, 0, newFileStatuses, 1, fileStatuses.length);
                    fileStatuses = newFileStatuses;
                }
            } catch (final IOException e) {
                fileStatuses = new FileStatus[0];
            }

            synchronized (this) {
                _files = fileStatuses;
                fireContentsChanged(this, 0, getSize() - 1);
            }
        }

        @Override
        public int getSize() {
            synchronized (this) {
                return _files.length;
            }
        }

        @Override
        public FileStatus getElementAt(final int index) {
            synchronized (this) {
                return _files[index];
            }
        }

        @Override
        public void propertyChange(final PropertyChangeEvent evt) {
            updateFileList();
        }
    }

    public static final Icon DIRECTORY_ICON = UIManager.getIcon("FileView.directoryIcon");
    public static final Icon FILE_ICON = UIManager.getIcon("FileView.fileIcon");
    public static final Icon COMPUTER_ICON = UIManager.getIcon("FileView.computerIcon");
    public static final Icon LEVEL_UP_ICON = UIManager.getLookAndFeelDefaults().getIcon("FileChooser.upFolderIcon");
    protected static final Logger logger = LoggerFactory.getLogger(HdfsUrlChooser.class);
    private static final long serialVersionUID = 1L;
    private static final int DEFAULT_WIDTH = 600;
    private static final int SPACE = 10;
    public static String HDFS_SCHEME = "hdfs";
    private final OpenType _openType;
    private final JList _fileList;

    private FileSystem _fileSystem;
    private JDialog _dialog = null;
    private Path _currentDirectory;
    private Path _selectedFile;

    private HdfsComboBoxModel _directoryComboBoxModel = new HdfsComboBoxModel();

    HdfsUrlChooser(URI uri, final OpenType openType) {
        if (uri != null) {
            if (!HDFS_SCHEME.equals(uri.getScheme())) {
                throw new IllegalArgumentException("Only HDFS allowed");
            }

            uri = UriBuilder.fromUri(uri).replaceQuery(null).fragment(null).build();
            HdfsUtils.getFileSystemFromUri(uri);
            _currentDirectory = new Path(uri);
        }

        _openType = openType;
        final DCLabel lookInLabel = DCLabel.dark("Look in:");
        final DCComboBox pathsComboBox = new DCComboBox<>(_directoryComboBoxModel);
        pathsComboBox.setRenderer(new ServerComboBoxRenderer());
        pathsComboBox.setMinimumSize(new Dimension(DEFAULT_WIDTH, 40));
        pathsComboBox.addListener(new DCComboBox.Listener() {
            @Override
            public void onItemSelected(final Path directory) {
                if (!directory.isAbsoluteAndSchemeAuthorityNull()) {
                    _fileSystem = HdfsUtils.getFileSystemFromUri(directory.toUri());
                }
                _currentDirectory = directory;
                ((HdfsDirectoryModel) _fileList.getModel()).updateFileList();
            }
        });
        _fileList = new JList<>(new HdfsDirectoryModel());
        _fileList.setLayoutOrientation(JList.VERTICAL_WRAP);
        _fileList.setPreferredSize(new Dimension(DEFAULT_WIDTH, 350));
        _fileList.setBorder(WidgetUtils.BORDER_THIN);
        _fileList.setCellRenderer(new HdfsFileListRenderer());
        _fileList.addMouseListener(new MouseAdapter() {
            public void mouseClicked(final MouseEvent evt) {
                if (evt.getClickCount() == 2) {
                    selectOrBrowsePath(false);
                }
            }

            @Override
            public void mousePressed(final MouseEvent e) {
                tryPopup(e);
            }

            @Override
            public void mouseReleased(final MouseEvent e) {
                tryPopup(e);
            }

            private void tryPopup(final MouseEvent e) {
                if (e.isPopupTrigger()) {
                    _fileList.setSelectedIndex(_fileList.locationToIndex(e.getPoint()));
                    final JPopupMenu popupMenu = new JPopupMenu();
                    if (_fileList.getModel().getElementAt(_fileList.getSelectedIndex()).isDirectory()) {
                        final JMenuItem browseMenuItem = new JMenuItem("Browse");
                        browseMenuItem.addActionListener(e1 -> selectOrBrowsePath(false));
                        popupMenu.add(browseMenuItem);
                    }

                    final JMenuItem selectMenuItem = new JMenuItem("Select");
                    selectMenuItem.addActionListener(e1 -> selectOrBrowsePath(true));
                    popupMenu.add(selectMenuItem);
                    popupMenu.show(_fileList, e.getX(), e.getY());
                }
            }
        });
        setLayout(new BorderLayout());
        final DCPanel topPanel = new DCPanel();
        topPanel.setLayout(new GridBagLayout());
        topPanel.setPreferredSize(new Dimension(DEFAULT_WIDTH, 40));
        WidgetUtils.addToGridBag(lookInLabel, topPanel, 0, 0, 1, 1, GridBagConstraints.WEST, 5);
        WidgetUtils.addToGridBag(pathsComboBox, topPanel, 1, 0, 100, 40, GridBagConstraints.WEST, 5, 1, 1,
                GridBagConstraints.HORIZONTAL);

        add(topPanel, BorderLayout.NORTH);

        final DCPanel panel = DCPanel.around(_fileList);
        panel.setBorder(WidgetUtils.BORDER_EMPTY);
        add(panel, BorderLayout.CENTER);
    }

    public static URI showDialog(final Component parent, final ServerInformationCatalog serverInformationCatalog,
            final String selectedServer, final URI currentUri, final OpenType openType) throws HeadlessException {

        final HdfsUrlChooser chooser = new HdfsUrlChooser(currentUri, openType);
        if (chooser._dialog != null) {
            // Prevent to show second instance of _dialog if the previous one
            // still exists
            return null;
        }

        final ComponentListener listener = new ComponentAdapter() {
            @Override
            public void componentShown(final ComponentEvent e) {
                if (chooser._currentDirectory == null) {
                    final boolean configured = chooser.scanHadoopConfigFiles(serverInformationCatalog, selectedServer);
                    if (configured) {
                        chooser.updateDirectories();
                    } else {
                        final URI uri = HdfsServerAddressDialog.showHdfsNameNodeDialog(chooser, chooser.getUri());
                        if (uri != null) {
                            chooser.updateCurrentDirectory(new Path(uri));
                        } else {
                            chooser.updateCurrentDirectory(null);
                            chooser._dialog.setVisible(false);
                        }
                    }
                }
            }
        };

        chooser.createDialog(chooser, parent, listener);

        if (chooser.getUri() != null) {
            chooser.rescanServer();
        }

        final Path selectedFile = chooser.getSelectedFile();
        if (selectedFile != null) {
            return selectedFile.toUri();
        }
        return null;
    }

    private void selectOrBrowsePath(final boolean selectDirectory) {
        // Double-click detected
        final FileStatus element = _fileList.getModel().getElementAt(_fileList.getSelectedIndex());
        if (element.isSymlink()) {
            try {
                _currentDirectory = element.getSymlink();
            } catch (final IOException e) {
                logger.warn("Could not get the symlink value for element {}", element, e);
            }
        } else if (element.isFile() || (element.isDirectory() && selectDirectory)) {
            _selectedFile = element.getPath();
            logger.info("Selected: " + _selectedFile);
            _dialog.dispose();
        } else if (element.isDirectory()) {
            _currentDirectory = element.getPath();
            _directoryComboBoxModel.updateDirectories();
        }
        ((HdfsDirectoryModel) _fileList.getModel()).updateFileList();
    }

    /**
     * This scans Hadoop environment variables for a directory with configuration files
     *
     * @param serverInformationCatalog
     * @return True if a configuration was yielded.
     */
    private boolean scanHadoopConfigFiles(final ServerInformationCatalog serverInformationCatalog,
            final String selectedServer) {

        final HadoopClusterInformation clusterInformation;
        if (selectedServer != null) {
            clusterInformation = (HadoopClusterInformation) serverInformationCatalog.getServer(selectedServer);
        } else {
            clusterInformation = (HadoopClusterInformation) serverInformationCatalog
                    .getServer(HadoopResource.DEFAULT_CLUSTERREFERENCE);
        }

        if (clusterInformation == null) {
            return false;
        }

        final Configuration configuration = HdfsUtils.getHadoopConfigurationWithTimeout(clusterInformation);

        _currentDirectory = new Path("/");

        try {
            _fileSystem = FileSystem.newInstance(configuration);
        } catch (final IOException e) {
            throw new IllegalArgumentException("Illegal URI when showing HDFS chooser", e);
        }
        final HdfsDirectoryModel model = (HdfsDirectoryModel) _fileList.getModel();
        model.updateFileList();
        return model._files.length > 0;
    }

    private void updateDirectories() {
        _directoryComboBoxModel.updateDirectories();
    }


    private void updateCurrentDirectory(final Path directory) {
        _currentDirectory = directory;

        if (directory != null) {
            _fileSystem = HdfsUtils.getFileSystemFromUri(directory.toUri());
            ((HdfsDirectoryModel) _fileList.getModel()).updateFileList();
            updateDirectories();
        }
    }

    private Path getCurrentDirectory() {
        return _currentDirectory;
    }

    private Path getRoot() {
        Path path = getCurrentDirectory();
        while (!path.isRoot()) {
            path = path.getParent();
        }

        return path;
    }

    private void rescanServer() {
        if (_fileSystem != null && getUri() != null) {
            try {
                _fileSystem.listStatus(new Path(getUri()));
            } catch (final IOException e) {
                throw new RuntimeException(e);
            }
        }
    }

    protected void createDialog(final Component chooser, final Component parent, final ComponentListener listener) {
        _dialog = WidgetFactory.createModalDialog(chooser, parent, _openType.getTitle(), true);

        if (listener != null) {
            _dialog.addComponentListener(listener);
        }
        _dialog.setVisible(true);
        _dialog.dispose();
        _dialog = null;
    }

    public URI getUri() {
        return _currentDirectory == null ? null : _currentDirectory.toUri();
    }

    private Path getSelectedFile() {
        return _selectedFile;
    }

    // Test
    public static void main(final String[] args) throws URISyntaxException {
        LookAndFeelManager.get().init();

        final List servers = new ArrayList<>();
        servers.add(new EnvironmentBasedHadoopClusterInformation("default", "hadoop conf dir"));
        servers.add(new DirectoryBasedHadoopClusterInformation("directory", "directopry set up",
                "C:\\Users\\claudiap\\git\\vagrant-vms\\bigdatavm\\hadoop_conf"));
        servers.add(new DirectConnectionHadoopClusterInformation("namenode", "directconnection",
                new URI("hdfs://192.168.0.255:9000/")));
        final ServerInformationCatalog serverInformationCatalog = new ServerInformationCatalogImpl(servers);

        final JFrame frame = new JFrame("test");

        frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        frame.setSize(DEFAULT_WIDTH, 400);
        frame.pack();
        frame.setVisible(true);

        final WindowContext windowContext = new DCWindowContext(null, null);
        final SelectHadoopClusterDialog selectHadoopConfigurationDialog =
                new SelectHadoopClusterDialog(windowContext, serverInformationCatalog, null);
        selectHadoopConfigurationDialog.setVisible(true);
        final String selectedServer = selectHadoopConfigurationDialog.getSelectedConfiguration();

        try {

            final URI selectedFile =
                    HdfsUrlChooser.showDialog(frame, serverInformationCatalog, selectedServer, null, OpenType.LOAD);
            System.out.println("Normal exit, selected file: " + selectedFile);
            System.exit(0);
        } catch (final Exception e) {
            System.err.println("Abnormal exit");
            e.printStackTrace(System.err);
            System.exit(1);
        }

    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy