org.jivesoftware.openfire.launcher.Launcher Maven / Gradle / Ivy
The newest version!
/*
* Copyright (C) 2004-2008 Jive Software. All rights reserved.
*
* 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.jivesoftware.openfire.launcher;
import java.awt.AWTException;
import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Desktop;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.MenuItem;
import java.awt.PopupMenu;
import java.awt.SystemTray;
import java.awt.TrayIcon;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.net.URI;
import java.net.URL;
import javax.swing.BorderFactory;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.JScrollPane;
import javax.swing.JTextPane;
import javax.swing.SwingWorker;
import javax.swing.UIManager;
import javax.swing.text.BadLocationException;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.StyleConstants;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
/**
* Graphical launcher for Openfire.
*
* @author Matt Tucker
*/
public class Launcher {
private String appName;
private File binDir;
private Process openfired;
private File configFile;
private JPanel toolbar = new JPanel();
private ImageIcon offIcon;
private ImageIcon onIcon;
private TrayIcon trayIcon;
private JFrame frame;
private JPanel cardPanel = new JPanel();
private CardLayout cardLayout = new CardLayout();
private JTextPane pane;
private boolean freshStart = true;
/**
* Creates a new Launcher object.
*/
public Launcher() throws AWTException {
// Initialize the SystemTray now (to avoid a bug!)
SystemTray tray = null;
try {
tray = SystemTray.getSystemTray();
}
catch (Throwable e) {
// Log to System error instead of standard error log.
System.err.println("Error loading system tray library, system tray support disabled.");
}
// Use the native look and feel.
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
}
catch (Exception e) {
e.printStackTrace();
}
if (System.getProperty("app.name") != null) {
appName = System.getProperty("app.name");
}
else {
appName = "Openfire";
}
binDir = new File("").getAbsoluteFile();
// See if the appdir property is set. If so, use it to find the executable.
if (System.getProperty("appdir") != null) {
binDir = new File(System.getProperty("appdir"));
}
configFile = new File(new File(binDir.getParent(), "conf"), "openfire.xml");
frame = new DroppableFrame() {
@Override
public void fileDropped(File file) {
String fileName = file.getName();
if (fileName.endsWith(".jar") || fileName.endsWith(".war")) {
installPlugin(file);
}
}
};
frame.setTitle(appName);
ImageIcon splash;
JPanel mainPanel = new JPanel();
JLabel splashLabel = null;
cardPanel.setLayout(cardLayout);
// Set the icon.
try {
splash = new ImageIcon(getClass().getClassLoader().getResource("splash.gif"));
splashLabel = new JLabel("", splash, JLabel.CENTER);
onIcon = new ImageIcon(getClass().getClassLoader().getResource("openfire_on-16x16.gif"));
offIcon = new ImageIcon(getClass().getClassLoader().getResource("openfire_off-16x16.gif"));
frame.setIconImage(offIcon.getImage());
}
catch (Exception e) {
e.printStackTrace();
}
mainPanel.setLayout(new BorderLayout());
cardPanel.setBackground(Color.white);
// Add buttons
final JButton startButton = new JButton("Start");
startButton.setActionCommand("Start");
final JButton stopButton = new JButton("Stop");
stopButton.setActionCommand("Stop");
final JButton browserButton = new JButton("Launch Admin");
browserButton.setActionCommand("Launch Admin");
final JButton quitButton = new JButton("Quit");
quitButton.setActionCommand("Quit");
toolbar.setLayout(new GridBagLayout());
toolbar.add(startButton, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0,
GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(5, 5, 5, 5), 0, 0));
toolbar.add(stopButton, new GridBagConstraints(1, 0, 1, 1, 0.0, 0.0,
GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(5, 5, 5, 5), 0, 0));
toolbar.add(browserButton, new GridBagConstraints(2, 0, 1, 1, 0.0, 0.0,
GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(5, 5, 5, 5), 0, 0));
toolbar.add(quitButton, new GridBagConstraints(3, 0, 1, 1, 0.0, 0.0,
GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(5, 5, 5, 5), 0, 0));
mainPanel.add(cardPanel, BorderLayout.CENTER);
mainPanel.add(toolbar, BorderLayout.SOUTH);
// create the main menu of the system tray icon
PopupMenu menu = new PopupMenu(appName + " Menu");
final MenuItem showMenuItem = new MenuItem("Hide");
showMenuItem.setActionCommand("Hide/Show");
menu.add(showMenuItem);
final MenuItem startMenuItem = new MenuItem("Start");
startMenuItem.setActionCommand("Start");
menu.add(startMenuItem);
final MenuItem stopMenuItem = new MenuItem("Stop");
stopMenuItem.setActionCommand("Stop");
menu.add(stopMenuItem);
final MenuItem browserMenuItem = new MenuItem("Launch Admin");
browserMenuItem.setActionCommand("Launch Admin");
menu.add(browserMenuItem);
menu.addSeparator();
final MenuItem quitMenuItem = new MenuItem("Quit");
quitMenuItem.setActionCommand("Quit");
menu.add(quitMenuItem);
browserButton.setEnabled(false);
stopButton.setEnabled(false);
browserMenuItem.setEnabled(false);
stopMenuItem.setEnabled(false);
ActionListener actionListener = new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if ("Start".equals(e.getActionCommand())) {
frame.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
// Adjust button and menu items.
startButton.setEnabled(false);
stopButton.setEnabled(true);
startMenuItem.setEnabled(false);
stopMenuItem.setEnabled(true);
// Startup Application
startApplication();
// Change to the "on" icon.
frame.setIconImage(onIcon.getImage());
trayIcon.setImage(onIcon.getImage());
// Start a thread to enable the admin button after 8 seconds.
Thread thread = new Thread() {
@Override
public void run() {
try {
sleep(8000);
}
catch (InterruptedException ie) {
// Ignore.
}
// Enable the Launch Admin button/menu item only if the
// server has started.
if (stopButton.isEnabled()) {
browserButton.setEnabled(true);
browserMenuItem.setEnabled(true);
frame.setCursor(Cursor.getDefaultCursor());
}
}
};
thread.start();
}
else if ("Stop".equals(e.getActionCommand())) {
stopApplication();
// Change to the "off" button.
frame.setIconImage(offIcon.getImage());
trayIcon.setImage(offIcon.getImage());
// Adjust buttons and menu items.
frame.setCursor(Cursor.getDefaultCursor());
browserButton.setEnabled(false);
startButton.setEnabled(true);
stopButton.setEnabled(false);
browserMenuItem.setEnabled(false);
startMenuItem.setEnabled(true);
stopMenuItem.setEnabled(false);
}
else if ("Launch Admin".equals(e.getActionCommand())) {
launchBrowser();
} else if ("Quit".equals(e.getActionCommand())) {
stopApplication();
System.exit(0);
}
else if ("Hide/Show".equals(e.getActionCommand()) || "PressAction".equals(e.getActionCommand())) {
toggleVisibility(showMenuItem);
}
}
};
// Register a listener for the radio buttons.
startButton.addActionListener(actionListener);
stopButton.addActionListener(actionListener);
browserButton.addActionListener(actionListener);
quitButton.addActionListener(actionListener);
// Register a listener for the menu items.
quitMenuItem.addActionListener(actionListener);
browserMenuItem.addActionListener(actionListener);
stopMenuItem.addActionListener(actionListener);
startMenuItem.addActionListener(actionListener);
showMenuItem.addActionListener(actionListener);
// Set the system tray icon with the menu
trayIcon = new TrayIcon(offIcon.getImage(), appName, menu);
trayIcon.setImageAutoSize(true);
trayIcon.addActionListener(actionListener);
trayIcon.addMouseListener(new MouseListener() {
@Override
public void mouseClicked(MouseEvent e) {
// Left click
if (e.getButton() == 1) {
toggleVisibility(showMenuItem);
}
}
@Override
public void mousePressed(MouseEvent e) {
}
@Override
public void mouseReleased(MouseEvent e) {
}
@Override
public void mouseEntered(MouseEvent e) {
}
@Override
public void mouseExited(MouseEvent e) {
}
});
if (tray != null) {
tray.add(trayIcon);
}
frame.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
stopApplication();
System.exit(0);
}
@Override
public void windowIconified(WindowEvent e) {
// Make the window disappear when minimized
frame.setVisible(false);
showMenuItem.setLabel("Show");
}
});
cardPanel.add("main", splashLabel);
frame.getContentPane().add(mainPanel, BorderLayout.CENTER);
frame.pack();
frame.setSize(400, 300);
frame.setResizable(true);
GraphicUtils.centerWindowOnScreen(frame);
frame.setVisible(true);
// Setup command area
final ImageIcon icon = new ImageIcon(getClass().getClassLoader().getResource("splash2.gif"));
pane = new DroppableTextPane() {
@Override
public void paintComponent(Graphics g) {
final Dimension size = pane.getSize();
int x = (size.width - icon.getIconWidth()) / 2;
int y = (size.height - icon.getIconHeight()) / 2;
// Approach 1: Dispaly image at at full size
g.setColor(Color.white);
g.fillRect(0, 0, size.width, size.height);
g.drawImage(icon.getImage(), x, y, null);
setOpaque(false);
super.paintComponent(g);
}
@Override
public void fileDropped(File file) {
String fileName = file.getName();
if (fileName.endsWith(".jar") || fileName.endsWith(".war")) {
installPlugin(file);
}
}
};
pane.setEditable(false);
final JPanel bevelPanel = new JPanel();
bevelPanel.setBackground(Color.white);
bevelPanel.setLayout(new BorderLayout());
bevelPanel.setBorder(BorderFactory.createLoweredBevelBorder());
bevelPanel.add(new JScrollPane(pane), BorderLayout.CENTER);
cardPanel.add("running", bevelPanel);
// Start the app.
startButton.doClick();
}
/**
* Creates a new GUI launcher instance.
*/
public static void main(String[] args) throws AWTException {
new Launcher();
}
private void toggleVisibility(MenuItem showMenuItem) {
// Hide/Unhide the window if the user clicked in the system tray icon or
// selected the menu option
if (frame.isVisible()) {
frame.setVisible(false);
showMenuItem.setLabel("Show");
} else {
frame.setVisible(true);
frame.setState(Frame.NORMAL);
showMenuItem.setLabel("Hide");
}
}
private synchronized void startApplication() {
if (openfired == null) {
try {
File windowsExe = new File(binDir, "openfired.exe");
File unixExe = new File(binDir, "openfired");
if (windowsExe.exists()) {
openfired = Runtime.getRuntime().exec(new String[]{windowsExe.toString()});
}
else if (unixExe.exists()) {
openfired = Runtime.getRuntime().exec(new String[]{unixExe.toString()});
}
else {
throw new FileNotFoundException();
}
}
catch (Exception e) {
// Try one more time using the jar and hope java is on the path
try {
File libDir = new File(binDir.getParentFile(), "lib").getAbsoluteFile();
openfired = Runtime.getRuntime().exec(new String[]{
"java", "-jar", new File(libDir, "startup.jar").toString()
});
}
catch (Exception ex) {
ex.printStackTrace();
JOptionPane.showMessageDialog(null,
"Launcher could not start,\n" + appName,
"File not found", JOptionPane.ERROR_MESSAGE);
}
}
final SimpleAttributeSet styles = new SimpleAttributeSet();
SwingWorker inputWorker = new SwingWorker() {
@Override
public String doInBackground() {
if (openfired != null) {
// Get the input stream and read from it
try (InputStream in = openfired.getInputStream()) {
int c;
while ((c = in.read()) != -1) {
try {
StyleConstants.setFontFamily(styles, "courier new");
pane.getDocument().insertString(pane.getDocument().getLength(),
"" + (char)c, styles);
}
catch (BadLocationException e) {
// Ignore.
}
}
}
catch (IOException e) {
e.printStackTrace();
}
}
return "ok";
}
};
inputWorker.execute();
SwingWorker errorWorker = new SwingWorker() {
@Override
public String doInBackground() {
if (openfired != null) {
// Get the input stream and read from it
try (InputStream in = openfired.getErrorStream()) {
int c;
while ((c = in.read()) != -1) {
try {
StyleConstants.setForeground(styles, Color.red);
pane.getDocument().insertString(pane.getDocument().getLength(), "" + (char)c, styles);
}
catch (BadLocationException e) {
// Ignore.
}
}
}
catch (IOException e) {
e.printStackTrace();
}
}
return "ok";
}
};
errorWorker.execute();
if (freshStart) {
try {
Thread.sleep(1000);
cardLayout.show(cardPanel, "running");
}
catch (Exception ex) {
// Ignore.
}
freshStart = false;
}
else {
// Clear Text
pane.setText("");
cardLayout.show(cardPanel, "running");
}
}
}
private synchronized void stopApplication() {
if (openfired != null) {
try {
// attempt to perform a graceful shutdown by sending
// an "exit" command to the process (via stdin)
try (Writer out = new OutputStreamWriter(
new BufferedOutputStream(openfired.getOutputStream()))) {
out.write("exit\n");
}
final Thread waiting = Thread.currentThread();
Thread waiter = new Thread() {
@Override
public void run() {
try {
// wait for the openfire server to stop
openfired.waitFor();
waiting.interrupt();
}
catch (InterruptedException ie) { /* ignore */ }
}
};
waiter.start();
try {
// wait for a maximum of ten seconds
Thread.sleep(10000);
waiter.interrupt();
openfired.destroy();
}
catch (InterruptedException ie) { /* ignore */ }
cardLayout.show(cardPanel, "main");
}
catch (Exception e) {
e.printStackTrace();
}
}
openfired = null;
}
private synchronized void launchBrowser() {
try {
// Note, we use standard DOM to read in the XML. This is necessary so that
// Launcher has fewer dependencies.
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
Document document = factory.newDocumentBuilder().parse(configFile);
Element rootElement = document.getDocumentElement();
Element adminElement = (Element)rootElement.getElementsByTagName("adminConsole").item(0);
String port = "-1";
String securePort = "-1";
Element portElement = (Element)adminElement.getElementsByTagName("port").item(0);
if (portElement != null) {
port = portElement.getTextContent();
}
Element securePortElement = (Element)adminElement.getElementsByTagName("securePort").item(0);
if (securePortElement != null) {
securePort = securePortElement.getTextContent();
}
if ("-1".equals(port)) {
Desktop.getDesktop().browse(URI.create("https://127.0.0.1:" + securePort + "/index.html"));
} else {
Desktop.getDesktop().browse(URI.create("http://127.0.0.1:" + port + "/index.html"));
}
}
catch (Exception e) {
// Make sure to print the exception
e.printStackTrace(System.out);
JOptionPane.showMessageDialog(new JFrame(), configFile + " " + e.getMessage());
}
}
private void installPlugin(final File plugin) {
final JDialog dialog = new JDialog(frame, "Installing Plugin", true);
dialog.getContentPane().setLayout(new BorderLayout());
JProgressBar bar = new JProgressBar();
bar.setIndeterminate(true);
bar.setString("Installing Plugin. Please wait...");
bar.setStringPainted(true);
dialog.getContentPane().add(bar, BorderLayout.CENTER);
dialog.pack();
dialog.setSize(225, 55);
final SwingWorker installerThread = new SwingWorker() {
@Override
public File doInBackground() {
File pluginsDir = new File(binDir.getParentFile(), "plugins");
String tempName = plugin.getName() + ".part";
File tempPluginsFile = new File(pluginsDir, tempName);
File realPluginsFile = new File(pluginsDir, plugin.getName());
// Copy Plugin into Dir.
try {
// Just for fun. Show no matter what for two seconds.
Thread.sleep(2000);
copy(plugin.toURI().toURL(), tempPluginsFile);
// If successfull, rename to real plugin name.
tempPluginsFile.renameTo(realPluginsFile);
}
catch (Exception e) {
e.printStackTrace();
}
return realPluginsFile;
}
@Override
public void done() {
dialog.setVisible(false);
}
};
// Start installation
installerThread.execute();
dialog.setLocationRelativeTo(frame);
dialog.setVisible(true);
}
private static void copy(URL src, File dst) throws IOException {
try (InputStream in = src.openStream()) {
try (OutputStream out = new FileOutputStream(dst)) {
dst.mkdirs();
copy(in, out);
}
}
}
/**
* Common code for copy routines. By convention, the streams are
* closed in the same method in which they were opened. Thus,
* this method does not close the streams when the copying is done.
*/
private static void copy(InputStream in, OutputStream out) throws IOException {
byte[] buffer = new byte[4096];
while (true) {
int bytesRead = in.read(buffer);
if (bytesRead < 0) {
break;
}
out.write(buffer, 0, bytesRead);
}
}
}