blob: e9144f89f1b25756ac20f7e27cb56ed6573acad3 (
plain) (
tree)
|
|
package de.bwlehrpool.bwlp_guac;
import java.io.IOException;
import java.util.ArrayList;
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 implements Cloneable {
private static final Logger LOGGER = LoggerFactory.getLogger(AvailableClient.class);
private static final AtomicLong CON_ID = new AtomicLong();
public ArrayList<JsonLocation> locationList = new ArrayList<JsonLocation>();
private final String clientip;
private String password;
private int locationid;
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;
this.locationid = source.locationid;
update(source);
}
private AvailableClient(String clientip) {
this.clientip = clientip;
}
/**
* 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 State getState() {
return state;
}
public int getLocationid() {
return locationid;
}
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)) {
LOGGER.info("Connection to " + this + " is OK");
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;
}
}
@Override
public AvailableClient clone() {
AvailableClient c = new AvailableClient(this.clientip);
c.state = this.state;
c.inUseBy = this.inUseBy;
c.password = this.password;
return c;
}
}
|