summaryrefslogtreecommitdiffstats
path: root/src/main/java/de/bwlehrpool/bwlp_guac/AvailableClient.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/de/bwlehrpool/bwlp_guac/AvailableClient.java')
-rw-r--r--src/main/java/de/bwlehrpool/bwlp_guac/AvailableClient.java157
1 files changed, 157 insertions, 0 deletions
diff --git a/src/main/java/de/bwlehrpool/bwlp_guac/AvailableClient.java b/src/main/java/de/bwlehrpool/bwlp_guac/AvailableClient.java
new file mode 100644
index 0000000..7c4ca80
--- /dev/null
+++ b/src/main/java/de/bwlehrpool/bwlp_guac/AvailableClient.java
@@ -0,0 +1,157 @@
+package de.bwlehrpool.bwlp_guac;
+
+import java.io.IOException;
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.apache.guacamole.protocol.GuacamoleConfiguration;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import de.bwlehrpool.bwlp_guac.JsonClient.State;
+
+public class AvailableClient {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(AvailableClient.class);
+
+ private static final AtomicLong CON_ID = new AtomicLong();
+
+ private final String clientip;
+
+ private String password;
+
+ private State state;
+
+ private String inUseBy;
+
+ private WrappedConnection connection;
+
+ private long deadline;
+
+ private long lastConnectionCheck;
+
+ private boolean connectionOk;
+
+ private final Object connCheckLock = new Object();
+
+ public AvailableClient(JsonClient source) {
+ this.clientip = source.clientip;
+ update(source);
+ }
+
+ /**
+ * Update this client's state, resetting "in use" if appropriate. Ie, the
+ * password changed, which would mean the remote VNC server was restarted, so it
+ * can't possibly be in use by the old user anymore.
+ */
+ public synchronized void update(JsonClient source) {
+ if (this.password == null || !this.password.equals(source.password)) {
+ if (source.state != State.OCCUPIED) {
+ if (this.inUseBy != null) {
+ LOGGER.info("Client " + this + "is available again");
+ this.inUseBy = null;
+ this.connection = null;
+ }
+ }
+ this.lastConnectionCheck = 0;
+ this.password = source.password;
+ }
+ this.state = source.state;
+ this.deadline = 0;
+ }
+
+ /**
+ * Try to reserve this client for the given user.
+ */
+ public synchronized boolean claim(String user) {
+ if (this.inUseBy != null || this.password == null || this.state != State.IDLE || user == null)
+ return false;
+ this.inUseBy = user;
+ this.connection = new WrappedConnection(this.clientip + "/" + CON_ID.incrementAndGet(), this);
+ this.state = State.OCCUPIED;
+ return true;
+ }
+
+ public synchronized boolean isInUseBy(String user) {
+ return (this.inUseBy != null && this.inUseBy.equals(user));
+ }
+
+ public synchronized WrappedConnection getConnection(String expectedOwner) {
+ if (isInUseBy(expectedOwner))
+ return this.connection;
+ return null;
+ }
+
+ public synchronized void releaseConnection(String expectedOwner) {
+ if (isInUseBy(expectedOwner)) {
+ LOGGER.info("Prematurely releasing client " + this);
+ this.inUseBy = null;
+ this.connection = null;
+ }
+ }
+
+ /**
+ * If this connection is not returned by the sat server anymore, we keep it
+ * around for another 5 minutes just in case.
+ */
+ public boolean isTimeout(long NOW) {
+ if (deadline == 0) {
+ deadline = NOW + 300000;
+ return false;
+ }
+ return deadline < NOW;
+ }
+
+ @Override
+ public String toString() {
+ return clientip + "/" + state + "/" + inUseBy;
+ }
+
+ public GuacamoleConfiguration toGuacConfig() {
+ GuacamoleConfiguration cfg = new GuacamoleConfiguration();
+ cfg.setProtocol("vnc");
+ cfg.setParameter("hostname", this.clientip);
+ cfg.setParameter("port", Integer.toString(5900)); // TODO
+ cfg.setParameter("password", password);
+ return cfg;
+ }
+
+ /**
+ * Check if the given VNC credentials actually work, so we avoid assigning a
+ * bogus entry to a user.
+ */
+ public boolean checkConnection(int retries) {
+ long now = System.currentTimeMillis();
+ synchronized (connCheckLock) {
+ if (now < this.lastConnectionCheck) {
+ this.lastConnectionCheck = 0;
+ }
+ if (now - this.lastConnectionCheck < 5000)
+ return this.connectionOk;
+ for (;;) {
+ try (VncConnection vnc = new VncConnection(this.clientip, 5900)) {
+ LOGGER.debug("VNC Version for " + this.clientip + " is " + vnc.handshake());
+ if (vnc.tryLogin(this.password)) {
+ this.lastConnectionCheck = now;
+ return this.connectionOk = true;
+ }
+ } catch (IOException e) {
+ LOGGER.info("Connection error VNC @ " + this, e);
+ if (retries-- > 0) {
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e1) {
+ Thread.currentThread().interrupt();
+ break;
+ }
+ continue;
+ }
+ }
+ break;
+ }
+ this.lastConnectionCheck = now;
+ this.password = null; // Render invalid, so ConnectionManager::getForUser() doesn't turn into an infinite loop
+ return this.connectionOk = false;
+ }
+ }
+
+}