/**
* OrbisGIS is a java GIS application dedicated to research in GIScience.
* OrbisGIS is developed by the GIS group of the DECIDE team of the
* Lab-STICC CNRS laboratory, see <http://www.lab-sticc.fr/>.
*
* The GIS group of the DECIDE team is located at :
*
* Laboratoire Lab-STICC – CNRS UMR 6285
* Equipe DECIDE
* UNIVERSITÉ DE BRETAGNE-SUD
* Institut Universitaire de Technologie de Vannes
* 8, Rue Montaigne - BP 561 56017 Vannes Cedex
*
* OrbisGIS is distributed under GPL 3 license.
*
* Copyright (C) 2007-2014 CNRS (IRSTV FR CNRS 2488)
* Copyright (C) 2015-2017 CNRS (Lab-STICC UMR CNRS 6285)
*
* This file is part of OrbisGIS.
*
* OrbisGIS is free software: you can redistribute it and/or modify it under the
* terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* OrbisGIS 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with
* OrbisGIS. If not, see <http://www.gnu.org/licenses/>.
*
* For more information, please consult: <http://www.orbisgis.org/>
* or contact directly:
* info_at_ orbisgis.org
*/
package org.orbisgis.framework;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
import java.nio.channels.OverlappingFileLockException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.Stack;
import javax.swing.JOptionPane;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.felix.framework.Logger;
import org.apache.felix.main.AutoProcessor;
import org.osgi.framework.Constants;
import org.osgi.framework.FrameworkEvent;
import org.osgi.framework.Version;
import org.osgi.framework.launch.Framework;
import org.xnap.commons.i18n.I18n;
import org.xnap.commons.i18n.I18nFactory;
/**
* Entry point of User Interface.
*/
final class Main {
private static final I18n I18N = I18nFactory.getI18n(Main.class);
private static boolean debugMode = false;
/** if true Remove configurations file and cache */
private static boolean noFailMode = false;
private static final int BUNDLE_STABILITY_TIMEOUT = 3000;
private static final int SAFE_MODE_COUNTDOWN_DELETE = 5000;
private static final Logger LOGGER = new Logger();
private static Version version;
private static final String OBR_REPOSITORY_URL = "obr.repository.url";
private static final String OBR_REPOSITORY_SNAPSHOT_URL = "obr.repository.snapshot.url";
//Minimum supported java version
public static final char MIN_JAVA_VERSION = '7';
protected static Framework m_fwk = null;
/**
* Utility class
*/
private Main() {
}
private static void parseCommandLine(String[] args) {
//Read parameters
Stack<String> sargs = new Stack<String>();
for (String arg : args) {
sargs.insertElementAt(arg, 0);
}
while (!sargs.empty()) {
String argument = sargs.pop();
if (argument.contentEquals("--debug")) {
debugMode = true;
} else if(argument.contentEquals("--nofailmode")) {
noFailMode = true;
}
}
}
/**
* Entry point of User Interface
*/
public static void main(String[] args) {
// Deactivate JOOQ Ascii Art printing
Properties properties = System.getProperties();
properties.setProperty("org.jooq.no-logo","True");
// debug try { Thread.sleep(5000); } catch (Exception ex) {}
long deploymentTime = 0;
BundleTools bundleTools = new BundleTools(LOGGER);
parseCommandLine(args);
//Check if the java version is greater than 1.6+
if (!isVersion(MIN_JAVA_VERSION)) {
JOptionPane.showMessageDialog(null, I18N.tr("OrbisGIS needs at least a java 1.7+"));
} else {
// Fetch application version
version = new Version(1, 0, 0);
try (InputStream fs = Main.class.getResourceAsStream("version.txt")) {
String versionTxt = IOUtils.readLines(fs).get(0);
version = new Version(versionTxt.replace("-", "."));
} catch (IOException ex) {
LOGGER.log(Logger.LOG_ERROR, ex.getLocalizedMessage(), ex);
}
// Create CoreWorkspace instance
CoreWorkspaceImpl coreWorkspace = new CoreWorkspaceImpl(version.getMajor(), version.getMinor(),
version.getMicro(), version.getQualifier(), LOGGER);
// Create Lock file if not exists
File lockFile = new File(coreWorkspace.getApplicationFolder(), "instance.lock");
if(!lockFile.exists()) {
try {
new File(coreWorkspace.getApplicationFolder()).mkdirs();
if(!lockFile.createNewFile()) {
LOGGER.log(Logger.LOG_ERROR, "Cannot create lock file !\n"+lockFile.getAbsolutePath());
return;
}
} catch (IOException ex) {
LOGGER.log(Logger.LOG_ERROR, "Application cache folder is not accessible !\n"+lockFile.getAbsolutePath(), ex);
return;
}
}
try(FileOutputStream fileOutputStream = new FileOutputStream(lockFile);
FileLock lock = fileOutputStream.getChannel().tryLock()) {
if(lock == null) {
LOGGER.log(Logger.LOG_ERROR, "Only a single instance of OrbisGIS can be run, please close other instance");
return;
}
// Fetch cache folder
File felixBundleCache = new File(coreWorkspace.getPluginCache());
// If safe mode delete cache
if (noFailMode && felixBundleCache.isDirectory()) {
System.err.println("Safe mode engaged, clear the following folder in 5 seconds..\n" + felixBundleCache);
try {
Thread.sleep(SAFE_MODE_COUNTDOWN_DELETE);
FileUtils.deleteDirectory(felixBundleCache);
} catch (InterruptedException ex) {
return;
} catch (IOException ex) {
LOGGER.log(Logger.LOG_ERROR, ex.getLocalizedMessage(), ex);
}
}
// Delete snapshot fragments bundles
long beginDeleteFragments = System.currentTimeMillis();
bundleTools.deleteFragmentInCache(felixBundleCache);
deploymentTime += System.currentTimeMillis() - beginDeleteFragments;
LOGGER.log(Logger.LOG_INFO, I18N.tr("Waiting for bundle stability, deployment of built-in bundles done in" + " {0} s", deploymentTime / 1000.0));
// Start main of felix framework
try {
String[] felixArgs = new String[]{"-b", BundleTools.BUNDLE_DIRECTORY, felixBundleCache.getAbsolutePath()};
LOGGER.log(Logger.LOG_INFO, "Start Apache Felix:\n" + Arrays.toString(felixArgs));
startFelix(BundleTools.BUNDLE_DIRECTORY, felixBundleCache.getAbsolutePath());
} catch (Exception ex) {
LOGGER.log(Logger.LOG_ERROR, ex.getLocalizedMessage(), ex);
}
} catch (OverlappingFileLockException ex) {
LOGGER.log(Logger.LOG_ERROR, "Only a single instance of OrbisGIS can be run, please close other instance");
} catch (IOException ex) {
LOGGER.log(Logger.LOG_ERROR, "Application cache folder is not accessible !\n"+lockFile.getAbsolutePath(), ex);
}
}
}
public static void startFelix(String bundleDir, String cacheDir) {
// Load system properties.
org.apache.felix.main.Main.loadSystemProperties();
// Read configuration properties.
Map<String, String> configProps = org.apache.felix.main.Main.loadConfigProperties();
// If no configuration properties were found, then create
// an empty properties object.
if (configProps == null)
{
System.err.println("No " + org.apache.felix.main.Main.CONFIG_PROPERTIES_FILE_VALUE + " found.");
configProps = new HashMap<>();
}
//If OrbisGIS is in a SNAPSHOT version, uses the release and snapshot orb.
//Else only uses the release one.
if(configProps.containsKey(OBR_REPOSITORY_URL) && configProps.containsKey(OBR_REPOSITORY_SNAPSHOT_URL)) {
if (version.getQualifier().equals("SNAPSHOT")) {
configProps.remove(OBR_REPOSITORY_URL);
configProps.put(OBR_REPOSITORY_URL, configProps.get(OBR_REPOSITORY_SNAPSHOT_URL));
}
configProps.remove(OBR_REPOSITORY_SNAPSHOT_URL);
}
// Copy framework properties from the system properties.
org.apache.felix.main.Main.copySystemProperties(configProps);
// If there is a passed in bundle auto-deploy directory, then
// that overwrites anything in the config file.
if (bundleDir != null)
{
configProps.put(AutoProcessor.AUTO_DEPLOY_DIR_PROPERY, bundleDir);
}
// If there is a passed in bundle cache directory, then
// that overwrites anything in the config file.
if (cacheDir != null)
{
configProps.put(Constants.FRAMEWORK_STORAGE, cacheDir);
}
// If enabled, register a shutdown hook to make sure the framework is
// cleanly shutdown when the VM exits.
String enableHook = configProps.get(org.apache.felix.main.Main.SHUTDOWN_HOOK_PROP);
if ((enableHook == null) || !enableHook.equalsIgnoreCase("false"))
{
Runtime.getRuntime().addShutdownHook(new Thread("Felix Shutdown Hook") {
public void run()
{
try
{
if (m_fwk != null)
{
m_fwk.stop();
m_fwk.waitForStop(0);
}
}
catch (Exception ex)
{
System.err.println("Error stopping framework: " + ex);
}
}
});
}
try
{
// Create an instance of the framework.
PluginHost pluginHost = new PluginHost(new File(configProps.get(Constants.FRAMEWORK_STORAGE)), LOGGER);
pluginHost.init(configProps);
m_fwk = pluginHost.getFramework();
// Use the system bundle context to process the auto-deploy
// and auto-install/auto-start properties.
AutoProcessor.process(configProps, m_fwk.getBundleContext());
FrameworkEvent event;
do
{
// Start the framework.
m_fwk.start();
// Wait for framework to stop to exit the VM.
event = m_fwk.waitForStop(0);
}
// If the framework was updated, then restart it.
while (event.getType() == FrameworkEvent.STOPPED_UPDATE);
// Otherwise, exit.
System.exit(0);
} catch (Exception ex) {
LOGGER.log(Logger.LOG_ERROR, "Could not create framework: ", ex);
ex.printStackTrace();
System.exit(0);
}
}
/**
* Utility method to check if the java machine is supported.
*
* @param minJavaVersion
* @return
*/
private static boolean isVersion(char minJavaVersion) {
String version = System.getProperty("java.version");
char minor = version.charAt(2);
if (minor >= minJavaVersion) {
return true;
}
return false;
}
}