/*
* sulky-modules - several general-purpose modules.
* Copyright (C) 2007-2016 Joern Huxhorn
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*
* Copyright 2007-2016 Joern Huxhorn
*
* 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 de.huxhorn.sulky.version;
import java.awt.EventQueue;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Properties;
import javax.swing.JOptionPane;
public class Main
{
public static final String PROPERTIES_NAME_PROPERTY = "main.properties.name";
public static final String PROPERTIES_NAME = "/main.properties";
public static final String MAIN_CLASS_KEY = "main.class";
public static final String REQUIRED_JAVA_VERSION_KEY = "java.version";
public static final String IGNORE_PRE_RELEASE_IDENTIFIER = "ignore.pre.release.identifier";
public static final String SYSTEM_EXIT_KEY = "system.exit";
public static final String SHOW_ERROR_DIALOG_KEY = "show.error.dialog";
public static final String SHOW_WARNING_DIALOG_KEY = "show.warning.dialog";
public static final String UNKNOWN_VERSION_FAIL_KEY = "unknown.version.fail";
public static final int NO_ERROR_STATUS_CODE = 0;
public static final int MAIN_CAUSED_EXCEPTION_STATUS_CODE = 1;
public static final int MISSING_PROPERTIES_STATUS_CODE = 2;
public static final int LOADING_PROPERTIES_FAILED_STATUS_CODE = 3;
public static final int MALFORMED_PROPERTIES_STATUS_CODE = 4;
public static final int MISSING_REQUIRED_JAVA_VERSION_STATUS_CODE = 5;
public static final int INVALID_REQUIRED_JAVA_VERSION_STATUS_CODE = 6;
public static final int MISSING_MAIN_CLASS_STATUS_CODE = 7;
public static final int FAILED_TO_RESOLVE_CLASS_STATUS_CODE = 8;
public static final int FAILED_TO_RESOLVE_METHOD_STATUS_CODE = 9;
public static final int ILLEGAL_ACCESS_STATUS_CODE = 10;
public static final int VERSION_MISMATCH_STATUS_CODE = 42;
public static final int UNKNOWN_VERSION_STATUS_CODE = 17;
private static boolean executingSystemExit;
private static int statusCode;
private static boolean showingErrorDialog;
private static boolean showingWarningDialog;
public static void main(String[] args)
{
statusCode = NO_ERROR_STATUS_CODE;
String propertiesName = System.getProperty(PROPERTIES_NAME_PROPERTY, PROPERTIES_NAME);
InputStream input = Main.class.getResourceAsStream(propertiesName);
if(input == null)
{
System.err.println("Failed to resolve properties from "+propertiesName+"!");
exit(MISSING_PROPERTIES_STATUS_CODE);
return;
}
Properties properties = new Properties();
try
{
properties.load(input);
}
catch (IOException e)
{
System.err.println("Failed to load properties from "+propertiesName+"!");
e.printStackTrace();
exit(LOADING_PROPERTIES_FAILED_STATUS_CODE);
return;
}
catch (IllegalArgumentException e)
{
System.err.println("Failed to load properties from "+propertiesName+"!");
e.printStackTrace();
exit(MALFORMED_PROPERTIES_STATUS_CODE);
return;
}
boolean failingOnUnknownVersion = Boolean.parseBoolean(properties.getProperty(UNKNOWN_VERSION_FAIL_KEY, "false"));
executingSystemExit = Boolean.parseBoolean(properties.getProperty(SYSTEM_EXIT_KEY, "true"));
showingErrorDialog = Boolean.parseBoolean(properties.getProperty(SHOW_ERROR_DIALOG_KEY, "true"));
showingWarningDialog = Boolean.parseBoolean(properties.getProperty(SHOW_WARNING_DIALOG_KEY, "false"));
boolean unknownVersion = false;
if(JavaVersion.getSystemJavaVersion().equals(YeOldeJavaVersion.MIN_VALUE))
{
unknownVersion = true;
if(failingOnUnknownVersion)
{
System.err.println("Failed to resolve system java version!");
exit(UNKNOWN_VERSION_STATUS_CODE);
return;
}
}
String requiredJavaVersionString = properties.getProperty(REQUIRED_JAVA_VERSION_KEY);
if(requiredJavaVersionString == null)
{
System.err.println("Failed to resolve "+REQUIRED_JAVA_VERSION_KEY+" property from "+propertiesName+"!");
exit(MISSING_REQUIRED_JAVA_VERSION_STATUS_CODE);
return;
}
JavaVersion requiredJavaVersion;
try
{
requiredJavaVersion = JavaVersion.parse(requiredJavaVersionString);
}
catch (IllegalArgumentException e)
{
e.printStackTrace();
exit(INVALID_REQUIRED_JAVA_VERSION_STATUS_CODE);
return;
}
boolean ignorePreReleaseIdentifier = Boolean.parseBoolean(properties.getProperty(IGNORE_PRE_RELEASE_IDENTIFIER, "false"));
if(!unknownVersion && !JavaVersion.isAtLeast(requiredJavaVersion, ignorePreReleaseIdentifier))
{
if(!showVersionWarning(requiredJavaVersion))
{
exit(VERSION_MISMATCH_STATUS_CODE);
return;
}
}
String mainClassName = properties.getProperty(MAIN_CLASS_KEY);
if(mainClassName == null)
{
System.err.println("Failed to resolve "+MAIN_CLASS_KEY+" property from "+propertiesName+"!");
exit(MISSING_MAIN_CLASS_STATUS_CODE);
return;
}
Class<?> mainClass;
try
{
mainClass = Class.forName(mainClassName);
}
catch (Throwable e)
{
System.err.println("Failed to resolve class "+mainClassName+"!");
e.printStackTrace();
exit(FAILED_TO_RESOLVE_CLASS_STATUS_CODE);
return;
}
Method mainMethod;
try
{
mainMethod = mainClass.getMethod("main", String[].class);
}
catch (Throwable e)
{
System.err.println("Failed to resolve main method in class "+mainClass+"!");
e.printStackTrace();
exit(FAILED_TO_RESOLVE_METHOD_STATUS_CODE);
return;
}
try
{
mainMethod.invoke(null, (Object)args);
}
catch (IllegalAccessException e)
{
System.err.println("Failed to invoke main method of class "+mainClass+"!");
e.printStackTrace();
exit(ILLEGAL_ACCESS_STATUS_CODE);
}
catch (InvocationTargetException e)
{
Throwable cause = e.getCause();
if(cause != null)
{
cause.printStackTrace();
}
else
{
e.printStackTrace();
}
exit(MAIN_CAUSED_EXCEPTION_STATUS_CODE);
}
}
public static int getStatusCode()
{
return statusCode;
}
private static void exit(int status)
{
if(executingSystemExit)
{
System.exit(status);
}
statusCode = status;
}
private static boolean showVersionWarning(JavaVersion requiredJavaVersion)
{
final String msg="This application requires Java "
+ requiredJavaVersion.toVersionString()
+ " but JVM is "
+ JavaVersion.getSystemJavaVersion().toVersionString()
+ "!";
VersionRunnable versionRunnable=null;
if(showingErrorDialog)
{
versionRunnable = new ShowVersionErrorDialog(msg);
}
else if(showingWarningDialog)
{
versionRunnable = new ShowVersionWarningDialog(msg);
}
if(versionRunnable != null)
{
try
{
EventQueue.invokeAndWait(versionRunnable);
return versionRunnable.isStartingAnyway();
}
catch (Throwable t)
{
// ignore
}
}
System.err.println(msg);
return false;
}
private interface VersionRunnable
extends Runnable
{
String TITLE = "Java Version Mismatch";
boolean isStartingAnyway();
}
private static class ShowVersionErrorDialog
implements VersionRunnable
{
private final String msg;
ShowVersionErrorDialog(String msg)
{
this.msg = msg;
}
public void run()
{
final String message = msg + "\n\nPlease upgrade your Java version.";
JOptionPane.showOptionDialog(null, message, TITLE, JOptionPane.DEFAULT_OPTION, JOptionPane.ERROR_MESSAGE, null, null, null);
}
public boolean isStartingAnyway()
{
return false;
}
}
private static class ShowVersionWarningDialog
implements VersionRunnable
{
private static final String YES_OPTION = "Yes";
private static final String NO_OPTION = "No";
private static final Object[] OPTIONS =new Object[]{YES_OPTION, NO_OPTION};
private static final String DEFAULT_OPTION = NO_OPTION;
private final String msg;
private boolean startingAnyway;
ShowVersionWarningDialog(String msg)
{
this.msg = msg;
}
public void run()
{
final String message = msg + "\n\nStart anyway?";
int result = JOptionPane.showOptionDialog(null, message, TITLE, JOptionPane.YES_NO_OPTION,
JOptionPane.WARNING_MESSAGE, null, OPTIONS, DEFAULT_OPTION);
startingAnyway = result == JOptionPane.YES_OPTION;
}
public boolean isStartingAnyway()
{
return startingAnyway;
}
}
}