/* * (C) Copyright 2011-2014 Nuxeo SA (http://nuxeo.com/) and contributors. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the GNU Lesser General Public License * (LGPL) version 2.1 which accompanies this distribution, and is available at * http://www.gnu.org/licenses/lgpl-2.1.html * * This library 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. * * Contributors: * Julien Carsique * */ package org.nuxeo.launcher.gui; import java.awt.Dimension; import java.awt.HeadlessException; import java.awt.Toolkit; import java.io.File; import java.io.FileReader; import java.io.IOException; import java.text.MessageFormat; import java.util.HashMap; import java.util.Locale; import java.util.MissingResourceException; import java.util.Properties; import java.util.ResourceBundle; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import javax.swing.SwingUtilities; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.commons.vfs2.FileChangeEvent; import org.apache.commons.vfs2.FileListener; import org.apache.commons.vfs2.FileObject; import org.apache.commons.vfs2.FileSystemException; import org.apache.commons.vfs2.VFS; import org.apache.commons.vfs2.impl.DefaultFileMonitor; import org.artofsolving.jodconverter.util.PlatformUtils; import org.nuxeo.connect.update.PackageException; import org.nuxeo.launcher.NuxeoLauncher; import org.nuxeo.launcher.config.ConfigurationGenerator; import org.nuxeo.launcher.daemon.DaemonThreadFactory; import org.nuxeo.launcher.gui.logs.LogsHandler; import org.nuxeo.launcher.gui.logs.LogsSource; import org.nuxeo.launcher.gui.logs.LogsSourceThread; /** * Launcher controller for graphical user interface * * @author jcarsique * @since 5.4.2 * @see NuxeoLauncher */ public class NuxeoLauncherGUI { static final Log log = LogFactory.getLog(NuxeoLauncherGUI.class); protected static final long UPDATE_FREQUENCY = 3000; private ExecutorService executor = newExecutor(); /** * @since 5.6 */ protected ExecutorService newExecutor() { return Executors.newCachedThreadPool(new DaemonThreadFactory( "NuxeoLauncherGUITask")); } protected NuxeoLauncher launcher; protected NuxeoFrame nuxeoFrame; protected HashMap<String, LogsSourceThread> logsMap = new HashMap<>(); /** * @since 5.6 */ public final HashMap<String, LogsSourceThread> getLogsMap() { return logsMap; } private DefaultFileMonitor dumpedConfigMonitor; private Thread nuxeoFrameUpdater; /** * @param aLauncher Launcher being used in background */ public NuxeoLauncherGUI(NuxeoLauncher aLauncher) { launcher = aLauncher; // Set OS-specific decorations if (PlatformUtils.isMac()) { System.setProperty("apple.laf.useScreenMenuBar", "true"); System.setProperty("com.apple.mrj.application.growbox.intrudes", "false"); System.setProperty("com.apple.mrj.application.live-resize", "true"); System.setProperty("com.apple.macos.smallTabs", "true"); } initFrame(); dumpedConfigMonitor = new DefaultFileMonitor(new FileListener() { @Override public void fileDeleted(FileChangeEvent event) throws Exception { // Ignore } @Override public void fileCreated(FileChangeEvent event) throws Exception { updateNuxeoFrame(); } @Override public void fileChanged(FileChangeEvent event) throws Exception { updateNuxeoFrame(); } synchronized private void updateNuxeoFrame() { waitForFrameLoaded(); log.debug("Configuration changed. Reloading frame..."); launcher.init(); updateServerStatus(); try { Properties props = new Properties(); props.load(new FileReader( getConfigurationGenerator().getDumpedConfig())); nuxeoFrame.updateLogsTab(props.getProperty("log.id")); } catch (IOException e) { log.error(e); } } }); try { dumpedConfigMonitor.setRecursive(false); FileObject dumpedConfig = VFS.getManager().resolveFile( getConfigurationGenerator().getDumpedConfig().getPath()); dumpedConfigMonitor.addFile(dumpedConfig); dumpedConfigMonitor.start(); } catch (FileSystemException e) { throw new RuntimeException("Couldn't find " + getConfigurationGenerator().getNuxeoConf(), e); } } protected void initFrame() { final NuxeoLauncherGUI controller = this; SwingUtilities.invokeLater(new Runnable() { @Override public void run() { try { if (nuxeoFrame != null) { executor.shutdownNow(); nuxeoFrame.close(); executor = newExecutor(); } nuxeoFrame = createNuxeoFrame(controller); nuxeoFrame.pack(); // Center frame Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); nuxeoFrame.setLocation( screenSize.width / 2 - (nuxeoFrame.getWidth() / 2), screenSize.height / 2 - (nuxeoFrame.getHeight() / 2)); nuxeoFrame.setVisible(true); } catch (HeadlessException e) { log.error(e); } } }); if (nuxeoFrameUpdater == null) { nuxeoFrameUpdater = new Thread() { @Override public void run() { while (true) { updateServerStatus(); try { Thread.sleep(UPDATE_FREQUENCY); } catch (InterruptedException e) { break; } } } }; nuxeoFrameUpdater.start(); } } /** * Instantiate a new {@link NuxeoFrame}. Can be overridden if needed. * * @param controller * @return */ protected NuxeoFrame createNuxeoFrame(NuxeoLauncherGUI controller) { return new NuxeoFrame(controller); } public void initLogsManagement(String logFile, ColoredTextPane textArea) { File file = new File(logFile); LogsSource logsSource = new LogsSource(file); logsSource.skip(file.length() - NuxeoFrame.LOG_MAX_SIZE); logsSource.addObserver(new LogsHandler(textArea)); LogsSourceThread logsSourceThread = new LogsSourceThread(logsSource); logsSourceThread.setDaemon(true); executor.execute(logsSourceThread); logsMap.put(logFile, logsSourceThread); } /** * @see NuxeoLauncher#stop() */ public void stop() { waitForFrameLoaded(); nuxeoFrame.stopping = true; nuxeoFrame.mainButton.setText(getMessage("mainbutton.stop.inprogress")); nuxeoFrame.mainButton.setToolTipText(NuxeoLauncherGUI.getMessage("mainbutton.stop.tooltip")); nuxeoFrame.mainButton.setIcon(nuxeoFrame.stopIcon); executor.execute(new Runnable() { @Override public void run() { launcher.stop(); nuxeoFrame.stopping = false; updateServerStatus(); } }); } /** * Update interface information with current server status. * * @see {@link NuxeoFrame#updateMainButton()} * {@link NuxeoFrame#updateSummary()} */ public void updateServerStatus() { waitForFrameLoaded(); nuxeoFrame.updateMainButton(); nuxeoFrame.updateLaunchBrowserButton(); nuxeoFrame.updateSummary(); } /** * Waits for the Launcher GUI frame being initialized. Should be called * before any access to {@link NuxeoFrame} from this controller. */ public void waitForFrameLoaded() { while (nuxeoFrame == null) { try { Thread.sleep(500); } catch (InterruptedException e) { log.error(e); } } } /** * @see NuxeoLauncher#doStart() NuxeoLauncher#doStartAndWait() */ public void start() { waitForFrameLoaded(); nuxeoFrame.stopping = false; nuxeoFrame.mainButton.setText(NuxeoLauncherGUI.getMessage("mainbutton.start.inprogress")); nuxeoFrame.mainButton.setToolTipText(NuxeoLauncherGUI.getMessage("mainbutton.stop.tooltip")); nuxeoFrame.mainButton.setIcon(nuxeoFrame.stopIcon); executor.execute(new Runnable() { @Override public void run() { try { launcher.doStartAndWait(); } catch (PackageException e) { log.error("Could not initialize the packaging subsystem", e); System.exit(1); } updateServerStatus(); } }); } /** * @param logFile LogFile managed by the involved reader * @param isActive Set logs reader active or not */ public void notifyLogsObserver(String logFile, boolean isActive) { LogsSourceThread logsSourceThread = logsMap.get(logFile); if (isActive) { logsSourceThread.getSource().resume(); } else { logsSourceThread.getSource().pause(); } } /** * @return Configuration generator used by {@link #launcher} */ public ConfigurationGenerator getConfigurationGenerator() { return launcher.getConfigurationGenerator(); } /** * Get internationalized message * * @param key Message key * @return Localized message value */ public static String getMessage(String key) { String message; try { message = ResourceBundle.getBundle("i18n/messages").getString(key); } catch (MissingResourceException e) { log.debug(getMessage("missing.translation") + key); message = ResourceBundle.getBundle("i18n/messages", Locale.ENGLISH).getString( key); } return message; } /** * Get internationalized message with parameters * * @param key Message key * @param params * @return Localized message value * * @since 5.9.2 */ public static String getMessage(String key, Object... params) { return MessageFormat.format(getMessage(key), params); } /** * @return the NuxeoLauncher managed by the current GUI * @since 5.5 */ public NuxeoLauncher getLauncher() { return launcher; } }