/*******************************************************************************
* Copyright (c) 2016 Weasis Team and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Nicolas Roduit - initial API and implementation
*******************************************************************************/
package org.weasis.launcher;
import java.awt.Desktop;
import java.awt.EventQueue;
import java.awt.Font;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.lang.management.ManagementFactory;
import java.lang.reflect.Method;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import java.util.ServiceLoader;
import java.util.Timer;
import java.util.TimerTask;
import javax.management.InstanceNotFoundException;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JTextPane;
import javax.swing.RootPaneContainer;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.event.HyperlinkEvent;
import javax.swing.event.HyperlinkListener;
import javax.swing.text.html.HTMLEditorKit;
import javax.swing.text.html.StyleSheet;
import org.apache.felix.framework.Felix;
import org.apache.felix.framework.util.FelixConstants;
import org.apache.felix.framework.util.Util;
import org.osgi.framework.Bundle;
import org.osgi.framework.Constants;
import org.osgi.framework.Version;
import org.osgi.util.tracker.ServiceTracker;
import org.weasis.launcher.applet.WeasisFrame;
public class WeasisLauncher {
public enum STATE {
UNINSTALLED(0x00000001), INSTALLED(0x00000002), RESOLVED(0x00000004), STARTING(0x00000008),
STOPPING(0x00000010), ACTIVE(0x00000020);
private int state;
private STATE(int state) {
this.state = state;
}
public static String valueOf(int state) {
for (STATE s : STATE.values()) {
if (s.state == state) {
return s.name();
}
}
return "UNKNOWN"; //$NON-NLS-1$
}
}
/**
* Switch for specifying bundle directory.
**/
public static final String BUNDLE_DIR_SWITCH = "-b"; //$NON-NLS-1$
/**
* The property name used to specify whether the launcher should install a shutdown hook.
**/
public static final String SHUTDOWN_HOOK_PROP = "felix.shutdown.hook"; //$NON-NLS-1$
/**
* The property name used to specify an URL to the configuration property file to be used for the created the
* framework instance.
**/
public static final String CONFIG_PROPERTIES_PROP = "felix.config.properties"; //$NON-NLS-1$
/**
* The default name used for the configuration properties file.
**/
public static final String CONFIG_PROPERTIES_FILE_VALUE = "config.properties"; //$NON-NLS-1$
/**
* The property name used to specify an URL to the extended property file.
**/
public static final String EXTENDED_PROPERTIES_PROP = "felix.extended.config.properties"; //$NON-NLS-1$
/**
* The default name used for the extended properties file.
**/
public static final String EXTENDED_PROPERTIES_FILE_VALUE = "ext-config.properties"; //$NON-NLS-1$
/**
* Name of the configuration directory.
*/
public static final String CONFIG_DIRECTORY = "conf"; //$NON-NLS-1$
static volatile Felix m_felix = null;
static volatile ServiceTracker m_tracker = null;
static volatile boolean frameworkLoaded = false;
private static String APP_PROPERTY_FILE = "weasis.properties"; //$NON-NLS-1$
public static final String P_WEASIS_VERSION = "weasis.version"; //$NON-NLS-1$
public static final String P_WEASIS_PROFILE = "weasis.profile"; //$NON-NLS-1$
public static final String P_WEASIS_NAME = "weasis.name"; //$NON-NLS-1$
public static final String P_WEASIS_PATH = "weasis.path"; //$NON-NLS-1$
private static final String P_WEASIS_RES_DATE = "weasis.resources.date"; //$NON-NLS-1$
static Properties modulesi18n = null;
private static String look = null;
private static RemotePreferences REMOTE_PREFS;
private static File prefDir;
/**
* <p>
* This method performs the main task of constructing an framework instance and starting its execution. The
* following functions are performed when invoked:
* </p>
* <ol>
* <li><i><b>Examine and verify command-line arguments.</b></i> The launcher accepts a "<tt>-b</tt>" command line
* switch to set the bundle auto-deploy directory and a single argument to set the bundle cache directory.</li>
* <li><i><b>Read the system properties file.</b></i> This is a file containing properties to be pushed into
* <tt>System.setProperty()</tt> before starting the framework. This mechanism is mainly shorthand for people
* starting the framework from the command line to avoid having to specify a bunch of <tt>-D</tt> system property
* definitions. The only properties defined in this file that will impact the framework's behavior are the those
* concerning setting HTTP proxies, such as <tt>http.proxyHost</tt>, <tt>http.proxyPort</tt>, and
* <tt>http.proxyAuth</tt>. Generally speaking, the framework does not use system properties at all.</li>
* <li><i><b>Read the framework's configuration property file.</b></i> This is a file containing properties used to
* configure the framework instance and to pass configuration information into bundles installed into the framework
* instance. The configuration property file is called <tt>config.properties</tt> by default and is located in the
* <tt>conf/</tt> directory of the Felix installation directory, which is the parent directory of the directory
* containing the <tt>felix.jar</tt> file. It is possible to use a different location for the property file by
* specifying the desired URL using the <tt>felix.config.properties</tt> system property; this should be set using
* the <tt>-D</tt> syntax when executing the JVM. If the <tt>config.properties</tt> file cannot be found, then
* default values are used for all configuration properties. Refer to the <a href="Felix.html#Felix(java.util.Map)">
* <tt>Felix</tt></a> constructor documentation for more information on framework configuration properties.</li>
* <li><i><b>Copy configuration properties specified as system properties into the set of configuration
* properties.</b></i> Even though the Felix framework does not consult system properties for configuration
* information, sometimes it is convenient to specify them on the command line when launching Felix. To make this
* possible, the Felix launcher copies any configuration properties specified as system properties into the set of
* configuration properties passed into Felix.</li>
* <li><i><b>Add shutdown hook.</b></i> To make sure the framework shutdowns cleanly, the launcher installs a
* shutdown hook; this can be disabled with the <tt>felix.shutdown.hook</tt> configuration property.</li>
* <li><i><b>Create and initialize a framework instance.</b></i> The OSGi standard <tt>FrameworkFactory</tt> is
* retrieved from <tt>META-INF/services</tt> and used to create a framework instance with the configuration
* properties.</li>
* <li><i><b>Auto-deploy bundles.</b></i> All bundles in the auto-deploy directory are deployed into the framework
* instance.</li>
* <li><i><b>Start the framework.</b></i> The framework is started and the launcher thread waits for the framework
* to shutdown.</li>
* </ol>
* <p>
* It should be noted that simply starting an instance of the framework is not enough to create an interactive
* session with it. It is necessary to install and start bundles that provide a some means to interact with the
* framework; this is generally done by bundles in the auto-deploy directory or specifying an "auto-start" property
* in the configuration property file. If no bundles providing a means to interact with the framework are installed
* or if the configuration property file cannot be found, the framework will appear to be hung or deadlocked. This
* is not the case, it is executing correctly, there is just no way to interact with it.
* </p>
* <p>
* The launcher provides two ways to deploy bundles into a framework at startup, which have associated configuration
* properties:
* </p>
* <ul>
* <li>Bundle auto-deploy - Automatically deploys all bundles from a specified directory, controlled by the
* following configuration properties:
* <ul>
* <li><tt>felix.auto.deploy.dir</tt> - Specifies the auto-deploy directory from which bundles are automatically
* deploy at framework startup. The default is the <tt>bundle/</tt> directory of the current directory.</li>
* <li><tt>felix.auto.deploy.action</tt> - Specifies the auto-deploy actions to be found on bundle JAR files found
* in the auto-deploy directory. The possible actions are <tt>install</tt>, <tt>update</tt>, <tt>start</tt>, and
* <tt>uninstall</tt>. If no actions are specified, then the auto-deploy directory is not processed. There is no
* default value for this property.</li>
* </ul>
* </li>
* <li>Bundle auto-properties - Configuration properties which specify URLs to bundles to install/start:
* <ul>
* <li><tt>felix.auto.install.N</tt> - Space-delimited list of bundle URLs to automatically install when the
* framework is started, where <tt>N</tt> is the start level into which the bundle will be installed (e.g.,
* felix.auto.install.2).</li>
* <li><tt>felix.auto.start.N</tt> - Space-delimited list of bundle URLs to automatically install and start when the
* framework is started, where <tt>N</tt> is the start level into which the bundle will be installed (e.g.,
* felix.auto.start.2).</li>
* </ul>
* </li>
* </ul>
* <p>
* These properties should be specified in the <tt>config.properties</tt> so that they can be processed by the
* launcher during the framework startup process.
* </p>
*
* @param argv
* Accepts arguments to set the auto-deploy directory and/or the bundle cache directory.
* @throws Exception
* If an error occurs.
**/
public static void main(String[] argv) throws Exception {
launch(argv);
}
public static void setJnlpSystemProperties() {
final String PREFIX = "jnlp.weasis."; //$NON-NLS-1$
final int PREFIX_LENGTH = PREFIX.length();
Properties properties = System.getProperties();
for (String propertyName : properties.stringPropertyNames()) {
if (propertyName.startsWith(PREFIX)) {
String value = properties.getProperty(propertyName);
System.setProperty(propertyName.substring(PREFIX_LENGTH), value);
properties.remove(propertyName);
}
}
// Disabling extension framework is mandatory to work with Java Web Start.
// From framework 4.4.1, See https://issues.apache.org/jira/browse/FELIX-4281.
System.setProperty(FelixConstants.FELIX_EXTENSIONS_DISABLE, "true"); //$NON-NLS-1$
}
public static void launch(String[] argv) throws Exception {
// Set system property for dynamically loading only native libraries corresponding of the current platform
setSystemSpecification();
// Remove the prefix "jnlp.weasis" of JNLP Properties
// Workaround for having a fully trusted application with JWS,
// http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6653241
setJnlpSystemProperties();
for (int i = 0; i < argv.length; i++) {
// @Deprecated : use properties with the prefix "jnlp.weasis" instead
if (argv[i].startsWith("-VMP") && argv[i].length() > 4) { //$NON-NLS-1$
String[] vmarg = argv[i].substring(4).split("=", 2); //$NON-NLS-1$
if (vmarg.length == 2) {
if (vmarg[1].startsWith("\"") && vmarg[1].endsWith("\"")) { //$NON-NLS-1$ //$NON-NLS-2$
vmarg[1] = vmarg[1].substring(1, vmarg[1].length() - 1);
}
System.setProperty(vmarg[0], Util.substVars(vmarg[1], vmarg[0], null, null));
}
}
}
final List<StringBuilder> commandList = splitCommand(argv);
// Look for bundle directory and/or cache directory.
// We support at most one argument, which is the bundle
// cache directory.
String bundleDir = null;
String cacheDir = null;
for (StringBuilder c : commandList) {
String command = c.toString();
if (command.startsWith("felix")) { //$NON-NLS-1$
String[] params = command.split(" "); //$NON-NLS-1$
if (params.length < 3 || params.length > 4) {
System.err.println("Usage: [$felix -b <bundle-deploy-dir>] [<bundle-cache-dir>]"); //$NON-NLS-1$
} else {
bundleDir = params[2];
if (params.length > 3) {
cacheDir = params[3];
}
}
commandList.remove(c);
break;
}
}
String portable = System.getProperty("weasis.portable.dir"); //$NON-NLS-1$
if (portable != null) {
File basePortableDir = new File(portable);
String baseURL = ""; //$NON-NLS-1$
try {
baseURL = basePortableDir.toURI().toURL().toString() + "weasis"; //$NON-NLS-1$
System.setProperty("weasis.codebase.url", baseURL); //$NON-NLS-1$
baseURL += "/" + CONFIG_DIRECTORY + "/"; //$NON-NLS-1$ //$NON-NLS-2$
if (System.getProperty(CONFIG_PROPERTIES_PROP) == null) {
System.setProperty(CONFIG_PROPERTIES_PROP, baseURL + CONFIG_PROPERTIES_FILE_VALUE);
}
if (System.getProperty(EXTENDED_PROPERTIES_PROP) == null) {
System.setProperty(EXTENDED_PROPERTIES_PROP, baseURL + EXTENDED_PROPERTIES_FILE_VALUE);
}
// Allow export feature for portable version
System.setProperty("weasis.export.dicom", "true"); //$NON-NLS-1$ //$NON-NLS-2$
System.setProperty("weasis.export.dicom.send", "true"); //$NON-NLS-1$ //$NON-NLS-2$
System.setProperty("weasis.import.dicom", "true"); //$NON-NLS-1$ //$NON-NLS-2$
System.setProperty("weasis.import.dicom.qr", "true"); //$NON-NLS-1$ //$NON-NLS-2$
} catch (Exception e) {
e.printStackTrace();
}
}
System.out.println();
System.out.println("***** Starting Configuration *****"); //$NON-NLS-1$
// Read configuration properties.
Map<String, String> serverProp = WeasisLauncher.loadConfigProperties();
// If no configuration properties were found, then create
// an empty properties object.
if (serverProp == null) {
System.err.println("Cannot start, no " + CONFIG_PROPERTIES_FILE_VALUE + " found."); //$NON-NLS-1$ //$NON-NLS-2$
serverProp = new HashMap<>();
}
// If there is a passed in bundle auto-deploy directory, then
// that overwrites anything in the config file.
if (bundleDir != null) {
serverProp.put(AutoProcessor.AUTO_DEPLOY_DIR_PROPERTY, bundleDir);
}
String profileName = serverProp.getOrDefault(P_WEASIS_PROFILE, "default"); //$NON-NLS-1$
serverProp.put(P_WEASIS_PROFILE, profileName);
// Define the sourceID for the temp and cache directory. The portable version will always have the same
// sourceID.
String sourceID =
toHex((portable == null ? System.getProperty("weasis.codebase.url", "unknown") + profileName : "portable") //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
.hashCode());
System.setProperty("weasis.source.id", sourceID); //$NON-NLS-1$
cacheDir = serverProp.get(Constants.FRAMEWORK_STORAGE) + "-" + sourceID; //$NON-NLS-1$
// If there is a passed in bundle cache directory, then
// that overwrites anything in the config file.
serverProp.put(Constants.FRAMEWORK_STORAGE, cacheDir);
// Load local properties and clean if necessary the previous version
WeasisLoader loader = loadProperties(serverProp);
final WeasisFrame mainFrame = loader.getMainFrame();
final Properties localProp = loader.getLocalProperties();
// If enabled, register a shutdown hook to make sure the framework is
// cleanly shutdown when the VM exits.
JVMShutdownHook shutdownHook = new JVMShutdownHook();
Runtime.getRuntime().addShutdownHook(shutdownHook);
registerAdditionalShutdownHook();
System.out.println(""); //$NON-NLS-1$
System.out.println("Starting..."); //$NON-NLS-1$
System.out.println(""); //$NON-NLS-1$
System.out.println("| | /| / /__ ___ ____ (_)__"); //$NON-NLS-1$
System.out.println("| |/ |/ / -_) _ `(_-</ (_-<"); //$NON-NLS-1$
System.out.println("|__/|__/\\__/\\_,_/___/_/___/"); //$NON-NLS-1$
System.out.println(""); //$NON-NLS-1$
int exitStatus = 0;
try {
// Now create an instance of the framework with our configuration properties.
m_felix = new Felix(serverProp);
// Initialize the framework, but don't start it yet.
m_felix.init();
// Use the system bundle context to process the auto-deploy
// and auto-install/auto-start properties.
loader.setFelix(serverProp, m_felix.getBundleContext());
loader.writeLabel(
String.format(Messages.getString("WeasisLauncher.starting"), System.getProperty(P_WEASIS_NAME))); //$NON-NLS-1$
m_tracker =
new ServiceTracker(m_felix.getBundleContext(), "org.apache.felix.service.command.CommandProcessor", //$NON-NLS-1$
null);
m_tracker.open();
// Start the framework.
m_felix.start();
// End of splash screen
loader.close();
loader = null;
// Start telnet after all other bundles. This will ensure that all the plugins commands are activated once
// telnet is available
for (Bundle b : m_felix.getBundleContext().getBundles()) {
if (b.getSymbolicName().equals("org.apache.felix.gogo.shell") && b.getState() == Bundle.INSTALLED) { //$NON-NLS-1$
b.start();
break;
}
}
SwingUtilities.invokeLater(() -> {
m_tracker.open();
Object commandSession = getCommandSession(m_tracker.getService());
if (commandSession != null) {
// execute the commands from main argv
for (StringBuilder command : commandList) {
commandSession_execute(commandSession, command);
}
commandSession_close(commandSession);
}
m_tracker.close();
});
String mainUI = serverProp.getOrDefault("weasis.main.ui", ""); //$NON-NLS-1$ //$NON-NLS-2$
mainUI = mainUI.trim();
if (!"".equals(mainUI)) { //$NON-NLS-1$
boolean uiStarted = false;
for (Bundle b : m_felix.getBundleContext().getBundles()) {
if (b.getSymbolicName().equals(mainUI) && b.getState() == Bundle.ACTIVE) {
uiStarted = true;
break;
}
}
if (!uiStarted) {
throw new IllegalStateException("Main User Interface bundle cannot be started"); //$NON-NLS-1$
}
}
frameworkLoaded = true;
showMessage(mainFrame, serverProp, localProp);
// Wait for framework to stop to exit the VM.
m_felix.waitForStop(0);
System.exit(0);
} catch (Throwable ex) {
exitStatus = -1;
System.err.println("Cannot not start framework: " + ex); //$NON-NLS-1$
System.err.println("Weasis cache will be cleaned at next launch."); //$NON-NLS-1$
System.err.println("State of the framework:"); //$NON-NLS-1$
for (Bundle b : m_felix.getBundleContext().getBundles()) {
System.err.println(" * " + b.getSymbolicName() + "-" + b.getVersion().toString() + " " //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ STATE.valueOf(b.getState()));
}
resetBundleCache();
} finally {
Runtime.getRuntime().halt(exitStatus);
}
}
private static void resetBundleCache() {
// Set flag to clean cache at next launch
File sourceIdProps =
new File(System.getProperty(P_WEASIS_PATH, ""), System.getProperty("weasis.source.id") + ".properties"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
Properties localSourceProp = readProperties(sourceIdProps);
localSourceProp.setProperty("weasis.clean.cache", "true"); //$NON-NLS-1$ //$NON-NLS-2$
FileUtil.storeProperties(sourceIdProps, localSourceProp, null);
}
private static void showMessage(final WeasisFrame mainFrame, Map<String, String> serverProp,
final Properties l_prop) {
String versionOld = serverProp.get("prev." + P_WEASIS_VERSION); //$NON-NLS-1$
String versionNew = serverProp.get(P_WEASIS_VERSION);
// First time launch
if (versionOld == null) {
String val = getGeneralProperty("weasis.show.disclaimer", "true", serverProp, l_prop, false, false); //$NON-NLS-1$ //$NON-NLS-2$
if (Boolean.valueOf(val)) {
EventQueue.invokeLater(() -> {
Object[] options =
{ Messages.getString("WeasisLauncher.ok"), Messages.getString("WeasisLauncher.no") }; //$NON-NLS-1$ //$NON-NLS-2$
String appName = System.getProperty(P_WEASIS_NAME);
int response = JOptionPane.showOptionDialog(
mainFrame.getRootPaneContainer() == null ? null
: mainFrame.getRootPaneContainer().getContentPane(),
String.format(Messages.getString("WeasisLauncher.msg"), appName), //$NON-NLS-1$
String.format(Messages.getString("WeasisLauncher.first"), appName), //$NON-NLS-1$
JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE, null, options, null);
if (response == 0) {
// Write "false" in weasis.properties. It can be useful when preferences are store remotely.
// The user will accept the disclaimer only once.
l_prop.setProperty("weasis.show.disclaimer", Boolean.FALSE.toString()); //$NON-NLS-1$
if (prefDir != null) {
FileUtil.storeProperties(new File(prefDir, APP_PROPERTY_FILE), l_prop, null);
}
} else {
File sourceID_props = new File(System.getProperty(P_WEASIS_PATH, ""), //$NON-NLS-1$
System.getProperty("weasis.source.id") + ".properties"); //$NON-NLS-1$ //$NON-NLS-2$
// delete the properties file to ask again
sourceID_props.delete();
System.err.println("Refusing the disclaimer"); //$NON-NLS-1$
System.exit(-1);
}
});
}
} else if (versionNew != null && !versionNew.equals(versionOld)) {
String val = getGeneralProperty("weasis.show.release", "true", serverProp, l_prop, false, false); //$NON-NLS-1$ //$NON-NLS-2$
if (Boolean.valueOf(val)) {
Version vOld = getVersion(versionOld);
Version vNew = getVersion(versionNew);
if (vNew.compareTo(vOld) > 0) {
String lastTag = l_prop.getProperty("weasis.version.release", null); //$NON-NLS-1$
if (lastTag != null) {
vOld = getVersion(lastTag);
if (vNew.compareTo(vOld) <= 0) {
// Message has been already displayed once.
return;
}
}
// Can be useful when preferences are store remotely.
// The user will see the release message only once.
l_prop.setProperty("weasis.version.release", vNew.toString()); //$NON-NLS-1$
if (prefDir != null) {
FileUtil.storeProperties(new File(prefDir, APP_PROPERTY_FILE), l_prop, null);
}
}
final String releaseNotesUrl = serverProp.get("weasis.releasenotes"); //$NON-NLS-1$
final StringBuilder message = new StringBuilder("<P>"); //$NON-NLS-1$
message.append(String.format(Messages.getString("WeasisLauncher.change.version"), //$NON-NLS-1$
System.getProperty(P_WEASIS_NAME), versionOld, versionNew));
EventQueue.invokeLater(() -> {
JTextPane jTextPane1 = new JTextPane();
HTMLEditorKit kit = new HTMLEditorKit();
StyleSheet ss = kit.getStyleSheet();
ss.addRule("body {font-family:sans-serif;font-size:12pt;background-color:#" //$NON-NLS-1$
+ Integer.toHexString((jTextPane1.getBackground().getRGB() & 0xffffff) | 0x1000000).substring(1)
+ ";color:#" //$NON-NLS-1$
+ Integer.toHexString((jTextPane1.getForeground().getRGB() & 0xffffff) | 0x1000000).substring(1)
+ ";margin:3;font-weight:normal;}"); //$NON-NLS-1$
jTextPane1.setContentType("text/html"); //$NON-NLS-1$
jTextPane1.setEditable(false);
jTextPane1.addHyperlinkListener(new HyperlinkListener() {
@Override
public void hyperlinkUpdate(HyperlinkEvent e) {
JTextPane pane = (JTextPane) e.getSource();
if (e.getEventType() == HyperlinkEvent.EventType.ENTERED) {
pane.setToolTipText(e.getDescription());
} else if (e.getEventType() == HyperlinkEvent.EventType.EXITED) {
pane.setToolTipText(null);
} else if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
if (System.getProperty("os.name", "unknown").toLowerCase().startsWith("linux")) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
try {
String cmd = String.format("xdg-open %s", e.getURL()); //$NON-NLS-1$
Runtime.getRuntime().exec(cmd);
} catch (IOException e1) {
System.err.println("Unable to launch the WEB browser"); //$NON-NLS-1$
e1.printStackTrace();
}
} else if (Desktop.isDesktopSupported()) {
final Desktop desktop = Desktop.getDesktop();
if (desktop.isSupported(Desktop.Action.BROWSE)) {
try {
desktop.browse(e.getURL().toURI());
} catch (Exception ex) {
System.err.println("Unable to launch the WEB browser"); //$NON-NLS-1$
}
}
}
}
}
});
message.append("<BR>"); //$NON-NLS-1$
String rn = Messages.getString("WeasisLauncher.release"); //$NON-NLS-1$
message.append(String.format("<a href=\"%s", //$NON-NLS-1$
releaseNotesUrl));
message.append("\" style=\"color:#FF9900\">"); //$NON-NLS-1$
message.append(rn);
message.append("</a>");//$NON-NLS-1$
message.append("</P>"); //$NON-NLS-1$
jTextPane1.setText(message.toString());
JOptionPane.showMessageDialog(
mainFrame.getRootPaneContainer() == null ? null
: mainFrame.getRootPaneContainer().getContentPane(),
jTextPane1, Messages.getString("WeasisLauncher.News"), JOptionPane.PLAIN_MESSAGE); //$NON-NLS-1$
});
}
}
}
private static Version getVersion(String version) {
String v = ""; //$NON-NLS-1$
if (version != null) {
int index = version.indexOf("-"); //$NON-NLS-1$
v = index > 0 ? version.substring(0, index) : version;
}
return new Version(v);
}
private static String toHex(int val) {
final char[] HEX_DIGIT = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
char[] ch8 = new char[8];
for (int i = 8; --i >= 0; val >>= 4) {
ch8[i] = HEX_DIGIT[val & 0xf];
}
return String.valueOf(ch8);
}
public static List<StringBuilder> splitCommand(String[] args) {
int length = args.length;
ArrayList<StringBuilder> list = new ArrayList<>(5);
for (int i = 0; i < length; i++) {
if (args[i].startsWith("$") && args[i].length() > 1) { //$NON-NLS-1$
StringBuilder command = new StringBuilder(args[i].substring(1));
// look for parameters
while (i + 1 < length && !args[i + 1].startsWith("$") && !args[i + 1].startsWith("-VMP")) { //$NON-NLS-1$ //$NON-NLS-2$
i++;
command.append(" "); //$NON-NLS-1$
if (args[i].indexOf(" ") != -1) { //$NON-NLS-1$
command.append("\""); //$NON-NLS-1$
command.append(args[i]);
command.append("\""); //$NON-NLS-1$
} else {
command.append(args[i]);
}
}
list.add(command);
}
}
// for (StringBuilder stringBuffer : list) {
// System.out.println("Command:" + stringBuffer);
// }
return list;
}
public static Object getCommandSession(Object commandProcessor) {
if (commandProcessor == null) {
return null;
}
Class<?>[] parameterTypes = new Class[] { InputStream.class, PrintStream.class, PrintStream.class };
Object[] arguments = new Object[] { System.in, System.out, System.err };
try {
Method nameMethod = commandProcessor.getClass().getMethod("createSession", parameterTypes); //$NON-NLS-1$
Object commandSession = nameMethod.invoke(commandProcessor, arguments);
return commandSession;
} catch (Exception ex) {
// Since the services returned by the tracker could become
// invalid at any moment, we will catch all exceptions, log
// a message, and then ignore faulty services.
System.err.println(ex);
}
return null;
}
public static boolean commandSession_close(Object commandSession) {
if (commandSession == null) {
return false;
}
try {
Method nameMethod = commandSession.getClass().getMethod("close"); //$NON-NLS-1$
nameMethod.invoke(commandSession);
return true;
} catch (Exception ex) {
// Since the services returned by the tracker could become
// invalid at any moment, we will catch all exceptions, log
// a message, and then ignore faulty services.
System.err.println(ex);
}
return false;
}
public static boolean commandSession_execute(Object commandSession, CharSequence charSequence) {
if (commandSession == null) {
return false;
}
Class[] parameterTypes = new Class[] { CharSequence.class };
Object[] arguments = new Object[] { charSequence };
try {
Method nameMethod = commandSession.getClass().getMethod("execute", parameterTypes); //$NON-NLS-1$
nameMethod.invoke(commandSession, arguments);
return true;
} catch (Exception ex) {
// Since the services returned by the tracker could become
// invalid at any moment, we will catch all exceptions, log
// a message, and then ignore faulty services.
System.err.println(ex);
ex.printStackTrace();
}
return false;
}
/**
* This following part has been copied from the Main class of the Felix project
*
**/
/**
* <p>
* Loads the configuration properties in the configuration property file associated with the framework installation;
* these properties are accessible to the framework and to bundles and are intended for configuration purposes. By
* default, the configuration property file is located in the <tt>conf/</tt> directory of the Felix installation
* directory and is called "<tt>config.properties</tt>". The installation directory of Felix is assumed to be the
* parent directory of the <tt>felix.jar</tt> file as found on the system class path property. The precise file from
* which to load configuration properties can be set by initializing the "<tt>felix.config.properties</tt>" system
* property to an arbitrary URL.
* </p>
*
* @return A <tt>Properties</tt> instance or <tt>null</tt> if there was an error.
**/
public static Map<String, String> loadConfigProperties() {
URI propURI = getPropertiesURI(CONFIG_PROPERTIES_PROP, CONFIG_PROPERTIES_FILE_VALUE);
// Read the properties file
Properties props = null;
if (propURI != null) {
System.out.println(CONFIG_PROPERTIES_PROP + ": " + propURI); //$NON-NLS-1$
props = readProperties(propURI, null);
} else {
System.err.println("No config.properties path found, Weasis cannot start!"); //$NON-NLS-1$
}
propURI = getPropertiesURI(EXTENDED_PROPERTIES_PROP, EXTENDED_PROPERTIES_FILE_VALUE);
if (propURI != null) {
System.out.println(EXTENDED_PROPERTIES_PROP + ": " + propURI); //$NON-NLS-1$
// Extended properties, add or override existing properties
props = readProperties(propURI, props);
}
// Perform variable substitution for system properties and
// convert to dictionary.
Map<String, String> map = new HashMap<>();
if (props != null) {
for (Enumeration<?> e = props.propertyNames(); e.hasMoreElements();) {
String name = (String) e.nextElement();
map.put(name, Util.substVars(props.getProperty(name), name, null, props));
}
}
return map;
}
public static URI getPropertiesURI(String configProp, String configFile) {
// The config properties file is either specified by a system
// property or it is in the conf/ directory of the Felix
// installation directory. Try to load it from one of these
// places.
// See if the property URL was specified as a property.
URI propURL;
String custom = System.getProperty(configProp);
if (custom != null) {
try {
propURL = new URI(custom);
} catch (URISyntaxException e) {
System.err.print(configProp + ": " + e); //$NON-NLS-1$
return null;
}
} else {
// Determine where the configuration directory is by figuring
// out where felix.jar is located on the system class path.
File confDir;
String classpath = System.getProperty("java.class.path"); //$NON-NLS-1$
int index = classpath.toLowerCase().indexOf("felix.jar"); //$NON-NLS-1$
int start = classpath.lastIndexOf(File.pathSeparator, index) + 1;
if (index >= start) {
// Get the path of the felix.jar file.
String jarLocation = classpath.substring(start, index);
// Calculate the conf directory based on the parent
// directory of the felix.jar directory.
confDir = new File(new File(new File(jarLocation).getAbsolutePath()).getParent(), CONFIG_DIRECTORY);
} else {
// Can't figure it out so use the current directory as default.
confDir = new File(System.getProperty("user.dir"), CONFIG_DIRECTORY); //$NON-NLS-1$
}
try {
propURL = new File(confDir, configFile).toURI();
} catch (Exception ex) {
System.err.print(configFile + ": " + ex); //$NON-NLS-1$
return null;
}
}
return propURL;
}
public static Properties readProperties(URI propURI, Properties props) {
// Read the properties file.
if (props == null) {
props = new Properties();
}
InputStream is = null;
try {
// Try to load config.properties.
is = FileUtil.getAdaptedConnection(propURI.toURL()).getInputStream();
props.load(is);
is.close();
} catch (Exception ex) {
System.err.println("Cannot read properties file: " + propURI); //$NON-NLS-1$
FileUtil.safeClose(is);
return props;
}
return props;
}
private static String getGeneralProperty(String key, String defaultValue, Map<String, String> serverProp,
Properties localProp, boolean storeInLocalPref, boolean serviceProperty) {
String value = localProp.getProperty(key, null);
String defaultVal = System.getProperty(key, null);
if (defaultVal == null) {
defaultVal = serverProp.getOrDefault(key, defaultValue);
}
if (value == null) {
value = defaultVal;
if (storeInLocalPref && value != null) {
// When first launch, set property that can be written later
localProp.setProperty(key, value);
}
}
if (serviceProperty) {
serverProp.put(key, value);
serverProp.put("def." + key, defaultVal); //$NON-NLS-1$
}
System.out.println(key + ": " + value); //$NON-NLS-1$
return value;
}
public static void setSystemSpecification() {
// Follows the OSGI specification to use Bundle-NativeCode in the bundle fragment :
// http://www.osgi.org/Specifications/Reference
String osName = System.getProperty("os.name"); //$NON-NLS-1$
String osArch = System.getProperty("os.arch"); //$NON-NLS-1$
if (osName != null && !osName.trim().equals("") && osArch != null && !osArch.trim().equals("")) { //$NON-NLS-1$ //$NON-NLS-2$
if (osName.toLowerCase().startsWith("win")) { //$NON-NLS-1$
// All Windows versions with a specific processor architecture (x86 or x86-64) are grouped under
// windows. If you need to make different native libraries for the Windows versions, define it in the
// Bundle-NativeCode tag of the bundle fragment.
osName = "windows"; //$NON-NLS-1$
} else if (osName.equals("Mac OS X")) { //$NON-NLS-1$
osName = "macosx"; //$NON-NLS-1$
} else if (osName.equals("SymbianOS")) { //$NON-NLS-1$
osName = "epoc32"; //$NON-NLS-1$
} else if (osName.equals("hp-ux")) { //$NON-NLS-1$
osName = "hpux"; //$NON-NLS-1$
} else if (osName.equals("Mac OS")) { //$NON-NLS-1$
osName = "macos"; //$NON-NLS-1$
} else if (osName.equals("OS/2")) { //$NON-NLS-1$
osName = "os2"; //$NON-NLS-1$
} else if (osName.equals("procnto")) { //$NON-NLS-1$
osName = "qnx"; //$NON-NLS-1$
} else {
osName = osName.toLowerCase();
}
if (osArch.equals("pentium") || osArch.equals("i386") || osArch.equals("i486") || osArch.equals("i586") //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
|| osArch.equals("i686")) { //$NON-NLS-1$
osArch = "x86"; //$NON-NLS-1$
} else if (osArch.equals("amd64") || osArch.equals("em64t") || osArch.equals("x86_64")) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
osArch = "x86-64"; //$NON-NLS-1$
} else if (osArch.equals("power ppc")) { //$NON-NLS-1$
osArch = "powerpc"; //$NON-NLS-1$
} else if (osArch.equals("psc1k")) { //$NON-NLS-1$
osArch = "ignite"; //$NON-NLS-1$
} else {
osArch = osArch.toLowerCase();
}
System.setProperty("native.library.spec", osName + "-" + osArch); //$NON-NLS-1$ //$NON-NLS-2$
}
}
public static WeasisLoader loadProperties(Map<String, String> serverProp) {
System.out.println("Operating system: " + System.getProperty("native.library.spec")); //$NON-NLS-1$ //$NON-NLS-2$
String dir = new File(serverProp.get(Constants.FRAMEWORK_STORAGE)).getParent();
System.setProperty(P_WEASIS_PATH, dir);
String weasisName = serverProp.getOrDefault(P_WEASIS_NAME, "Weasis");//$NON-NLS-1$
System.setProperty(P_WEASIS_NAME, weasisName);
String profileName = serverProp.getOrDefault(P_WEASIS_PROFILE, "default"); //$NON-NLS-1$
System.setProperty(P_WEASIS_PROFILE, profileName);
String user = System.getProperty("weasis.user", null); //$NON-NLS-1$
boolean localSessionUser = user == null;
if (user == null) {
user = System.getProperty("user.name", "local"); //$NON-NLS-1$ //$NON-NLS-2$
}
System.setProperty("weasis.user", user); //$NON-NLS-1$
StringBuilder bufDir = new StringBuilder(dir);
bufDir.append(File.separator);
bufDir.append("preferences"); //$NON-NLS-1$
bufDir.append(File.separator);
bufDir.append(user);
bufDir.append(File.separator);
bufDir.append(profileName);
prefDir = new File(bufDir.toString());
try {
prefDir.mkdirs();
} catch (Exception e) {
prefDir = new File(dir);
e.printStackTrace();
}
System.out.println("Preferences directory: " + prefDir.getPath()); //$NON-NLS-1$
if (REMOTE_PREFS == null && user != null) {
ServiceLoader<RemotePreferences> prefs = ServiceLoader.load(RemotePreferences.class);
Iterator<RemotePreferences> commandsIterator = prefs.iterator();
while (commandsIterator.hasNext()) {
REMOTE_PREFS = commandsIterator.next();
REMOTE_PREFS.initialize(user, localSessionUser, profileName, bufDir.toString());
System.out.println("Loading remote preferences for : " + user); //$NON-NLS-1$
break;
}
}
if (REMOTE_PREFS != null) {
try {
REMOTE_PREFS.read();
} catch (Exception e) {
System.out.println("Cannot read preferences remotely: " + e.getMessage()); //$NON-NLS-1$
}
}
String portable = System.getProperty("weasis.portable.dir"); //$NON-NLS-1$
if (portable != null) {
System.out.println("Starting portable version"); //$NON-NLS-1$
System.setProperty("weasis.portable.dicom.directory", //$NON-NLS-1$
serverProp.get("weasis.portable.dicom.directory")); //$NON-NLS-1$
}
File profileProps = new File(prefDir, APP_PROPERTY_FILE);
Properties lProp = readProperties(profileProps);
// General Preferences priority order:
// 1) Last value (does not exist for first launch of Weasis in an operating system session).
// 2) Java System property
// 3) Property defined in weasis/conf/config.properties or in ext-config.properties (extension of config)
// 4) default value
final String lang = getGeneralProperty("locale.lang.code", "en", serverProp, lProp, true, false); //$NON-NLS-1$ //$NON-NLS-2$
getGeneralProperty("locale.format.code", "system", serverProp, lProp, true, false); //$NON-NLS-1$ //$NON-NLS-2$
// Set value back to the bundle context properties, sling logger uses bundleContext.getProperty(prop)
getGeneralProperty("org.apache.sling.commons.log.level", "INFO", serverProp, lProp, true, true); //$NON-NLS-1$ //$NON-NLS-2$
// Empty string make the file log writer disable
String logActivatation =
getGeneralProperty("org.apache.sling.commons.log.file.activate", "false", serverProp, lProp, true, true); //$NON-NLS-1$ //$NON-NLS-2$
if ("true".equalsIgnoreCase(logActivatation)) { //$NON-NLS-1$
String logFile = dir + File.separator + "log" + File.separator + "default.log"; //$NON-NLS-1$ //$NON-NLS-2$
serverProp.put("org.apache.sling.commons.log.file", logFile); //$NON-NLS-1$
lProp.remove("org.apache.sling.commons.log.file"); //$NON-NLS-1$
}
getGeneralProperty("org.apache.sling.commons.log.file.number", "5", serverProp, lProp, true, true); //$NON-NLS-1$ //$NON-NLS-2$
getGeneralProperty("org.apache.sling.commons.log.file.size", "10MB", serverProp, lProp, true, true); //$NON-NLS-1$ //$NON-NLS-2$
getGeneralProperty("org.apache.sling.commons.log.stack.limit", "3", serverProp, lProp, true, true); //$NON-NLS-1$ //$NON-NLS-2$
getGeneralProperty("org.apache.sling.commons.log.pattern", //$NON-NLS-1$
"{0,date,dd.MM.yyyy HH:mm:ss.SSS} *{4}* [{2}] {3}: {5}", serverProp, lProp, false, true); //$NON-NLS-1$
URI translationModules = null;
if (portable != null) {
File file = new File(portable, "weasis/bundle-i18n/buildNumber.properties"); //$NON-NLS-1$
if (file.canRead()) {
translationModules = file.toURI();
String path = file.getParentFile().toURI().toString();
System.setProperty("weasis.i18n", path); //$NON-NLS-1$
System.out.println("i18n path: " + path); //$NON-NLS-1$
}
} else {
String path = System.getProperty("weasis.i18n", null); //$NON-NLS-1$
if (path != null) {
path += path.endsWith("/") ? "buildNumber.properties" : "/buildNumber.properties"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
try {
translationModules = new URI(path);
} catch (URISyntaxException e) {
System.err.println("Cannot find translation modules: " + e); //$NON-NLS-1$
}
}
}
if (translationModules != null) {
modulesi18n = readProperties(translationModules, null);
if (modulesi18n != null) {
System.setProperty("weasis.languages", modulesi18n.getProperty("languages", "")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
}
}
Locale locale = textToLocale(lang);
if (Locale.ENGLISH.equals(locale)) {
// if English no need to load i18n bundle fragments
modulesi18n = null;
} else {
String suffix = locale.toString();
SwingResources.loadResources("/swing/basic_" + suffix + ".properties"); //$NON-NLS-1$ //$NON-NLS-2$
SwingResources.loadResources("/swing/synth_" + suffix + ".properties"); //$NON-NLS-1$ //$NON-NLS-2$
}
// JVM Locale
Locale.setDefault(locale);
// LookAndFeel Locale
UIManager.getDefaults().setDefaultLocale(locale);
// For new components
JComponent.setDefaultLocale(locale);
String nativeLook;
String sysSpec = System.getProperty("native.library.spec", "unknown"); //$NON-NLS-1$ //$NON-NLS-2$
int index = sysSpec.indexOf("-"); //$NON-NLS-1$
if (index > 0) {
nativeLook = "weasis.look." + sysSpec.substring(0, index); //$NON-NLS-1$
look = System.getProperty(nativeLook, null);
if (look == null) {
look = serverProp.get(nativeLook);
}
}
if (look == null) {
look = System.getProperty("weasis.look", null); //$NON-NLS-1$
if (look == null) {
look = serverProp.get("weasis.look"); //$NON-NLS-1$
}
}
String localLook = lProp.getProperty("weasis.look", null); //$NON-NLS-1$
// installSubstanceLookAndFeels must be the first condition to install substance if necessary
if (LookAndFeels.installSubstanceLookAndFeels() && look == null) {
if ("Mac OS X".equals(System.getProperty("os.name"))) { //$NON-NLS-1$ //$NON-NLS-2$
look = "com.apple.laf.AquaLookAndFeel"; //$NON-NLS-1$
} else {
look = "org.pushingpixels.substance.api.skin.SubstanceTwilightLookAndFeel"; //$NON-NLS-1$
}
}
// Set the default value for L&F
if (look == null) {
look = getAvailableLookAndFeel(look);
}
serverProp.put("weasis.look", look); //$NON-NLS-1$
// If look is in local prefs, use it
if (localLook != null) {
look = localLook;
}
/*
* Build a Frame or catch it from JApplet
*
* This will ensure the popup message or other dialogs to have frame parent. When the parent is null the dialog
* can be hidden under the main frame
*/
final WeasisFrame mainFrame = new WeasisFrame();
try {
SwingUtilities.invokeAndWait(() -> {
// Set look and feels
boolean substance = look.startsWith("org.pushingpixels"); //$NON-NLS-1$
if (substance) {
// Keep system window for the main frame
// JFrame.setDefaultLookAndFeelDecorated(true);
JDialog.setDefaultLookAndFeelDecorated(true);
}
look = setLookAndFeel(look);
Object instance = null;
MBeanServer server = ManagementFactory.getPlatformMBeanServer();
try {
ObjectName objectName1 = ObjectName.getInstance("weasis:name=MainWindow"); //$NON-NLS-1$
// Try to get frame from an Applet
instance = server.getAttribute(objectName1, "RootPaneContainer"); //$NON-NLS-1$
if (instance instanceof RootPaneContainer) {
mainFrame.setRootPaneContainer((RootPaneContainer) instance);
}
} catch (InstanceNotFoundException e2) {
} catch (Exception e3) {
// ignored
} finally {
try {
if (instance == null) {
// Build a JFrame which will be used later in base.ui module
ObjectName objectName2 = new ObjectName("weasis:name=MainWindow"); //$NON-NLS-1$
mainFrame.setRootPaneContainer(new JFrame());
server.registerMBean(mainFrame, objectName2);
}
} catch (Exception e1) {
e1.printStackTrace();
}
}
});
} catch (Exception e) {
System.err.println("WARNING : Unable to set the Look&Feel " + look); //$NON-NLS-1$
e.printStackTrace();
}
lProp.put("weasis.look", look); //$NON-NLS-1$
System.out.println("weasis.look: " + look); //$NON-NLS-1$
File sourceID_props = new File(dir, System.getProperty("weasis.source.id") + ".properties"); //$NON-NLS-1$ //$NON-NLS-2$
Properties localSourceProp = readProperties(sourceID_props);
String versionOld = localSourceProp.getProperty(P_WEASIS_VERSION);
System.out.println("Last running version: " + versionOld); //$NON-NLS-1$
if (versionOld != null) {
serverProp.put("prev." + P_WEASIS_VERSION, versionOld); //$NON-NLS-1$
}
String versionNew = serverProp.get(P_WEASIS_VERSION);
System.out.println("Current version: " + versionNew); //$NON-NLS-1$
String cleanCacheAfterCrash = localSourceProp.getProperty("weasis.clean.cache"); //$NON-NLS-1$
boolean update = false;
// Loads the resource files
String resPath = serverProp.getOrDefault("weasis.resources.url", //$NON-NLS-1$
System.getProperty("weasis.codebase.url", "") + "/resources.zip"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
File cacheDir = null;
try {
if (resPath.endsWith(".zip")) { //$NON-NLS-1$
cacheDir =
new File(dir + File.separator + "data" + File.separator + System.getProperty("weasis.source.id"), //$NON-NLS-1$ //$NON-NLS-2$
"resources"); //$NON-NLS-1$
String date =
FileUtil.writeResources(resPath, cacheDir, localSourceProp.getProperty(P_WEASIS_RES_DATE));
if (date != null) {
update = true;
localSourceProp.put(P_WEASIS_RES_DATE, date);
}
}
} catch (Exception e) {
cacheDir = null;
System.err.println(e.getMessage() + "\n"); //$NON-NLS-1$
}
if (cacheDir == null) {
if (portable != null) {
cacheDir = new File(portable, "weasis" + File.separator + "resources"); //$NON-NLS-1$ //$NON-NLS-2$
} else {
File f = new File(System.getProperty("user.dir")); //$NON-NLS-1$
cacheDir = new File(f.getParent(), "weasis-distributions" + File.separator + "resources"); //$NON-NLS-1$ //$NON-NLS-2$
}
}
System.out.println("weasis.resources.path: " + cacheDir.getPath()); //$NON-NLS-1$
serverProp.put("weasis.resources.path", cacheDir.getPath()); //$NON-NLS-1$
// Splash screen that shows bundles loading
final WeasisLoader loader = new WeasisLoader(cacheDir, mainFrame, lProp);
// Display splash screen
loader.open();
if (versionNew != null) {
// Add also to java properties for the about
System.setProperty(P_WEASIS_VERSION, versionNew);
localSourceProp.put(P_WEASIS_VERSION, versionNew);
if (versionOld == null || !versionOld.equals(versionNew)) {
update = true;
}
}
FileUtil.storeProperties(profileProps, lProp, null);
// Clean cache if Weasis has crashed during the previous launch
boolean cleanCache = Boolean.parseBoolean(serverProp.get("weasis.clean.previous.version")); //$NON-NLS-1$
if (cleanCacheAfterCrash != null && "true".equals(cleanCacheAfterCrash)) { //$NON-NLS-1$
serverProp.put(Constants.FRAMEWORK_STORAGE_CLEAN, Constants.FRAMEWORK_STORAGE_CLEAN_ONFIRSTINIT);
localSourceProp.remove("weasis.clean.cache"); //$NON-NLS-1$
update = true;
System.out.println("Clean plug-in cache because Weasis has crashed during the previous launch"); //$NON-NLS-1$
}
// Clean cache when version has changed
else if (cleanCache && versionNew != null) {
if (!versionNew.equals(versionOld)) {
System.out.println(String.format("Clean previous Weasis version: %s", versionOld)); //$NON-NLS-1$
serverProp.put(Constants.FRAMEWORK_STORAGE_CLEAN, Constants.FRAMEWORK_STORAGE_CLEAN_ONFIRSTINIT);
System.out.println("Clean plug-in cache because the version has changed"); //$NON-NLS-1$
}
}
if (update) {
FileUtil.storeProperties(sourceID_props, localSourceProp, null);
}
System.out.println("***** End of Configuration *****"); //$NON-NLS-1$
return loader;
}
private static Properties readProperties(File propsFile) {
Properties properties = new Properties();
if (propsFile.canRead()) {
try (FileInputStream fis = new FileInputStream(propsFile)) {
properties.load(fis);
} catch (Exception e) {
}
} else {
File appFoler = new File(System.getProperty(P_WEASIS_PATH, "")); //$NON-NLS-1$
appFoler.mkdirs();
}
return properties;
}
/**
* Changes the look and feel for the whole GUI
*/
public static String setLookAndFeel(String look) {
// Do not display metal LAF in bold, it is ugly
UIManager.put("swing.boldMetal", Boolean.FALSE); //$NON-NLS-1$
// Display slider value is set to false (already in all LAF by the panel title), used by GTK LAF
UIManager.put("Slider.paintValue", Boolean.FALSE); //$NON-NLS-1$
String laf = getAvailableLookAndFeel(look);
try {
UIManager.setLookAndFeel(laf);
} catch (Exception e) {
System.err.println("WARNING : Unable to set the Look&Feel"); //$NON-NLS-1$
laf = UIManager.getSystemLookAndFeelClassName();
}
// Fix font issue for displaying some Asiatic characters. Some L&F have special fonts.
LookAndFeels.setUIFont(new javax.swing.plaf.FontUIResource("SansSerif", Font.PLAIN, 12)); //$NON-NLS-1$
return laf;
}
public static String getAvailableLookAndFeel(String look) {
UIManager.LookAndFeelInfo[] lafs = UIManager.getInstalledLookAndFeels();
String laf = null;
if (look != null) {
for (int i = 0, n = lafs.length; i < n; i++) {
if (lafs[i].getClassName().equals(look)) {
laf = look;
break;
}
}
}
if (laf == null) {
if ("Mac OS X".equals(System.getProperty("os.name"))) { //$NON-NLS-1$ //$NON-NLS-2$
laf = "com.apple.laf.AquaLookAndFeel"; //$NON-NLS-1$
} else {
// Try to set Nimbus, concurrent thread issue
// http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6785663
for (int i = 0, n = lafs.length; i < n; i++) {
if (lafs[i].getName().equals("Nimbus")) { //$NON-NLS-1$
laf = lafs[i].getClassName();
break;
}
}
}
// Should never happen
if (laf == null) {
laf = UIManager.getSystemLookAndFeelClassName();
}
}
return laf;
}
static class HaltTask extends TimerTask {
@Override
public void run() {
System.out.println("Force to close the application"); //$NON-NLS-1$
Runtime.getRuntime().halt(1);
}
}
public static Locale textToLocale(String value) {
if (value == null || value.trim().equals("")) { //$NON-NLS-1$
return Locale.ENGLISH;
}
if ("system".equals(value)) { //$NON-NLS-1$
String language = System.getProperty("user.language", "en"); //$NON-NLS-1$ //$NON-NLS-2$
String country = System.getProperty("user.country", ""); //$NON-NLS-1$ //$NON-NLS-2$
String variant = System.getProperty("user.variant", ""); //$NON-NLS-1$ //$NON-NLS-2$
return new Locale(language, country, variant);
}
String[] val = value.split("_", 3); //$NON-NLS-1$
String language = val.length > 0 ? val[0] : ""; //$NON-NLS-1$
String country = val.length > 1 ? val[1] : ""; //$NON-NLS-1$
String variant = val.length > 2 ? val[2] : ""; //$NON-NLS-1$
return new Locale(language, country, variant);
}
private static void registerAdditionalShutdownHook() {
try {
Class.forName("sun.misc.Signal"); //$NON-NLS-1$
Class.forName("sun.misc.SignalHandler"); //$NON-NLS-1$
sun.misc.Signal.handle(new sun.misc.Signal("TERM"), new sun.misc.SignalHandler() { //$NON-NLS-1$
@Override
public void handle(sun.misc.Signal arg0) {
shutdownHook();
}
});
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
System.err.println("Cannot find sun.misc.Signal for shutdown hook exstension"); //$NON-NLS-1$
}
}
private static class JVMShutdownHook extends Thread {
@Override
public void run() {
shutdownHook();
}
}
private static void shutdownHook() {
try {
if (m_felix != null) {
m_felix.stop();
// wait asynchronous stop (max 30 seconds to stop all bundles)
m_felix.waitForStop(30000);
}
} catch (Exception ex) {
System.err.println("Error stopping framework: " + ex); //$NON-NLS-1$
} finally {
storeRemotePreferences();
cleanImageCache();
// If System.exit() hangs call Runtime.getRuntime().halt(1) to kill the application
Timer timer = new Timer();
timer.schedule(new HaltTask(), 15000);
}
}
static void storeRemotePreferences() {
// After all bundles has been stopped, we can copy the preferences
if (REMOTE_PREFS != null) {
try {
REMOTE_PREFS.store();
System.out.println("End of storing remote preferences."); //$NON-NLS-1$
} catch (Throwable e) { // should garanty the clean cache after
System.out.println("Cannot store preferences remotely: " + e.getMessage()); //$NON-NLS-1$
}
}
}
static void cleanImageCache() {
// Clean temp folder.
String dir = System.getProperty("weasis.tmp.dir"); //$NON-NLS-1$
if (dir != null) {
FileUtil.deleteDirectoryContents(new File(dir), 3, 0);
}
}
}