org.apache.ace.webui.vaadin.component.MainActionToolbar.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.ace.webui.vaadin.component.MainActionToolbar.java

Source

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.apache.ace.webui.vaadin.component;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import org.apache.ace.client.repository.RepositoryAdmin;
import org.apache.ace.webui.UIExtensionFactory;
import org.apache.ace.webui.vaadin.ShortcutHelper;
import org.apache.felix.dm.DependencyManager;
import org.osgi.framework.ServiceReference;
import org.osgi.service.event.EventHandler;

import com.vaadin.event.ShortcutAction.KeyCode;
import com.vaadin.terminal.gwt.server.WebApplicationContext;
import com.vaadin.ui.Button;
import com.vaadin.ui.Button.ClickEvent;
import com.vaadin.ui.Component;
import com.vaadin.ui.GridLayout;
import com.vaadin.ui.HorizontalLayout;
import com.vaadin.ui.Label;
import com.vaadin.ui.Window.Notification;

/**
 * Provides the main actions toolbar where one can commit, revert or retrieve changes.
 */
public abstract class MainActionToolbar extends GridLayout implements EventHandler {

    /**
     * Provides a button listener for the logout button.
     */
    private class LogoutButtonListener implements Button.ClickListener, ConfirmationDialog.Callback {

        public void buttonClick(ClickEvent event) {
            // Avoid double-clicks...
            event.getButton().setEnabled(false);

            final RepositoryAdmin repoAdmin = getRepositoryAdmin();
            try {
                if (repoAdmin.isModified() && repoAdmin.isCurrent()) {
                    getWindow().addWindow(new ConfirmationDialog("Revert changes?",
                            "The repository is changed. Are you sure you want to loose all local changes?", this));
                } else {
                    logout();
                }
            } catch (IOException e) {
                showError("Changes not stored", "Failed to store the changes to the server.", e);
            }
        }

        public void onDialogResult(String buttonName) {
            if (ConfirmationDialog.YES.equals(buttonName)) {
                try {
                    logout();
                } catch (IOException e) {
                    showError("Warning", "There were errors during the logout procedure.", e);
                }
            }
        }

        /**
         * Does the actual logout of the user.
         * 
         * @throws IOException
         *             in case of I/O problems during the logout.
         */
        private void logout() throws IOException {
            getRepositoryAdmin().logout(true /* force */);
            doAfterLogout();
        }
    }

    /**
     * Provides a button listener for the retrieve button.
     */
    private final class RetrieveButtonListener implements Button.ClickListener, ConfirmationDialog.Callback {

        public void buttonClick(ClickEvent event) {
            // Avoid double-clicks...
            event.getButton().setEnabled(false);

            final RepositoryAdmin repoAdmin = getRepositoryAdmin();
            try {
                if (repoAdmin.isModified()) {
                    // Warn the user about the possible loss of changes...
                    getWindow().addWindow(new ConfirmationDialog("Retrieve latest changes?",
                            "The repository is changed. Are you sure you want to loose all local changes?", this));
                } else {
                    retrieveData();
                }
            } catch (IOException e) {
                handleIOException(e);
            }
        }

        public void onDialogResult(String buttonName) {
            if (ConfirmationDialog.YES.equals(buttonName)) {
                try {
                    retrieveData();
                } catch (IOException e) {
                    handleIOException(e);
                }
            }
        }

        private void handleIOException(IOException e) {
            showError("Retrieve failed", "Failed to retrieve the data from the server.", e);
        }

        /**
         * Does the actual retrieval of the latest version.
         * 
         * @throws IOException
         *             in case of I/O problems during the retrieve.
         */
        private void retrieveData() throws IOException {
            getRepositoryAdmin().checkout();
            doAfterRetrieve();
        }
    }

    /**
     * Provides a button listener for the revert button.
     */
    private final class RevertButtonListener implements Button.ClickListener, ConfirmationDialog.Callback {

        public void buttonClick(ClickEvent event) {
            // Avoid double-clicks...
            event.getButton().setEnabled(false);

            try {
                if (getRepositoryAdmin().isModified()) {
                    // Revert all changes...
                    getWindow().addWindow(new ConfirmationDialog("Revert changes?",
                            "Are you sure you want to overwrite all local changes?", this));
                } else {
                    // Nothing to revert...
                    showWarning("Nothing to revert", "There are no local changes that need to be reverted.");
                }
            } catch (IOException e) {
                handleIOException(e);
            }
        }

        public void onDialogResult(String buttonName) {
            if (ConfirmationDialog.YES.equals(buttonName)) {
                try {
                    revertChanges();
                } catch (IOException e) {
                    handleIOException(e);
                }
            }
        }

        private void handleIOException(IOException e) {
            showError("Revert failed", "Failed to revert your changes.", e);
        }

        /**
         * Does the actual revert of changes.
         * 
         * @throws IOException
         *             in case of problems during I/O exception.
         */
        private void revertChanges() throws IOException {
            getRepositoryAdmin().revert();
            doAfterRevert();
        }
    }

    /**
     * Provides a button listener for the store button.
     */
    private final class StoreButtonListener implements Button.ClickListener {

        public void buttonClick(ClickEvent event) {
            // Avoid double-clicks...
            event.getButton().setEnabled(false);

            final RepositoryAdmin repoAdmin = getRepositoryAdmin();
            try {
                if (repoAdmin.isModified()) {
                    if (repoAdmin.isCurrent()) {
                        commitChanges();
                    } else {
                        getWindow().showNotification("Changes not stored",
                                "Unable to store your changes; repository changed!",
                                Notification.TYPE_WARNING_MESSAGE);
                    }
                } else {
                    showWarning("Nothing to store", "There are no changes that can be stored to the repository.");
                }
            } catch (IOException e) {
                showError("Changes not stored", "Failed to store the changes to the server.", e);
            }
        }

        /**
         * Does the actual commit of changes.
         * 
         * @throws IOException
         *             in case of I/O problems during the commit.
         */
        private void commitChanges() throws IOException {
            getRepositoryAdmin().commit();
            doAfterCommit();
        }
    }

    private final ConcurrentMap<ServiceReference<UIExtensionFactory>, UIExtensionFactory> m_extensions;
    private final boolean m_showLogoutButton;

    private Button m_retrieveButton;
    private Button m_storeButton;
    private Button m_revertButton;
    private Button m_logoutButton;

    private HorizontalLayout m_extraComponentBar;

    /**
     * Creates a new {@link MainActionToolbar} instance.
     * 
     * @param user
     * @param manager
     * 
     * @param showLogoutButton
     *            <code>true</code> if a logout button should be shown, <code>false</code> if it should not.
     */
    public MainActionToolbar(boolean showLogoutButton) {
        super(6, 1);

        m_extensions = new ConcurrentHashMap<>();
        m_showLogoutButton = showLogoutButton;

        setWidth("100%");
        setSpacing(true);

        initComponent();
    }

    @Override
    public void attach() {
        try {
            addCrossPlatformShortcut(m_retrieveButton, KeyCode.G, "Retrieves the latest changes from the server");
            addCrossPlatformShortcut(m_storeButton, KeyCode.S, "Stores all local changes");
            addCrossPlatformShortcut(m_revertButton, KeyCode.U, "Reverts all local changes");

            if (m_showLogoutButton) {
                addCrossPlatformShortcut(m_logoutButton, KeyCode.L, "Log out");
            }
        } finally {
            super.attach();
        }
    }

    /**
     * {@inheritDoc}
     */
    public void handleEvent(org.osgi.service.event.Event event) {
        boolean modified = false;
        try {
            modified = getRepositoryAdmin().isModified();
        } catch (IOException e) {
            showError("Communication failed!", "Failed to communicate with the server.", e);
        }

        // always enabled...
        m_retrieveButton.setEnabled(true);
        // only enabled when an actual change has been made...
        m_storeButton.setEnabled(modified);
        m_revertButton.setEnabled(modified);
    }

    protected final void add(ServiceReference<UIExtensionFactory> ref, UIExtensionFactory factory) {
        m_extensions.put(ref, factory);
        setExtraComponents();
    }

    /**
     * Called after a commit/store has taken place, allows additional UI-updates to be performed.
     * 
     * @throws IOException
     */
    protected abstract void doAfterCommit() throws IOException;

    /**
     * Called after a logout has taken place, allows additional UI-updates to be performed.
     * 
     * @throws IOException
     */
    protected abstract void doAfterLogout() throws IOException;

    /**
     * Called after a retrieve has taken place, allows additional UI-updates to be performed.
     * 
     * @throws IOException
     */
    protected abstract void doAfterRetrieve() throws IOException;

    /**
     * Called after a revert has taken place, allows additional UI-updates to be performed.
     * 
     * @throws IOException
     */
    protected abstract void doAfterRevert() throws IOException;

    protected final List<Component> getExtraComponents() {
        // create a shapshot of the current extensions...
        Map<ServiceReference<UIExtensionFactory>, UIExtensionFactory> extensions = new HashMap<>(m_extensions);

        // Make sure we've got a predictable order of the components...
        List<ServiceReference<UIExtensionFactory>> refs = new ArrayList<>(extensions.keySet());
        Collections.sort(refs);

        Map<String, Object> context = new HashMap<>();

        List<Component> result = new ArrayList<>();
        for (ServiceReference<UIExtensionFactory> ref : refs) {
            UIExtensionFactory factory = extensions.get(ref);

            result.add(factory.create(context));
        }
        return result;
    }

    /**
     * @return a repository admin instance, never <code>null</code>.
     */
    protected abstract RepositoryAdmin getRepositoryAdmin();

    /**
     * Called by Felix DM when initializing this component.
     */
    protected void init(org.apache.felix.dm.Component component) {
        DependencyManager dm = component.getDependencyManager();
        component.add(dm.createServiceDependency()
                .setService(UIExtensionFactory.class,
                        "(" + UIExtensionFactory.EXTENSION_POINT_KEY + "="
                                + UIExtensionFactory.EXTENSION_POINT_VALUE_MENU + ")")
                .setCallbacks("add", "remove").setRequired(false));
    }

    protected final void remove(ServiceReference<UIExtensionFactory> ref, UIExtensionFactory factory) {
        m_extensions.remove(ref);
        setExtraComponents();
    }

    protected void showError(String title, String message, Exception e) {
        StringBuilder sb = new StringBuilder("<br/>");
        sb.append(message);
        if (e.getMessage() != null) {
            sb.append("<br/>").append(e.getMessage());
        } else {
            sb.append("<br/>unknown error!");
        }
        getWindow().showNotification(title, sb.toString(), Notification.TYPE_ERROR_MESSAGE);
    }

    protected void showWarning(String title, String message) {
        getWindow().showNotification(title, String.format("<br/>%s", message), Notification.TYPE_WARNING_MESSAGE);
    }

    private void addCrossPlatformShortcut(Button button, int key, String description) {
        // ACE-427 - NPE when using getMainWindow() if no authentication is used...
        WebApplicationContext context = (WebApplicationContext) getApplication().getContext();
        ShortcutHelper.addCrossPlatformShortcut(context.getBrowser(), button, description, key);
    }

    /**
     * Initializes this component.
     */
    private void initComponent() {
        m_retrieveButton = new Button("Retrieve");
        m_retrieveButton.setEnabled(false);
        m_retrieveButton.addListener(new RetrieveButtonListener());
        addComponent(m_retrieveButton, 0, 0);

        m_storeButton = new Button("Store");
        m_storeButton.setEnabled(false);
        m_storeButton.addListener(new StoreButtonListener());
        addComponent(m_storeButton, 1, 0);

        m_revertButton = new Button("Revert");
        m_revertButton.setEnabled(false);
        m_revertButton.addListener(new RevertButtonListener());
        addComponent(m_revertButton, 2, 0);

        Label spacer = new Label("");
        spacer.setWidth("2em");
        addComponent(spacer, 3, 0);

        m_extraComponentBar = new HorizontalLayout();
        m_extraComponentBar.setSpacing(true);

        addComponent(m_extraComponentBar, 4, 0);

        m_logoutButton = new Button("Logout");
        m_logoutButton.addListener(new LogoutButtonListener());
        m_logoutButton.setVisible(m_showLogoutButton);

        addComponent(m_logoutButton, 5, 0);

        // Ensure the spacer gets all the excessive room, causing the logout
        // button to appear at the right side of the screen....
        setColumnExpandRatio(3, 5);
    }

    private void setExtraComponents() {
        m_extraComponentBar.removeAllComponents();
        for (Component c : getExtraComponents()) {
            m_extraComponentBar.addComponent(c);
        }
    }
}