com.peergreen.webconsole.core.notifier.NotifierService.java Source code

Java tutorial

Introduction

Here is the source code for com.peergreen.webconsole.core.notifier.NotifierService.java

Source

/**
 * Peergreen S.A.S. All rights reserved.
 * Proprietary and confidential.
 *
 * 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 com.peergreen.webconsole.core.notifier;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.TimeUnit;

import org.apache.felix.ipojo.annotations.Component;
import org.apache.felix.ipojo.annotations.Invalidate;
import org.apache.felix.ipojo.annotations.Provides;
import org.apache.felix.ipojo.annotations.StaticServiceProperty;

import com.peergreen.webconsole.Constants;
import com.peergreen.webconsole.HelpOverlay;
import com.peergreen.webconsole.core.notifier.utils.Notification;
import com.peergreen.webconsole.core.notifier.utils.NotificationButton;
import com.peergreen.webconsole.core.notifier.utils.ScopeButton;
import com.peergreen.webconsole.notifier.Task;
import com.vaadin.shared.ui.label.ContentMode;
import com.vaadin.ui.Button;
import com.vaadin.ui.HorizontalLayout;
import com.vaadin.ui.Label;
import com.vaadin.ui.ProgressBar;
import com.vaadin.ui.UI;
import com.vaadin.ui.VerticalLayout;
import com.vaadin.ui.Window;

/**
 * Notifier service implementation
 *
 * @author Mohammed Boukada
 */
@Component
@Provides(properties = @StaticServiceProperty(name = Constants.CONSOLE_ID, type = "java.lang.String", mandatory = true))
public class NotifierService implements InternalNotifierService, Serializable {

    private static final long serialVersionUID = 1L;

    private final List<HelpOverlay> overlays = new ArrayList<>();
    private final Map<com.vaadin.ui.Component, ScopeButton> scopesButtons = new ConcurrentHashMap<>();
    private final Map<UI, NotificationButton> notificationButtons = new ConcurrentHashMap<>();
    private final Map<UI, HorizontalLayout> tasksBars = new ConcurrentHashMap<>();
    private final ConcurrentLinkedDeque<Notification> notifications = new ConcurrentLinkedDeque<>();
    private final Queue<BaseTask> tasks = new ConcurrentLinkedQueue<>();
    private BaseTask currentTask;

    /**
     * Close all overlays
     */
    @Override
    public void closeAll() {
        for (HelpOverlay overlay : overlays) {
            overlay.close();
        }
        overlays.clear();
    }

    @Invalidate
    private void cleanup() {
        this.notificationButtons.clear();
        this.scopesButtons.clear();
        this.tasksBars.clear();
        this.notifications.clear();
        this.tasks.clear();
        closeAll();

    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void addNotification(String notification) {
        notifications.add(new Notification(notification, System.currentTimeMillis()));
        for (final Map.Entry<UI, NotificationButton> notificationButtonEntry : notificationButtons.entrySet()) {
            UI ui = notificationButtonEntry.getKey();
            if (isUIAvailable(ui)) {
                ui.access(new Runnable() {
                    @Override
                    public void run() {
                        notificationButtonEntry.getValue().incrementBadge();
                        updateNotificationBadge(notificationButtonEntry.getValue());
                    }
                });
            }
        }
    }

    @Override
    public Task createTask(String message) {
        BaseTask task = new BaseTask(message, this);
        tasks.add(task);
        showTask();
        return task;
    }

    /**
     * Show current task
     */
    protected void showTask() {
        if (currentTask == null) {
            currentTask = tasks.peek();
            for (final Map.Entry<UI, HorizontalLayout> taskBar : tasksBars.entrySet()) {
                UI ui = taskBar.getKey();
                if (isUIAvailable(ui)) {
                    ui.access(new Runnable() {
                        @Override
                        public void run() {
                            taskBar.getValue().removeAllComponents();
                            if (currentTask != null) {
                                ProgressBar progressBar = new ProgressBar();
                                progressBar.setIndeterminate(true);
                                progressBar.setVisible(true);
                                taskBar.getValue().addComponent(progressBar);
                                taskBar.getValue()
                                        .addComponent(new Label(currentTask.getMessage(), ContentMode.HTML));
                            }
                        }
                    });
                }
            }
        }
    }

    private boolean isUIAvailable(UI ui) {
        if (ui.isClosing()) {
            clearComponentsForUI(ui);
            return false;
        }
        return true;
    }

    /**
     * Remove task
     *
     * @param task task
     */
    protected void hideTask(BaseTask task) {
        tasks.remove(task);
        if (currentTask.equals(task)) {
            currentTask = null;
        }
        showTask();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void clearComponentsForUI(UI ui) {
        notificationButtons.remove(ui);
        tasksBars.remove(ui);
        List<com.vaadin.ui.Component> scopesButtonsToRemove = new ArrayList<>();
        for (Map.Entry<com.vaadin.ui.Component, ScopeButton> scopeButtonEntry : scopesButtons.entrySet()) {
            if (scopeButtonEntry.getValue().getButtonUi().equals(ui)) {
                scopesButtonsToRemove.add(scopeButtonEntry.getKey());
            }
        }
        for (com.vaadin.ui.Component scopeButtonToRemove : scopesButtonsToRemove) {
            scopesButtons.remove(scopeButtonToRemove);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public HelpOverlay createHelpOverlay(String caption, String text) {
        HelpOverlay o = new HelpOverlay();
        o.setCaption(caption);
        o.addComponent(new Label(text, ContentMode.HTML));
        o.setStyleName("login");
        overlays.add(o);
        return o;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void addScopeButton(com.vaadin.ui.Component scope, Button button, UI ui, boolean notify) {
        scopesButtons.put(scope, new ScopeButton(button, ui, 0));
        if (notify) {
            setBadgeAsNew(button, ui);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void removeScopeButton(com.vaadin.ui.Component scope) {
        if (scopesButtons.containsKey(scope)) {
            scopesButtons.remove(scope);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void addNotificationsButton(Button button, final Window window, final UI ui) {
        final VerticalLayout layout = new VerticalLayout();
        layout.setMargin(true);
        layout.setSpacing(true);
        window.setContent(layout);

        notificationButtons.put(ui, new NotificationButton(button, 0));

        button.addClickListener(new NotificationClickListener(layout));
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void addTasksBar(HorizontalLayout tasksBar, UI ui) {
        tasksBars.put(ui, tasksBar);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void hideScopeButton(com.vaadin.ui.Component scope) {
        if (scopesButtons.containsKey(scope)) {
            scopesButtons.get(scope).getButton().setVisible(false);
        }
    }

    /**
     * {@inheritDoc}
     *
     * @param scope
     */
    @Override
    public void removeBadge(com.vaadin.ui.Component scope) {
        updateBadge(scope, 0);
        scopesButtons.get(scope).getButton().setHtmlContentAllowed(true);
        setCaption(scopesButtons.get(scope).getButton(), scopesButtons.get(scope).getButtonUi(),
                getInitialCaption(scopesButtons.get(scope).getButton()));
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void incrementBadge(com.vaadin.ui.Component scope) {
        if (scopesButtons.containsKey(scope) && scopesButtons.get(scope).getButton().isAttached()) {
            updateBadge(scope, +1);
            scopesButtons.get(scope).getButton().setVisible(true);
            scopesButtons.get(scope).getButton().setHtmlContentAllowed(true);
            String newCaption = getInitialCaption(scopesButtons.get(scope).getButton()) + "<span class=\"badge\">"
                    + scopesButtons.get(scope).getBadge() + "</span>";
            setCaption(scopesButtons.get(scope).getButton(), scopesButtons.get(scope).getButtonUi(), newCaption);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void decrementBadge(com.vaadin.ui.Component scope) {
        if (scopesButtons.containsKey(scope) && scopesButtons.get(scope).getButton().isAttached()) {
            updateBadge(scope, -1);
            scopesButtons.get(scope).getButton().setHtmlContentAllowed(true);
            String newCaption = getInitialCaption(scopesButtons.get(scope).getButton())
                    + ((scopesButtons.get(scope).getBadge() == 0) ? ""
                            : "<span class=\"badge\">" + scopesButtons.get(scope).getBadge() + "</span>");
            setCaption(scopesButtons.get(scope).getButton(), scopesButtons.get(scope).getButtonUi(), newCaption);
        }
    }

    /**
     * Set badge as new
     *
     * @param button button
     * @param ui     button UI
     */
    private void setBadgeAsNew(final Button button, UI ui) {
        button.setHtmlContentAllowed(true);
        final String newCaption = getInitialCaption(button) + "<span class=\"badge\">new</span>";
        setCaption(button, ui, newCaption);

    }

    /**
     * Set button caption
     *
     * @param button  button
     * @param ui      button UI
     * @param caption new button caption
     */
    private void setCaption(final Button button, UI ui, final String caption) {
        ui.access(new Runnable() {
            @Override
            public void run() {
                button.setCaption(caption);
            }
        });
    }

    /**
     * Update badge when it is changed
     *
     * @param scope scope
     * @param op    operation (+1, -1, set to 0)
     */
    private void updateBadge(com.vaadin.ui.Component scope, int op) {
        if (scopesButtons.containsKey(scope)) {
            Integer badge = scopesButtons.get(scope).getBadge();
            if (op == 0) {
                badge = 0;
            } else if (op == +1) {
                badge++;
            } else if (op == -1 && badge > 0) {
                badge--;
            }
            scopesButtons.get(scope).setBadge(badge);
        }
    }

    /**
     * Get initial caption of the button
     *
     * @param button button
     * @return button caption
     */
    private String getInitialCaption(Button button) {
        if (button.getCaption().contains("<span")) {
            return button.getCaption().substring(0, button.getCaption().indexOf("<span"));
        }
        return button.getCaption();
    }

    /**
     * Update notification button, make it as unread.
     *
     * @param notificationButton notification button
     */
    private void updateNotificationBadge(NotificationButton notificationButton) {
        int badge = notificationButton.getBadge();
        notificationButton.getButton().addStyleName("unread");
        notificationButton.getButton().setCaption(String.valueOf(badge));
        notificationButton.getButton().setDescription("Notifications (" + badge + " unread)");
    }

    private static final int TWELVE = 12;
    private static final int TWENTY_FOUR = 12;
    private static final int THIRTEEN = 30;
    private static final int SIXTY = 60;
    private static final int THOUSAND = 1000;

    /**
     * Format time
     *
     * @param t timestamp
     * @return time string
     */
    private String formatTime(Long t) {

        Long days = TimeUnit.MILLISECONDS.toDays(t);
        Long months = days / THIRTEEN;
        Long years = months / TWELVE;

        StringBuilder date = new StringBuilder();
        if (years > 0) {
            date.append(years);
            if (years == 1) {
                date.append(" year");
            } else {
                date.append(" years");
            }
        } else {
            if (months > 0) {
                date.append(months);
                if (months == 1) {
                    date.append(" month");
                } else {
                    date.append(" months");
                }
            } else {
                if (days > 0) {
                    date.append(days);
                    if (days == 1) {
                        date.append(" day");
                    } else {
                        date.append(" days");
                    }
                } else {
                    Long hours = TimeUnit.MILLISECONDS.toHours(t - days * TWENTY_FOUR * SIXTY * SIXTY * THOUSAND);
                    if (hours > 0) {
                        date.append(hours);
                        if (hours == 1) {
                            date.append(" hour");
                        } else {
                            date.append(" hours");
                        }
                    } else {
                        Long minutes = TimeUnit.MILLISECONDS.toMinutes(t - hours * SIXTY * SIXTY * THOUSAND);
                        if (minutes > 0) {
                            date.append(minutes);
                            if (minutes == 1) {
                                date.append(" minute");
                            } else {
                                date.append(" minutes");
                            }
                        } else {
                            date.append("a few seconds");
                        }
                    }
                }
            }
        }
        date.append(" ago");
        return date.toString();
    }

    private static final int MAX_NB_NOTIFICATIONS = 50;

    /**
     * @author Mohammed Boukada
     */
    private class NotificationClickListener implements Button.ClickListener {
        private boolean opened = false;

        private final VerticalLayout layout;

        public NotificationClickListener(VerticalLayout layout) {
            this.layout = layout;
        }

        @Override
        public void buttonClick(Button.ClickEvent event) {
            for (Map.Entry<UI, NotificationButton> notificationButtonEntry : notificationButtons.entrySet()) {
                notificationButtonEntry.getValue().setBadge(0);
            }
            if (opened) {
                opened = false;
            } else {
                opened = true;
                layout.removeAllComponents();
                int i = 0;
                Iterator<Notification> iterator = notifications.descendingIterator();
                while (iterator.hasNext() && i < MAX_NB_NOTIFICATIONS) {
                    Notification notification = iterator.next();
                    layout.addComponent(new Label("<hr>", ContentMode.HTML));
                    Label message = new Label("<b>" + notification.getMessage() + "</b>", ContentMode.HTML);
                    message.setWidth("280px");
                    layout.addComponent(message);
                    Label date = new Label(
                            "<span>" + formatTime(System.currentTimeMillis() - notification.getDate()) + "</span>",
                            ContentMode.HTML);
                    layout.addComponent(date);
                    i++;
                }
            }
        }
    }
}