/**
* See the NOTICE file distributed with this work for additional information
* regarding copyright ownership.
*
* This 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 2.1 of the License, or (at your option)
* any later version.
*
* This software 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 software; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA, or see the FSF
* site: http://www.fsf.org.
*/
package org.ut.biolab.medsavant;
import com.apple.eawt.AboutHandler;
import com.apple.eawt.AppEvent;
import com.apple.eawt.Application;
import com.apple.eawt.PreferencesHandler;
import com.apple.eawt.QuitHandler;
import com.apple.eawt.QuitResponse;
import org.ut.biolab.medsavant.shared.serverapi.CustomTablesAdapter;
import org.ut.biolab.medsavant.shared.serverapi.OntologyManagerAdapter;
import org.ut.biolab.medsavant.shared.serverapi.NetworkManagerAdapter;
import org.ut.biolab.medsavant.shared.serverapi.SessionManagerAdapter;
import org.ut.biolab.medsavant.shared.serverapi.UserManagerAdapter;
import org.ut.biolab.medsavant.shared.serverapi.CohortManagerAdapter;
import org.ut.biolab.medsavant.shared.serverapi.AnnotationManagerAdapter;
import org.ut.biolab.medsavant.shared.serverapi.VariantManagerAdapter;
import org.ut.biolab.medsavant.shared.serverapi.SetupAdapter;
import org.ut.biolab.medsavant.shared.serverapi.GeneSetManagerAdapter;
import org.ut.biolab.medsavant.shared.serverapi.LogManagerAdapter;
import org.ut.biolab.medsavant.shared.serverapi.MedSavantServerRegistry;
import org.ut.biolab.medsavant.shared.serverapi.SettingsManagerAdapter;
import org.ut.biolab.medsavant.shared.serverapi.ProjectManagerAdapter;
import org.ut.biolab.medsavant.shared.serverapi.NotificationManagerAdapter;
import org.ut.biolab.medsavant.shared.serverapi.DBUtilsAdapter;
import org.ut.biolab.medsavant.shared.serverapi.ReferenceManagerAdapter;
import org.ut.biolab.medsavant.shared.serverapi.PatientManagerAdapter;
import java.rmi.*;
import java.rmi.registry.*;
import java.awt.Insets;
import javax.swing.ToolTipManager;
import javax.swing.UIManager;
import javax.swing.plaf.ColorUIResource;
import com.jidesoft.plaf.LookAndFeelFactory;
import gnu.getopt.Getopt;
import java.awt.Color;
import java.io.File;
import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Proxy;
import java.net.NoRouteToHostException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.net.ssl.SSLHandshakeException;
import javax.rmi.ssl.SslRMIClientSocketFactory;
import javax.swing.JOptionPane;
import javax.swing.UIDefaults;
import javax.swing.UIManager.LookAndFeelInfo;
import javax.swing.plaf.InsetsUIResource;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.ut.biolab.medsavant.client.controller.SettingsController;
import org.ut.biolab.medsavant.client.util.ClientMiscUtils;
import org.ut.biolab.medsavant.client.util.MedSavantExceptionHandler;
import org.ut.biolab.medsavant.client.util.MedSavantWorker;
import org.ut.biolab.medsavant.client.util.ServerModificationInvocationHandler;
import org.ut.biolab.medsavant.shared.util.MiscUtils;
import org.ut.biolab.medsavant.client.view.MedSavantFrame;
import org.ut.biolab.medsavant.client.view.font.FontFactory;
import org.ut.biolab.medsavant.client.view.login.SplashFrame;
import org.ut.biolab.medsavant.client.view.util.DialogUtils;
import org.ut.biolab.medsavant.shared.model.exception.LockException;
import org.ut.biolab.medsavant.shared.model.SessionExpiredException;
import org.ut.biolab.medsavant.shared.serverapi.RegionSetManagerAdapter;
import org.ut.biolab.medsavant.shared.util.DirectorySettings;
import org.ut.biolab.medsavant.shared.util.VersionSettings;
import org.ut.biolab.savant.analytics.savantanalytics.AnalyticsAgent;
public class MedSavantClient implements MedSavantServerRegistry {
private static final Log LOG = LogFactory.getLog(MedSavantClient.class);
public static CustomTablesAdapter CustomTablesManager;
public static AnnotationManagerAdapter AnnotationManagerAdapter;
public static CohortManagerAdapter CohortManager;
public static GeneSetManagerAdapter GeneSetManager;
public static LogManagerAdapter LogManager;
public static NetworkManagerAdapter NetworkManager;
public static OntologyManagerAdapter OntologyManager;
public static PatientManagerAdapter PatientManager;
public static ProjectManagerAdapter ProjectManager;
public static UserManagerAdapter UserManager;
public static SessionManagerAdapter SessionManager;
public static SettingsManagerAdapter SettingsManager;
public static RegionSetManagerAdapter RegionSetManager;
public static ReferenceManagerAdapter ReferenceManager;
public static DBUtilsAdapter DBUtils;
public static SetupAdapter SetupManager;
public static VariantManagerAdapter VariantManager; //proxy
public static NotificationManagerAdapter NotificationManager;
private static boolean initialized = false;
private static String[] restartCommand;
private static boolean restarting = false;
private static final Object managerLock = new Object();
//Proxy the adapters to process annotations and fire events to the cache controller.
private static void initProxies() {
VariantManager = (VariantManagerAdapter) Proxy.newProxyInstance(
VariantManager.getClass().getClassLoader(),
new Class[]{VariantManagerAdapter.class},
new ServerModificationInvocationHandler<VariantManagerAdapter>(VariantManager));
CohortManager = (CohortManagerAdapter) Proxy.newProxyInstance(
CohortManager.getClass().getClassLoader(),
new Class[]{CohortManagerAdapter.class},
new ServerModificationInvocationHandler<CohortManagerAdapter>(CohortManager));
PatientManager = (PatientManagerAdapter) Proxy.newProxyInstance(
PatientManager.getClass().getClassLoader(),
new Class[]{PatientManagerAdapter.class},
new ServerModificationInvocationHandler<PatientManagerAdapter>(PatientManager));
RegionSetManager = (RegionSetManagerAdapter) Proxy.newProxyInstance(
RegionSetManager.getClass().getClassLoader(),
new Class[]{RegionSetManagerAdapter.class},
new ServerModificationInvocationHandler<RegionSetManagerAdapter>(RegionSetManager));
OntologyManager = (OntologyManagerAdapter) Proxy.newProxyInstance(
OntologyManager.getClass().getClassLoader(),
new Class[]{OntologyManagerAdapter.class},
new ServerModificationInvocationHandler<OntologyManagerAdapter>(OntologyManager));
}
/**
* Restarts MedSavant (This function has NOT been tested with Web Start)
*/
public static void restart() {
if (!restarting) {
restarting = true;
try {
/* if (msg != null) {
DialogUtils.displayMessage("MedSavant needs to restart.", msg);
}*/
SettingsController.getInstance().setBoolean("BootFromLogout", true);
System.out.println("Restarting with " + restartCommand[0]);
Runtime.getRuntime().exec(restartCommand);
System.exit(0);
} catch (IOException e) { //thrown by exec
DialogUtils.displayError("Error restarting MedSavant. Please restart MedSavant manually.");
LOG.error(e);
} catch (Exception e) {
LOG.error(e);
}
}
}
public static void setRestartCommand(String[] args) {
List<String> restartCommandList = new ArrayList<String>();
String launcher = System.getProperty("java.home") + File.separator + "bin" + File.separator + "java";
restartCommandList.add(launcher);
for (String jvmArg : ManagementFactory.getRuntimeMXBean().getInputArguments()) {
restartCommandList.add(jvmArg);
}
restartCommandList.add("-cp");
restartCommandList.add(ManagementFactory.getRuntimeMXBean().getClassPath());
restartCommandList.add(MedSavantClient.class.getName());
for (String arg : args) {
restartCommandList.add(arg);
}
restartCommand = restartCommandList.toArray(new String[restartCommandList.size()]);
}
/**
* Ensure the user is running Java 1.7+
*/
static public void checkJavaVersion() {
System.out.println(System.getProperty("java.specification.version"));
String javaVersion = System.getProperty("java.specification.version");
if (javaVersion.equals("1.7") || javaVersion.equals("1.8")) {
return;
}
DialogUtils.displayError("Incompatible Java Version", "Please upgrade your version of Java to 1.7 or greater.");
System.exit(1);
}
static public void main(String args[]) {
//checkJavaVersion();
new MedSavantWorker<Void>("Analytics Start"){
@Override
protected Void doInBackground() throws Exception {
try{
AnalyticsAgent.onStartSession("MedSavant", VersionSettings.getVersionString());
}catch(Exception ex){
LOG.error("Couldn't connect to analytics agent.");
}
return null;
}
@Override
protected void showSuccess(Void result) {
}
}.execute();
// Avoids "Comparison method violates its general contract" bug.
// http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=7075600
System.setProperty("java.util.Arrays.useLegacyMergeSort", "true");
setRestartCommand(args);
setExceptionHandler();
verifyJIDE();
setLAF();
// initialize settings
SettingsController.getInstance();
Getopt g = new Getopt("MedSavant", args, "h:p:d:u:w:");
int c;
while ((c = g.getopt()) != -1) {
switch (c) {
case 'h':
String host = g.getOptarg();
SettingsController.getInstance().setServerAddress(host);
break;
case 'p':
int port = Integer.parseInt(g.getOptarg());
SettingsController.getInstance().setServerPort(port + "");
break;
case 'd':
String dbname = g.getOptarg();
SettingsController.getInstance().setDBName(dbname);
break;
case 'u':
String username = g.getOptarg();
SettingsController.getInstance().setUsername(username);
break;
case 'w':
String password = g.getOptarg();
SettingsController.getInstance().setPassword(password);
break;
case '?':
break; // getopt() already printed an error
default:
System.out.print("getopt() returned " + c + "\n");
}
}
//Setup temporary directories
String username = System.getProperty("user.name");
DirectorySettings.setTmpDirectory((new File(System.getProperty("java.io.tmpdir"), "msavant_" + username)).getAbsolutePath());
DirectorySettings.setMedSavantDirectory((new File(System.getProperty("user.home"), MiscUtils.WINDOWS ? "medsavant" : ".medsavant")).getAbsolutePath());
LOG.info("MedSavant booted");
System.out.println("READY.");
SplashFrame loginFrame = new SplashFrame();
loginFrame.setVisible(true);
}
public static void initializeRegistry(String serverAddress, String serverPort) throws RemoteException, NotBoundException, NoRouteToHostException, ConnectIOException {
if (initialized) {
return;
}
int port = (new Integer(serverPort)).intValue();
Registry registry;
LOG.debug("Connecting to MedSavantServerEngine @ " + serverAddress + ":" + serverPort + "...");
try {
registry = LocateRegistry.getRegistry(serverAddress, port, new SslRMIClientSocketFactory());
LOG.debug("Retrieving adapters...");
setAdaptersFromRegistry(registry);
LOG.info("Connected with SSL/TLS Encryption");
} catch (ConnectIOException ex) {
if (ex.getCause() instanceof SSLHandshakeException) {
registry = LocateRegistry.getRegistry(serverAddress, port);
LOG.debug("Retrieving adapters...");
setAdaptersFromRegistry(registry);
LOG.info("Connected without SSL/TLS encryption");
}
}
LOG.debug("Done");
}
private static void setAdaptersFromRegistry(Registry registry) throws RemoteException, NotBoundException, NoRouteToHostException, ConnectIOException {
CustomTablesAdapter CustomTablesManager;
AnnotationManagerAdapter AnnotationManagerAdapter;
CohortManagerAdapter CohortManager;
GeneSetManagerAdapter GeneSetManager;
LogManagerAdapter LogManager;
NetworkManagerAdapter NetworkManager;
OntologyManagerAdapter OntologyManager;
PatientManagerAdapter PatientManager;
ProjectManagerAdapter ProjectManager;
UserManagerAdapter UserManager;
SessionManagerAdapter SessionManager;
SettingsManagerAdapter SettingsManager;
RegionSetManagerAdapter RegionSetManager;
ReferenceManagerAdapter ReferenceManager;
DBUtilsAdapter DBUtils;
SetupAdapter SetupManager;
VariantManagerAdapter VariantManager;
NotificationManagerAdapter NotificationManager;
// try {
AnnotationManagerAdapter = (AnnotationManagerAdapter) registry.lookup(ANNOTATION_MANAGER);
CohortManager = (CohortManagerAdapter) (registry.lookup(COHORT_MANAGER));
LogManager = (LogManagerAdapter) registry.lookup(LOG_MANAGER);
NetworkManager = (NetworkManagerAdapter) registry.lookup(NETWORK_MANAGER);
OntologyManager = (OntologyManagerAdapter) registry.lookup(ONTOLOGY_MANAGER);
PatientManager = (PatientManagerAdapter) registry.lookup(PATIENT_MANAGER);
ProjectManager = (ProjectManagerAdapter) registry.lookup(PROJECT_MANAGER);
GeneSetManager = (GeneSetManagerAdapter) registry.lookup(GENE_SET_MANAGER);
ReferenceManager = (ReferenceManagerAdapter) registry.lookup(REFERENCE_MANAGER);
RegionSetManager = (RegionSetManagerAdapter) registry.lookup(REGION_SET_MANAGER);
SessionManager = (SessionManagerAdapter) registry.lookup(SESSION_MANAGER);
SettingsManager = (SettingsManagerAdapter) registry.lookup(SETTINGS_MANAGER);
UserManager = (UserManagerAdapter) registry.lookup(USER_MANAGER);
VariantManager = (VariantManagerAdapter) registry.lookup(VARIANT_MANAGER);
DBUtils = (DBUtilsAdapter) registry.lookup(DB_UTIL_MANAGER);
SetupManager = (SetupAdapter) registry.lookup(SETUP_MANAGER);
CustomTablesManager = (CustomTablesAdapter) registry.lookup(CUSTOM_TABLES_MANAGER);
NotificationManager = (NotificationManagerAdapter) registry.lookup(NOTIFICATION_MANAGER);
if (Thread.interrupted()) {
return;
}
synchronized (managerLock) {
MedSavantClient.CustomTablesManager = CustomTablesManager;
MedSavantClient.AnnotationManagerAdapter = AnnotationManagerAdapter;
MedSavantClient.CohortManager = CohortManager;
MedSavantClient.GeneSetManager = GeneSetManager;
MedSavantClient.LogManager = LogManager;
MedSavantClient.NetworkManager = NetworkManager;
MedSavantClient.OntologyManager = OntologyManager;
MedSavantClient.PatientManager = PatientManager;
MedSavantClient.ProjectManager = ProjectManager;
MedSavantClient.UserManager = UserManager;
MedSavantClient.SessionManager = SessionManager;
MedSavantClient.SettingsManager = SettingsManager;
MedSavantClient.RegionSetManager = RegionSetManager;
MedSavantClient.ReferenceManager = ReferenceManager;
MedSavantClient.DBUtils = DBUtils;
MedSavantClient.SetupManager = SetupManager;
MedSavantClient.VariantManager = VariantManager;
MedSavantClient.NotificationManager = NotificationManager;
initProxies();
}
}
private static void setLAF() {
try {
if (ClientMiscUtils.MAC) {
customizeForMac();
}
// UIManager.setLookAndFeel("javax.swing.plaf.metal.MetalLookAndFeel"); //Metal works with sliders.
//UIManager.setLookAndFeel("com.sun.java.swing.plaf.gtk.GTKLookAndFeel"); //GTK doesn't work with sliders.
//UIManager.setLookAndFeel("javax.swing.plaf.nimbus.NimbusLookAndFeel"); //Nimbus doesn't work with sliders.
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
for (LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) {
LOG.debug("Installed LAF: " + info.getName() + " class: " + info.getClassName());
}
LOG.debug("System LAF is: " + UIManager.getSystemLookAndFeelClassName());
LOG.debug("Cross platform LAF is: " + UIManager.getCrossPlatformLookAndFeelClassName());
LookAndFeelFactory.addUIDefaultsInitializer(new LookAndFeelFactory.UIDefaultsInitializer() {
public void initialize(UIDefaults defaults) {
Map<String, Object> defaultValues = new HashMap<String, Object>();
defaultValues.put("Slider.trackWidth", new Integer(7));
defaultValues.put("Slider.majorTickLength", new Integer(6));
defaultValues.put("Slider.highlight", new ColorUIResource(255, 255, 255));
defaultValues.put("Slider.horizontalThumbIcon", javax.swing.plaf.metal.MetalIconFactory.getHorizontalSliderThumbIcon());
defaultValues.put("Slider.verticalThumbIcon", javax.swing.plaf.metal.MetalIconFactory.getVerticalSliderThumbIcon());
defaultValues.put("Slider.focusInsets", new InsetsUIResource(0, 0, 0, 0));
for (Map.Entry<String, Object> e : defaultValues.entrySet()) {
if (defaults.get(e.getKey()) == null) {
LOG.debug("Missing key " + e.getKey() + ", using default value " + e.getValue());
defaults.put(e.getKey(), e.getValue());
} else {
LOG.debug("Found key " + e.getKey() + " with value " + defaults.get(e.getKey()));
}
}
}
});
if (MiscUtils.WINDOWS) {
UIManager.put("CheckBox.background", new javax.swing.plaf.ColorUIResource(Color.WHITE));
UIManager.put("Panel.background", new javax.swing.plaf.ColorUIResource(Color.WHITE));
LookAndFeelFactory.installJideExtension(LookAndFeelFactory.XERTO_STYLE_WITHOUT_MENU);
/*UIManager.put("JideTabbedPane.tabAreaBackground", new javax.swing.plaf.ColorUIResource(Color.WHITE));
UIManager.put("JideTabbedPane.background", new javax.swing.plaf.ColorUIResource(Color.WHITE));
UIManager.put("SidePane.background", new javax.swing.plaf.ColorUIResource(Color.WHITE));*/
} else {
LookAndFeelFactory.installJideExtension();
}
LookAndFeelFactory.installDefaultLookAndFeelAndExtension();
System.setProperty("awt.useSystemAAFontSettings", "on");
System.setProperty("swing.aatext", "true");
UIManager.put("TabbedPane.contentBorderInsets", new Insets(0, 0, 0, 0));
//tooltips
UIManager.put("ToolTip.background", new ColorUIResource(255, 255, 255));
ToolTipManager.sharedInstance().setDismissDelay(8000);
ToolTipManager.sharedInstance().setInitialDelay(500);
} catch (Exception x) {
LOG.error("Unable to install look & feel.", x);
}
}
private static void customizeForMac() {
try {
UIManager.put("Panel.background", new Color(237, 237, 237)); // the above line makes the bg dark, setting back
System.setProperty("apple.laf.useScreenMenuBar", "true");
System.setProperty("com.apple.mrj.application.apple.menu.about.name", "MedSavant");
batchApplyProperty(new String[]{
"Button.font",
"ToggleButton.font",
"RadioButton.font",
"CheckBox.font",
"ColorChooser.font",
"ComboBox.font",
"Label.font",
"List.font",
"MenuBar.font",
"MenuItem.font",
"RadioButtonMenuItem.font",
"CheckBoxMenuItem.font",
"Menu.font",
"PopupMenu.font",
"OptionPane.font",
"Panel.font",
"ProgressBar.font",
"ScrollPane.font",
"Viewport.font",
"TabbedPane.font",
"Table.font",
"TableHeader.font",
"TextField.font",
"PasswordField.font",
"TextArea.font",
"TextPane.font",
"EditorPane.font",
"TitledBorder.font",
"ToolBar.font",
"ToolTip.font",
"Tree.font",
"JOptionPane.font",
"JDialog.font"}, FontFactory.getGeneralFont());
System.setProperty("awt.useSystemAAFontSettings", "on");
System.setProperty("swing.aatext", "true");
UIManager.put("TitledBorder.border", UIManager.getBorder("TitledBorder.aquaVariant"));
//com.apple.eawt.FullScreenUtilities.setWindowCanFullScreen(this, true);
Application macOSXApplication = Application.getApplication();
macOSXApplication.setAboutHandler(new AboutHandler() {
@Override
public void handleAbout(AppEvent.AboutEvent evt) {
JOptionPane.showMessageDialog(MedSavantFrame.getInstance(), "MedSavant "
+ VersionSettings.getVersionString()
+ "\nCreated by Biolab at University of Toronto.");
}
});
macOSXApplication.setPreferencesHandler(new PreferencesHandler() {
@Override
public void handlePreferences(AppEvent.PreferencesEvent pe) {
DialogUtils.displayMessage("Preferences available for Administrators only");
}
});
macOSXApplication.setQuitHandler(new QuitHandler() {
@Override
public void handleQuitRequestWith(AppEvent.QuitEvent evt, QuitResponse resp) {
MedSavantFrame.getInstance().requestClose();
resp.cancelQuit(); // If user accepted close request, System.exit() was called and we never get here.
}
});
} catch (Throwable x) {
System.err.println("Warning: MedSavant requires Java for Mac OS X 10.6 Update 3 (or later).\nPlease check Software Update for the latest version.");
}
}
private static void batchApplyProperty(String[] propn, Object o) {
for (String s : propn) {
UIManager.put(s, o);
}
}
private static void verifyJIDE() {
com.jidesoft.utils.Lm.verifyLicense("Marc Fiume", "Savant Genome Browser", "1BimsQGmP.vjmoMbfkPdyh0gs3bl3932");
}
private static void setExceptionHandler() {
Thread.setDefaultUncaughtExceptionHandler(
new Thread.UncaughtExceptionHandler() {
@Override
public void uncaughtException(Thread t, Throwable e) {
LOG.info("Global exception handler caught: " + t.getName() + ": " + e);
if (e instanceof InvocationTargetException) {
e = ((InvocationTargetException) e).getCause();
}
if (e instanceof SessionExpiredException) {
SessionExpiredException see = (SessionExpiredException) e;
MedSavantExceptionHandler.handleSessionExpiredException(see);
return;
}
if (e instanceof LockException) {
DialogUtils.displayMessage("Cannot modify database", "<html>Another process is making changes.<br/>Please try again later.</html>");
return;
}
e.printStackTrace();
DialogUtils.displayException("Error", e.getLocalizedMessage(), e);
}
});
}
}