diff options
author | Jonathan Bauer | 2016-03-03 18:01:37 +0100 |
---|---|---|
committer | Jonathan Bauer | 2016-03-03 18:01:37 +0100 |
commit | f6a0867f2d8235a9c38c06dc0702c6e948928bcf (patch) | |
tree | bcd38a61059097cf559d6f4b8898628285871fce /dozentenmodul/src/main/java/org/openslx/dozmod/gui/control/AdvancedConfigurator.java | |
parent | [client] add 10px border to wizard's footer (diff) | |
download | tutor-module-f6a0867f2d8235a9c38c06dc0702c6e948928bcf.tar.gz tutor-module-f6a0867f2d8235a9c38c06dc0702c6e948928bcf.tar.xz tutor-module-f6a0867f2d8235a9c38c06dc0702c6e948928bcf.zip |
[client/server] add support for advanced configuration for usb/netrules stuff
Diffstat (limited to 'dozentenmodul/src/main/java/org/openslx/dozmod/gui/control/AdvancedConfigurator.java')
-rw-r--r-- | dozentenmodul/src/main/java/org/openslx/dozmod/gui/control/AdvancedConfigurator.java | 351 |
1 files changed, 351 insertions, 0 deletions
diff --git a/dozentenmodul/src/main/java/org/openslx/dozmod/gui/control/AdvancedConfigurator.java b/dozentenmodul/src/main/java/org/openslx/dozmod/gui/control/AdvancedConfigurator.java new file mode 100644 index 00000000..df036aa3 --- /dev/null +++ b/dozentenmodul/src/main/java/org/openslx/dozmod/gui/control/AdvancedConfigurator.java @@ -0,0 +1,351 @@ +package org.openslx.dozmod.gui.control; + +import java.awt.Color; +import java.awt.Insets; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import javax.swing.BorderFactory; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTextArea; +import javax.swing.JTextPane; +import javax.swing.text.BadLocationException; +import javax.swing.text.MutableAttributeSet; +import javax.swing.text.SimpleAttributeSet; +import javax.swing.text.StyleConstants; +import javax.swing.text.StyledDocument; + +import org.apache.log4j.Logger; +import org.openslx.bwlp.thrift.iface.NetDirection; +import org.openslx.bwlp.thrift.iface.NetRule; +import org.openslx.dozmod.gui.Gui; +import org.openslx.dozmod.gui.helper.GridManager; +import org.openslx.dozmod.gui.helper.MessageType; + +/** + * Widget for advanced configuration options for lectures. This handles + * following options - Network rules - Runscript - USB + */ +public class AdvancedConfigurator extends AdvancedConfiguratorLayout { + + private static final long serialVersionUID = -3497629601818983994L; + private final static Logger LOGGER = Logger + .getLogger(AdvancedConfigurator.class); + + /** + * Character defining how the rules are parsed, e.g. for whitespace \\s + * Example: "IN 8.8.8.8 80" + */ + private static final String FIELD_DELIMITER = "\\s"; + + public AdvancedConfigurator() { + super(); + } + + public AdvancedConfiguration getState() { + List<NetRule> rules = parseNetRules(taNetworkRules.getText()); + if (rules != null) { + return new AdvancedConfiguration(rules, taRunScript.getText()); + } + return null; + } + + public void setState(final AdvancedConfiguration config) { + // setText() blanks the text area if null is given, so no null checks + this.taNetworkRules.setText(decodeNetRulesToText(config.netRulesList)); + this.taRunScript.setText(config.runScriptText); + } + + public boolean isValidState() { + // TODO test state validity? + List<NetRule> tmp = parseNetRules(taNetworkRules.getText()); + if (tmp != null) + LOGGER.debug("Parsed: " + tmp.toString()); + else { + LOGGER.debug("Parsed null."); + } + return tmp != null && !tmp.isEmpty(); + } + + /** + * @param netRulesList + * list of NetRule to decode + * @return String representation of the list of rules + */ + public static String decodeNetRulesToText(final List<NetRule> netRulesList) { + if (netRulesList == null || netRulesList.isEmpty()) + return ""; + + String decodedRules = ""; + Iterator<NetRule> it = netRulesList.iterator(); + while (it.hasNext()) { + String currentLine = ""; + NetRule currentRule = it.next(); + // simple test for validity (since this comes from the server it + // should be correct anyways) + if (currentRule.host.isEmpty() || currentRule.port > 65535) { + LOGGER.error("Invalid rule! Ignoring: " + currentRule.host + + ":" + currentRule.port); + continue; + } + currentLine += currentRule.host + " "; + currentLine += currentRule.port + " "; + currentLine += currentRule.direction.name(); + decodedRules += currentLine + System.lineSeparator(); + } + return decodedRules; + } + + /** + * @param rawNetRules + * the raw text to be parsed + * @return list of valid net rules parsed from the given rawNetRules, + * invalid ones are not included + * @throws Exception + * when parsing fails + */ + public List<NetRule> parseNetRules(final String rawNetRules) { + if (rawNetRules == null) + return null; + List<NetRule> rulesList = new ArrayList<NetRule>(); + if (rawNetRules.isEmpty()) { + return rulesList; + } + // rawNetRules is the text as entered in the textarea + // split it line by line + final String[] netRules = rawNetRules.split(System.lineSeparator()); + for (String ruleLine : netRules) { + LOGGER.debug("Parsing rule: " + ruleLine); + // split the fields and check if we have 3 as expected. + String[] fields = ruleLine.split(FIELD_DELIMITER); + if (fields.length != 3) { + markText(ruleLine, Color.RED); + LOGGER.debug("Invalid number of fields! Expected 3, got: " + + fields.length); + Gui.showMessageBox( + "Nicht genug Felder! Nutzen Sie: <host> <port> [in|out]", + MessageType.ERROR, LOGGER, null); + break; + } + + // start to check fields one by one from the last to the first .... + // check net direction: accept either 'in' or 'out' (case + // insensitive) + if (!fields[2].matches("(?i)^(IN|OUT)+$")) { + markText(ruleLine, Color.RED); + LOGGER.debug("Invalid net direction! Expected (case insensitive) 'IN' or OUT'. Got: " + + fields[2]); + Gui.showMessageBox( + "Ungültige Richtung: nur 'IN' oder 'OUT' erlaubt!", + MessageType.ERROR, LOGGER, null); + break; + } + + // check port: accept if > -2 and < 65535 + int port = -2; + try { + port = Integer.parseInt(fields[1]); + } catch (NumberFormatException e) { + LOGGER.error("Could not parse '" + fields[1] + "' to an int."); + } + // TODO: port = -1 for all ports? + if (port <= -2 && port > 65535) { + markText(ruleLine, Color.RED); + LOGGER.debug("Invalid port number! Got: " + port); + Gui.showMessageBox( + "Ungültiges Port! Muss zwischen 0 und 65536 sein.", + MessageType.ERROR, LOGGER, null); + break; + } + // check hostname: bit more to do here + // for IPs and/or resolvable hostnames we make use of java.net's + // InetAddress.getByName() method which checks the validity of + // an IP-Address represented by a string or if the given hostname + // is resolvable. If any of these happen, we have a valid hostname. + // Non-resolvable hostnames are handled differently, see after the + // try/catch-block + InetAddress ruleHost = null; + try { + ruleHost = InetAddress.getByName(fields[0]); + } catch (UnknownHostException e) { + LOGGER.debug("Invalid hostname (java.net): ", e); + // might be good to see this exception in the log file + } + if (ruleHost == null) { + // either invalid IP-Address or an invalid resolvable hostname + // however it might also be a non-resolvable hostname that would + // be + // valid in the actual pool-rooms, so lets check its syntax + // according to: http://tools.ietf.org/html/rfc1034#section-3.1 + LOGGER.debug("Invalid host/IP! Got: " + fields[0]); + if (checkHostnameSimple(fields[0])) { + markText(ruleLine, Color.YELLOW); + if (!Gui.showMessageBox( + "Konnte '" + + fields[0] + + "' nicht verifizieren. Wollen Sie es trotzdem verwenden?", + MessageType.WARNING_RETRY, LOGGER, null)) { + break; + } else { + // keep it + } + } else { + markText(ruleLine, Color.RED); + continue; + } + } else { + markText(ruleLine, Color.GREEN); + } + // valid, put it in the list + rulesList.add(new NetRule(rulesList.size(), NetDirection + .valueOf(fields[2].toUpperCase()), fields[0], port)); + } + if (netRules.length == rulesList.size()) { + return rulesList; + } + return null; + } + + private boolean checkHostnameSimple(final String hostname) { + if (hostname.length() > 254) { + LOGGER.debug("Hostname is too long!"); + Gui.showMessageBox("Hostname ist zu lang!", MessageType.ERROR, + LOGGER, null); + return false; + } + for (int i = 0; i < hostname.length(); i++) { + Character c = Character.valueOf(hostname.charAt(i)); + if (!(Character.isDigit(c) || Character.isLetter(c) + || c.equals('-') || c.equals('.') || c.equals('/'))) { + LOGGER.debug("Invalid character in hostname: '" + c + "'"); + Gui.showMessageBox("Ungültiges Zeichen '" + c + + "' in hostname!", MessageType.ERROR, LOGGER, null); + return false; + } + } + return true; + } + + // TODO still buggy with text colors: the marking works fine + // but when trying to input new text, funny things happen + private void markText(final String txt, final Color color) { + MutableAttributeSet origSet = taNetworkRules.getInputAttributes(); + SimpleAttributeSet set = new SimpleAttributeSet(); + StyleConstants.setForeground(set, color); + StyleConstants.setBold(set, color == Color.red); + StyledDocument doc = taNetworkRules.getStyledDocument(); + try { + boolean found = false; + for (int pos = 0; pos < doc.getLength() - txt.length() + 1; ++pos) { + String current = doc.getText(pos, txt.length()); + if (current.endsWith(System.lineSeparator())) { + current = current.substring(0, current.length() - 1); + } + if (current.equals(txt)) { + doc.setCharacterAttributes(pos, txt.length(), set, true); + found = true; + break; + } + } + if (found) { + } + } catch (BadLocationException e) { + LOGGER.error( + "Failed to set '" + txt + "' to color " + color.toString(), + e); + } + taNetworkRules.setCharacterAttributes(origSet, true); + taNetworkRules.setStyledDocument(doc); + } + + /** + * Wrapper class for the advanced configuration information needed since we + * need to return a single object from the runAndReturn routine + */ + public static class AdvancedConfiguration { + public List<NetRule> netRulesList; + public String runScriptText; + + public AdvancedConfiguration(List<NetRule> netRulesList, + String runScriptText) { + this.netRulesList = netRulesList; + this.runScriptText = runScriptText; + } + } +} + +/** + * Internal layout class for the advanced configurator (to keep it clean even + * for widgets) + */ +class AdvancedConfiguratorLayout extends JPanel { + + private static final long serialVersionUID = 648729071828404053L; + + private final static String txtNetworkOptionsTitle = "Netzwerk Einstellungen"; + private final static String txtNetworkOptionsDesc = "Hier können Sie Firewall-Regeln festlegen mit folgendem Format (TODO)"; + private final static String txtNetworkRulesTitle = "Netzwerk-Regeln"; + private final static String txtRunScriptTitle = "Run-Skript"; + private final static String txtRunScriptDesc = "Ein hier abgelegtes Skript wird beim Start einer Windows-VM automatisch ausgeführt."; + + private final JPanel pnlNetworkOptions; + private final JPanel pnlRunScript; + protected final JTextPane taNetworkRules; + protected final JTextArea taRunScript; + + public AdvancedConfiguratorLayout() { + + GridManager grid = new GridManager(this, 1, true, + new Insets(5, 5, 5, 5)); + + // middle panel for network rules + pnlNetworkOptions = new JPanel(); + GridManager gridNetworkOptions = new GridManager(pnlNetworkOptions, 1, + true, new Insets(2, 2, 2, 2)); + pnlNetworkOptions.setBorder(BorderFactory + .createTitledBorder(txtNetworkOptionsTitle)); + taNetworkRules = new JTextPane(); + + JScrollPane scpNetworkRules = new JScrollPane(taNetworkRules, + JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, + JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); + pnlNetworkOptions.setBorder(BorderFactory + .createTitledBorder(txtNetworkRulesTitle)); + gridNetworkOptions + .add(new WordWrapLabel(txtNetworkOptionsDesc, false, true)) + .fill(true, false).expand(true, false); + gridNetworkOptions.nextRow(); + gridNetworkOptions.add(scpNetworkRules).fill(true, true) + .expand(true, true); + gridNetworkOptions.finish(false); + + // second middle panel for the run script textpane + pnlRunScript = new JPanel(); + GridManager gridRunScript = new GridManager(pnlRunScript, 1, true, + new Insets(2, 2, 2, 2)); + taRunScript = new JTextArea("", 5, 20); + taRunScript.setLineWrap(true); + taRunScript.setWrapStyleWord(true); + JScrollPane scpRunScript = new JScrollPane(taRunScript, + JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, + JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); + pnlRunScript.setBorder(BorderFactory + .createTitledBorder(txtRunScriptTitle)); + gridRunScript.add(new WordWrapLabel(txtRunScriptDesc, false, true)) + .fill(true, false).expand(true, false); + gridRunScript.nextRow(); + gridRunScript.add(scpRunScript).fill(true, true).expand(true, true); + gridRunScript.finish(false); + + // build the final grid + grid.add(pnlNetworkOptions).fill(true, true).expand(true, true); + grid.nextRow(); + grid.add(pnlRunScript).fill(true, true).expand(true, true); + grid.finish(false); + + } +} |