001/*
002 * Copyright (c) 2014 Maxim Yunusov
003 *    Licensed under the Apache License, Version 2.0 (the "License");
004 *    you may not use this file except in compliance with the License.
005 *    You may obtain a copy of the License at
006 *
007 *        http://www.apache.org/licenses/LICENSE-2.0
008 *
009 *    Unless required by applicable law or agreed to in writing, software
010 *    distributed under the License is distributed on an "AS IS" BASIS,
011 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012 *    See the License for the specific language governing permissions and
013 *    limitations under the License.
014 */
015
016package org.maxur.perfmodel.backend.service.impl;
017
018import org.jvnet.hk2.annotations.Service;
019import org.maxur.perfmodel.backend.service.Application;
020import org.slf4j.Logger;
021import org.slf4j.LoggerFactory;
022
023import javax.inject.Named;
024import javax.swing.*;
025import java.awt.*;
026import java.io.IOException;
027import java.net.URI;
028import java.net.URL;
029
030import static java.awt.Desktop.Action.BROWSE;
031import static java.awt.Desktop.getDesktop;
032import static java.awt.Desktop.isDesktopSupported;
033import static java.awt.SystemTray.getSystemTray;
034import static java.lang.String.format;
035import static javax.swing.JOptionPane.showMessageDialog;
036import static javax.swing.SwingUtilities.invokeLater;
037import static org.maxur.perfmodel.backend.utils.OsUtils.isWindows;
038
039/**
040 * This class represents Performance Model Calculator Application
041 * It's GUI Implementation with Tray Icon
042 *
043 * @author Maxim
044 * @version 1.0
045 * @since <pre>24.10.2014</pre>
046 * <p>
047 * see http://docs.oracle.com/javase/tutorial/displayCode.html?code=http://docs.oracle.com/javase/tutorial/uiswing/examples/misc/TrayIconDemoProject/src/misc/TrayIconDemo.java
048 */
049@Service
050public class TrayIconApplication extends Application {
051
052    private static final Logger LOGGER = LoggerFactory.getLogger(TrayIconApplication.class);
053
054    public static final String IMG_FAVICON_PATH = "/img/favicon.png";
055
056    private TrayIcon trayIcon;
057
058    @SuppressWarnings("unused")
059    @Named("webapp.url")
060    private URI webappUri;
061
062    public TrayIconApplication() {
063    }
064
065    @Override
066    public boolean isApplicable() {
067        //Check the SystemTray support
068        return SystemTray.isSupported();
069    }
070
071    @Override
072    protected void onInit() {
073        try {
074            final String className = isWindows() ?
075                    "com.sun.java.swing.plaf.windows.WindowsLookAndFeel" :
076                    "javax.swing.plaf.metal.MetalLookAndFeel";
077            UIManager.setLookAndFeel(className);
078        } catch (UnsupportedLookAndFeelException | IllegalAccessException
079                | InstantiationException | ClassNotFoundException ex
080                ) {
081            LOGGER.error("Tray application is not created", ex);
082        }
083        UIManager.put("swing.boldMetal", Boolean.FALSE);
084    }
085
086    @Override
087    protected void onStart() {
088        invokeLater(this::run);
089    }
090
091    private void run() {
092        trayIcon = new TrayIcon(createImage(IMG_FAVICON_PATH, "tray icon"));
093        trayIcon.setToolTip("Performance Model Calculator");
094        trayIcon.setImageAutoSize(true);
095        trayIcon.setPopupMenu(createPopupMenu());
096        trayIcon.addActionListener(e -> openBrowser());
097        try {
098            getSystemTray().add(trayIcon);
099        } catch (AWTException e) {
100            final String msg = "TrayIcon could not be added.";
101            LOGGER.error(msg);
102            throw new IllegalStateException(msg, e);
103        }
104    }
105
106    private PopupMenu createPopupMenu() {
107        final PopupMenu popup = new PopupMenu();
108
109        final MenuItem aboutItem = new MenuItem("About");
110        final MenuItem startClientItem = new MenuItem("Start Client");
111        final MenuItem manageServiceItem = new MenuItem("Stop Service");
112        final MenuItem exitItem = new MenuItem("Exit");
113
114        startClientItem.addActionListener(e -> openBrowser());
115
116        manageServiceItem.addActionListener(e -> {
117            if (webServer().isStarted()) {
118                webServer().stop();
119            } else {
120                webServer().restart();
121            }
122            final String label = webServer().isStarted() ? "Stop Service" : "Start Service";
123            manageServiceItem.setLabel(label);
124            final String message = webServer().isStarted() ? "Web Service was started" : "Web Service was stopped";
125            trayIcon.displayMessage("Performance Model Calculator",
126                    message,
127                    TrayIcon.MessageType.INFO);
128            startClientItem.setEnabled(webServer().isStarted());
129        });
130
131        aboutItem.addActionListener(e ->
132                        showMessageDialog(null, "Performance Model Calculator. Version: " + version())
133        );
134
135        exitItem.addActionListener(e -> stop());
136
137        popup.add(aboutItem);
138        popup.add(startClientItem);
139        popup.add(manageServiceItem);
140        popup.addSeparator();
141        popup.add(exitItem);
142
143        return popup;
144    }
145
146    @Override
147    protected void onStop() {
148        getSystemTray().remove(trayIcon);
149    }
150
151    private static Image createImage(final String path, final String description) {
152        final URL imageURL = TrayIconApplication.class.getResource(path);
153        if (imageURL == null) {
154            final String msg = format("Resource '%s' is not found", IMG_FAVICON_PATH);
155            LOGGER.error(msg);
156            throw new IllegalStateException(msg);
157        }
158        return (new ImageIcon(imageURL, description)).getImage();
159    }
160
161    private void openBrowser() {
162        if (!isDesktopSupported()) {
163            LOGGER.error("Cannot open browser. Desktop is not supported");
164            return;
165        }
166        final Desktop desktop = getDesktop();
167        if (!desktop.isSupported(BROWSE)) {
168            LOGGER.error("Cannot open browser. Desktop is not supported browse action");
169            return;
170        }
171        try {
172            desktop.browse(webappUri);
173        } catch (IOException e) {
174            LOGGER.error("Cannot open browser", e);
175        }
176    }
177
178}
179
180