package edu.yu.einstein.genplay.gui.dialog.optionDialog; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; import java.io.BufferedReader; import java.io.FileOutputStream; import java.io.FileReader; import java.io.IOException; import java.lang.management.ManagementFactory; import java.text.DecimalFormat; import java.text.NumberFormat; import javax.swing.JLabel; import javax.swing.JSlider; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; /** * Panel to select the memory to allocate to GenPlay. * Only available for the OSX installed version of GenPlay * @author Julien Lajugie */ class MemoryOptionPanel extends OptionPanel { /** Generated serial ID */ private static final long serialVersionUID = 8758894281496945808L; /** Path to the app bundle info file */ private final static String PLIST_PATH = "GenPlay.app/Contents/Info.plist"; /** Number of bytes in a MB */ private final static int BYTES_PER_MB = 1048576; /** Max memory that can be allocated to GenPlay as a ratio to the RAM available */ private final static double MAX_MEMORY_USAGE_RATIO = 0.9; /** Min memory that can be allocated to GenPlay in MB*/ private final static int MIN_MEMORY = 100; /** If the RAM cannot be determined (because of the JVM) we assume the following (in MB) */ private static final int DEFAULT_RAM_ASSUMED = 50000; private final JLabel jlMemory; // label memory private final JSlider jsMemorySelect; // slider to select the memory to allocate private final JLabel jlMemorySelected; // label showing the memory amount selected private final JLabel jlRestartNeeded; // label telling that a restart is needed private final String xmxParameter; // String of the xmx parameter private final int xmxMemory; // value of the xmx parameter in MB private final int computerRam; // RAM of the computer in MB /** * Creates an instance of {@link MemoryOptionPanel} * @throws IOException if the Plist file cannot be found */ MemoryOptionPanel() throws IOException { super("Memory"); xmxParameter = retrieveXmxParameter(); computerRam = getComputerRAM(); int maxMemory = (int) (computerRam * MAX_MEMORY_USAGE_RATIO); xmxMemory = Math.min(extractXmxMemory(), maxMemory); jlMemory = new JLabel("Select the maximum amount of memory that can be allocated to GenPlay:"); jsMemorySelect = new JSlider(JSlider.HORIZONTAL, MIN_MEMORY, maxMemory, xmxMemory); jsMemorySelect.setMajorTickSpacing(1000); jsMemorySelect.setMinorTickSpacing(100); jsMemorySelect.setPaintTicks(true); jsMemorySelect.setPaintLabels(false); final NumberFormat format = DecimalFormat.getInstance(); format.setMaximumFractionDigits(1); String currentMemoryValue = format.format(jsMemorySelect.getValue() / 1000d); jlMemorySelected = new JLabel("Memory allocated to GenPlay: " + currentMemoryValue + "GB"); jsMemorySelect.addChangeListener(new ChangeListener() { @Override public void stateChanged(ChangeEvent e) { String currentMemoryValue = format.format(jsMemorySelect.getValue() / 1000d); jlMemorySelected.setText("Memory allocated to GenPlay: " + currentMemoryValue + "GB"); } }); jlRestartNeeded = new JLabel("Changes will take effect when you restart GenPlay"); // add the components setLayout(new GridBagLayout()); GridBagConstraints c = new GridBagConstraints(); c.gridx = 0; c.gridy = 0; c.insets = new Insets(5, 0, 5, 0); add(jlMemory, c); c.gridx = 0; c.gridy = 1; c.insets = new Insets(5, 0, 5, 0); add(jsMemorySelect, c); c.gridx = 0; c.gridy = 2; c.insets = new Insets(5, 0, 5, 0); add(jlMemorySelected, c); c.gridx = 0; c.gridy = 3; c.insets = new Insets(5, 0, 5, 0); add(jlRestartNeeded, c); } /** * Extracts the memory value from the Xmx parameter * @return the memory set in the Xmx parameter in MB */ private int extractXmxMemory() { if (xmxParameter == null) { return -1; } String memoryString = xmxParameter.substring(4, xmxParameter.length() - 1); int mem = Integer.parseInt(memoryString); // multiply by 1000 if the parameter is in GB if (xmxParameter.toLowerCase().charAt(xmxParameter.length() - 1) == 'g') { mem *= 1000; } return mem; } /** * @return the RAM memory in MB */ private int getComputerRAM() { long memorySize = ((com.sun.management.OperatingSystemMXBean) ManagementFactory .getOperatingSystemMXBean()).getTotalPhysicalMemorySize(); // if the RAM cannot be retrieved if (memorySize == 0) { return DEFAULT_RAM_ASSUMED; } return (int) (memorySize / (double) BYTES_PER_MB); } /** * @return the Xmx jvm parameter from the plist file * @throws IOException */ private String retrieveXmxParameter() throws IOException { BufferedReader reader = null; try { reader = new BufferedReader(new FileReader(PLIST_PATH)); String xmxParam = null; String line; while (((line = reader.readLine()) != null) && (xmxParam == null)) { String lowerCaseline = line.toLowerCase(); int xmxStartIndex = lowerCaseline.indexOf("-xmx"); if (xmxStartIndex != -1) { int xmxStopIndex = lowerCaseline.indexOf('m', xmxStartIndex + 4); if (xmxStopIndex == -1) { xmxStopIndex = lowerCaseline.indexOf('g', xmxStartIndex + 4); } if (xmxStopIndex != -1) { xmxParam = line.substring(xmxStartIndex, xmxStopIndex + 1); } } } return xmxParam; } finally { if (reader != null) { reader.close(); } } } /** * Replaces the value of the Xmx parameter in the plist file * if it changed * @throws IOException */ void writeNewPList() throws IOException { int selectedMemory = jsMemorySelect.getValue(); if (selectedMemory != xmxMemory) { String newXmxString = "-Xmx" + selectedMemory + "M"; BufferedReader reader = null; FileOutputStream writer = null; try { reader = new BufferedReader(new FileReader(PLIST_PATH)); String line; String input = ""; while ((line = reader.readLine()) != null) { line = line.replace(xmxParameter, newXmxString); input += line + '\n'; } reader.close(); writer = new FileOutputStream(PLIST_PATH); writer.write(input.getBytes()); } finally { if (reader != null){ reader.close(); } if (writer != null) { writer.close(); } } } } }