/* * (C) Copyright 2011-2015 Nuxeo SA (http://nuxeo.com/) and others. * * 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. * * 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.lang.SystemUtils; 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.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 (SystemUtils.IS_OS_MAC) { 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) { // Ignore } @Override public void fileCreated(FileChangeEvent event) { updateNuxeoFrame(); } @Override public void fileChanged(FileChangeEvent event) { 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 */ 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 NuxeoFrame#updateMainButton() * @see 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(launcher == null || launcher.getErrorValue() == NuxeoLauncher.EXIT_CODE_OK ? NuxeoLauncher.EXIT_CODE_INVALID : launcher.getErrorValue()); } 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; } }