/*
* (c) Copyright 2010-2011 AgileBirds
*
* This file is part of OpenFlexo.
*
* OpenFlexo 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.
*
* OpenFlexo 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 OpenFlexo. If not, see <http://www.gnu.org/licenses/>.
*
*/
package org.openflexo;
import java.awt.Frame;
import java.awt.Window;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.lang.management.ManagementFactory;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.Authenticator;
import java.net.PasswordAuthentication;
import java.net.Proxy;
import java.net.ProxySelector;
import java.net.SocketAddress;
import java.net.URI;
import java.net.URL;
import java.nio.channels.FileLock;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.LogManager;
import java.util.logging.Logger;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import org.openflexo.application.FlexoApplication;
import org.openflexo.components.AskParametersDialog;
import org.openflexo.components.SplashWindow;
import org.openflexo.components.WelcomeDialog;
import org.openflexo.foundation.FlexoMainLocalizer;
import org.openflexo.foundation.param.TextFieldParameter;
import org.openflexo.foundation.utils.OperationCancelledException;
import org.openflexo.foundation.utils.ProjectInitializerException;
import org.openflexo.foundation.utils.ProjectLoadingCancelledException;
import org.openflexo.localization.FlexoLocalization;
import org.openflexo.logging.FlexoLoggingFormatter;
import org.openflexo.logging.FlexoLoggingManager;
import org.openflexo.logging.FlexoLoggingManager.LoggingManagerDelegate;
import org.openflexo.module.Module;
import org.openflexo.module.ModuleLoadingException;
import org.openflexo.module.Modules;
import org.openflexo.module.UserType;
import org.openflexo.prefs.FlexoPreferences;
import org.openflexo.ssl.DenaliSecurityProvider;
import org.openflexo.toolbox.FileResource;
import org.openflexo.toolbox.ResourceLocator;
import org.openflexo.toolbox.ToolBox;
import org.openflexo.utils.CancelException;
import org.openflexo.utils.TooManyFailedAttemptException;
import org.openflexo.view.FlexoFrame;
import org.openflexo.view.controller.FlexoController;
import sun.misc.Signal;
import sun.misc.SignalHandler;
/**
* Main class of the Flexo Application Suite
*
*
* @author sguerin
*/
public class Flexo {
private static final Logger logger = Logger.getLogger(Flexo.class.getPackage().getName());
public static boolean isDev = false;
private static File outLogFile;
private static File errLogFile;
private static String fileNameToOpen;
private static boolean demoMode = false;
public static boolean isDemoMode() {
return demoMode;
}
private static String getResourcePath() {
if (ToolBox.getPLATFORM() == ToolBox.MACOS) {
try {
Class fileManager = Class.forName("com.apple.eio.FileManager");
if (fileManager == null) {
return null;
}
Method m = fileManager.getDeclaredMethod("getResource", new Class[] { String.class, String.class });
String s = (String) m.invoke(null, "English.dict", "Localized");
s = s.substring(System.getProperty("user.dir").length() + 1);
s = s.substring(0, s.length() - "Localized/English.dict".length());
ResourceLocator.resetFlexoResourceLocation(new File(s));
return s;
} catch (SecurityException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
// e.printStackTrace();
}
} else {
ResourceLocator.resetFlexoResourceLocation(new File(System.getProperty("user.dir")));
return System.getProperty("user.dir");
}
return null;
}
@SuppressWarnings("restriction")
private static void registerShutdownHook() {
try {
Class.forName("sun.misc.Signal");
Class.forName("sun.misc.SignalHandler");
Signal.handle(new Signal("TERM"), new SignalHandler() {
@Override
public void handle(Signal arg0) {
FlexoFrame activeFrame = FlexoFrame.getActiveFrame(false);
if (activeFrame != null) {
try {
activeFrame.getController().getModuleLoader()
.quit(activeFrame.getController().getProjectLoader().someProjectsAreModified());
} catch (OperationCancelledException e) {
e.printStackTrace();
}
}
}
});
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
/**
* Launch method to start Flexo in multimodule mode. Program args are in dev: -userType MAINTAINER Dev -nosplash otherwise see windows
* launchers or build.xml for package args VM args: -Xmx512M (for big projects push it to 1024) For MacOS also add: -Xdock:name=Flexo
* -Dapple.laf.useScreenMenuBar=true
*
* @param args
*/
public static void main(final String[] args) {
String userTypeName = null;
boolean noSplash = false;
if (args.length > 0) {
// ATTENTION: Argument cannot start with "-D", nor start with "-X", nor start with "-agentlib" since they are reserved keywords
// for JVM
for (int i = 0; i < args.length; i++) {
if (args[i].equals("-userType")) {
userTypeName = args[i + 1];
}
if (args[i].equals("-nosplash")) {
noSplash = true;
} else if (args[i].equalsIgnoreCase("DEV")) {
isDev = true;
} else if (args[i].equalsIgnoreCase("demo")) {
demoMode = true;
}
}
}
// 1. Very important to initiate first the ResourceLocator. Nothing else. See also issue 463.
if (ToolBox.getPLATFORM() != ToolBox.MACOS || !isDev) {
getResourcePath();
}
ResourceLocator.printDirectoriesSearchOrder(System.err);
remapStandardOuputs(isDev);
UserType userTypeNamed = UserType.getUserTypeNamed(userTypeName);
UserType.setCurrentUserType(userTypeNamed);
SplashWindow splashWindow = null;
if (!noSplash) {
splashWindow = new SplashWindow(FlexoFrame.getActiveFrame(), UserType.getCurrentUserType());
}
final SplashWindow splashWindow2 = splashWindow;
FlexoProperties.load();
initializeLoggingManager();
// First init localization with default location
FlexoLocalization.initWith(new FlexoMainLocalizer());
final ApplicationContext applicationContext = new InteractiveApplicationContext();
FlexoApplication.installEventQueue();
// Before starting the UI, we need to initialize localization
FlexoApplication.initialize(applicationContext.getModuleLoader());
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
initFlexo(applicationContext, splashWindow2);
}
});
Modules.getInstance();
try {
DenaliSecurityProvider.insertSecurityProvider();
} catch (Exception e) {
if (logger.isLoggable(Level.WARNING)) {
logger.log(Level.WARNING, "Could not insert security provider", e);
}
}
initProxyManagement();
if (logger.isLoggable(Level.INFO)) {
logger.info("Starting on " + ToolBox.getPLATFORM() + "... JVM version is " + System.getProperty("java.version"));
}
if (logger.isLoggable(Level.INFO)) {
logger.info("Working directory is " + new File(".").getAbsolutePath());
}
if (logger.isLoggable(Level.INFO)) {
logger.info("Heap memory is about: " + ManagementFactory.getMemoryMXBean().getHeapMemoryUsage().getMax() / (1024 * 1024) + "Mb");
}
applicationContext.getModuleLoader().setAllowsDocSubmission(isDev || FlexoProperties.instance().getAllowsDocSubmission());
if (logger.isLoggable(Level.INFO)) {
logger.info("Launching FLEXO Application Suite version " + FlexoCst.BUSINESS_APPLICATION_VERSION_NAME + "...");
}
if (!isDev) {
registerShutdownHook();
}
}
protected static void initFlexo(ApplicationContext applicationContext, SplashWindow splashWindow) {
if (ToolBox.getPLATFORM() == ToolBox.MACOS) {
System.setProperty("apple.laf.useScreenMenuBar", "true");
}
initUILAF(AdvancedPrefs.getLookAndFeelString());
if (isDev) {
FlexoLoggingFormatter.logDate = false;
}
if (fileNameToOpen == null) {
showWelcomDialog(applicationContext, splashWindow);
} else {
try {
File projectDirectory = new File(fileNameToOpen);
if (splashWindow != null) {
splashWindow.setVisible(false);
splashWindow.dispose();
splashWindow = null;
}
Module module = Modules.getInstance().getModule(GeneralPreferences.getFavoriteModuleName());
if (module == null) {
if (Modules.getInstance().getAvailableModules().size() > 0) {
module = Modules.getInstance().getAvailableModules().get(0);
}
}
applicationContext.getModuleLoader().switchToModule(module);
applicationContext.getProjectLoader().loadProject(projectDirectory);
} catch (ProjectLoadingCancelledException e) {
// project need a conversion, but user cancelled the conversion.
showWelcomDialog(applicationContext, null);
} catch (ProjectInitializerException e) {
e.printStackTrace();
FlexoController.notify(FlexoLocalization.localizedForKey("could_not_open_project_located_at")
+ e.getProjectDirectory().getAbsolutePath());
showWelcomDialog(applicationContext, null);
} catch (ModuleLoadingException e) {
e.printStackTrace();
FlexoController.notify(FlexoLocalization.localizedForKey("could_not_load_module") + " " + e.getModule());
showWelcomDialog(applicationContext, null);
}
}
}
public static void showWelcomDialog(final ApplicationContext applicationContext, final SplashWindow splashWindow) {
WelcomeDialog welcomeDialog = new WelcomeDialog(applicationContext);
welcomeDialog.pack();
welcomeDialog.center();
if (splashWindow != null) {
splashWindow.setVisible(false);
splashWindow.dispose();
}
welcomeDialog.setVisible(true);
welcomeDialog.toFront();
}
static void initUILAF(String value) {
if (UIManager.getLookAndFeel().getClass().getName().equals(value)) {
return;
}
try {
UIManager.setLookAndFeel(value);
for (Frame frame : Frame.getFrames()) {
for (Window window : frame.getOwnedWindows()) {
SwingUtilities.updateComponentTreeUI(window);
}
SwingUtilities.updateComponentTreeUI(frame);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (UnsupportedLookAndFeelException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private static void initProxyManagement() {
AdvancedPrefs.applyProxySettings();
final ProxySelector defaultSelector = ProxySelector.getDefault();
ProxySelector.setDefault(new ProxySelector() {
@Override
public void connectFailed(URI uri, SocketAddress sa, IOException ioe) {
System.err.println("Failed " + uri + " sa " + sa);
defaultSelector.connectFailed(uri, sa, ioe);
}
@Override
public List<Proxy> select(URI uri) {
List<Proxy> proxies = new ArrayList<Proxy>(defaultSelector.select(uri));
if (!proxies.contains(Proxy.NO_PROXY)) {
proxies.add(Proxy.NO_PROXY);
}
return proxies;
}
});
Authenticator.setDefault(new Authenticator() {
private URL previous;
int count = 0;
@Override
protected PasswordAuthentication getPasswordAuthentication() {
try {
if (previous == getRequestingURL()) {
count++;
}
if (previous != getRequestingURL() && AdvancedPrefs.getProxyLogin() != null && AdvancedPrefs.getProxyPassword() != null) {
count = 0;
} else {
if (count < 3) {
TextFieldParameter[] params = new TextFieldParameter[2];
params[0] = new TextFieldParameter("login", "login", AdvancedPrefs.getProxyLogin());
params[1] = new TextFieldParameter("password", "password", AdvancedPrefs.getProxyPassword());
params[1].setIsPassword(true);
AskParametersDialog dialog = AskParametersDialog.createAskParametersDialog(null,
FlexoLocalization.localizedForKey("enter_proxy_login_password"),
FlexoLocalization.localizedForKey("enter_proxy_login_password"), params);
if (dialog.getStatus() == AskParametersDialog.VALIDATE) {
AdvancedPrefs.setProxyLogin(params[0].getValue());
AdvancedPrefs.setProxyPassword(params[1].getValue());
AdvancedPrefs.save();
} else {
throw new CancelException();
}
} else {
if (logger.isLoggable(Level.WARNING)) {
logger.warning("Too many attempts (3) failed. Throwing exception to prevent locking.");
}
throw new TooManyFailedAttemptException();
}
}
return new PasswordAuthentication(AdvancedPrefs.getProxyLogin(), AdvancedPrefs.getProxyPassword().toCharArray());
} finally {
previous = getRequestingURL();
}
}
});
}
/**
*
*/
private static void remapStandardOuputs(boolean outputToConsole) {
try {
// First let's see if we will be able to write into the log directory
File outputDir = FlexoPreferences.getLogDirectory();
if (!outputDir.exists()) {
outputDir.mkdirs();
}
if (outputDir.isFile()) {
if (logger.isLoggable(Level.SEVERE)) {
logger.severe("Can not write to " + outputDir.getAbsolutePath() + " because it is already a file.");
}
}
if (!outputDir.canWrite()) {
if (logger.isLoggable(Level.SEVERE)) {
logger.severe("Can not write to " + outputDir.getAbsolutePath() + " because access is denied by the file system");
}
}
String outString = outputDir.getAbsolutePath() + "/Flexo.out";
String errString = outputDir.getAbsolutePath() + "/Flexo.err";
outLogFile = getOutputFile(outString);
if (outLogFile != null) {
PrintStream ps1 = new PrintStream(outLogFile);
if (outputToConsole) {
System.setOut(new PrintStream(new DoublePrintStream(ps1, System.out)));
} else {
System.setOut(ps1);
}
}
errLogFile = getOutputFile(errString);
if (errLogFile != null) {
PrintStream ps1 = new PrintStream(errLogFile);
if (outputToConsole) {
System.setErr(new PrintStream(new DoublePrintStream(ps1, System.err)));
} else {
System.setErr(ps1);
}
}
} catch (Exception e) {
if (logger.isLoggable(Level.SEVERE)) {
logger.log(Level.SEVERE, e.getMessage(), e);
}
}
}
public static File getErrLogFile() {
return errLogFile;
}
public static File getOutLogFile() {
return outLogFile;
}
private static class DoublePrintStream extends OutputStream {
private final PrintStream ps1;
private final PrintStream ps2;
public DoublePrintStream(PrintStream ps1, PrintStream ps2) {
this.ps1 = ps1;
this.ps2 = ps2;
}
@Override
public void write(int b) throws IOException {
ps1.print((char) b);
ps2.print((char) b);
}
}
private static File getOutputFile(String outString) throws IOException {
int attempt = 0;
File out = null;
while (out == null && attempt < 100) {
// Get a file channel for the file
File file = new File(outString + (attempt == 0 ? "" : "." + attempt) + ".log");
File lock = new File(outString + (attempt == 0 ? "" : "." + attempt) + ".log.lck");
if (!file.exists()) {
try {
boolean done = file.createNewFile();
if (done) {
out = file;
}
} catch (RuntimeException e1) {
e1.printStackTrace();
}
}
if (!file.canWrite()) {
out = null;
attempt++;
continue;
}
if (lock.exists()) {
lock.delete();
}
if (lock.exists()) {
out = null;
attempt++;
continue;
} else {
try {
lock.createNewFile();
boolean lockAcquired = false;
FileOutputStream fos = new FileOutputStream(lock);
try {
FileLock fileLock = fos.getChannel().lock();
lockAcquired = true;
} catch (IOException e) {
} finally {
if (!lockAcquired) {
fos.close();
}
}
lock.deleteOnExit();
} catch (RuntimeException e) {
e.printStackTrace();
}
}
out = file;
attempt++;
}
return out;
}
public static FlexoLoggingManager initializeLoggingManager() {
try {
FlexoProperties properties = FlexoProperties.instance();
logger.info("Default logging config file " + System.getProperty("java.util.logging.config.file"));
return FlexoLoggingManager.initialize(
properties.getMaxLogCount(),
properties.getIsLoggingTrace(),
properties.getCustomLoggingFile() != null ? properties.getCustomLoggingFile() : new File(System
.getProperty("java.util.logging.config.file")), properties.getDefaultLoggingLevel(),
new LoggingManagerDelegate() {
@Override
public void setMaxLogCount(Integer maxLogCount) {
FlexoProperties.instance().setMaxLogCount(maxLogCount);
}
@Override
public void setKeepLogTrace(boolean logTrace) {
FlexoProperties.instance().setIsLoggingTrace(logTrace);
}
@Override
public void setDefaultLoggingLevel(Level lev) {
String fileName = "SEVERE";
if (lev == Level.SEVERE) {
fileName = "SEVERE";
}
if (lev == Level.WARNING) {
fileName = "WARNING";
}
if (lev == Level.INFO) {
fileName = "INFO";
}
if (lev == Level.FINE) {
fileName = "FINE";
}
if (lev == Level.FINER) {
fileName = "FINER";
}
if (lev == Level.FINEST) {
fileName = "FINEST";
}
reloadLoggingFile(new FileResource("Config/logging_" + fileName + ".properties").getAbsolutePath());
FlexoProperties.instance().setLoggingFileName(null);
}
@Override
public void setConfigurationFileName(String configurationFile) {
reloadLoggingFile(configurationFile);
FlexoProperties.instance().setLoggingFileName(configurationFile);
}
});
} catch (SecurityException e) {
logger.severe("cannot read logging configuration file : " + System.getProperty("java.util.logging.config.file")
+ "\nIt seems the file has read access protection.");
e.printStackTrace();
return null;
} catch (IOException e) {
logger.severe("cannot read logging configuration file : " + System.getProperty("java.util.logging.config.file"));
e.printStackTrace();
return null;
}
}
private static boolean reloadLoggingFile(String filePath) {
logger.info("reloadLoggingFile with " + filePath);
System.setProperty("java.util.logging.config.file", filePath);
try {
LogManager.getLogManager().readConfiguration();
} catch (SecurityException e) {
logger.warning("The specified logging configuration file can't be read (not enough privileges).");
e.printStackTrace();
return false;
} catch (IOException e) {
logger.warning("The specified logging configuration file cannot be read.");
e.printStackTrace();
return false;
}
return true;
}
public static void setFileNameToOpen(String filename) {
Flexo.fileNameToOpen = filename;
}
}