mirror of
https://github.com/gyurix1968/guacamole-client.git
synced 2025-09-06 21:27:40 +00:00
Merge pull request #248 from glyptodon/concurrent-policy
GUAC-830: Implement concurrency policy attributes
This commit is contained in:
@@ -42,6 +42,20 @@ public class ConnectionModel extends GroupedObjectModel {
|
|||||||
*/
|
*/
|
||||||
private String protocol;
|
private String protocol;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The maximum number of connections that can be established to this
|
||||||
|
* connection concurrently, zero if no restriction applies, or null if the
|
||||||
|
* default restrictions should be applied.
|
||||||
|
*/
|
||||||
|
private Integer maxConnections;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The maximum number of connections that can be established to this
|
||||||
|
* connection concurrently by any one user, zero if no restriction applies,
|
||||||
|
* or null if the default restrictions should be applied.
|
||||||
|
*/
|
||||||
|
private Integer maxConnectionsPerUser;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new, empty connection.
|
* Creates a new, empty connection.
|
||||||
*/
|
*/
|
||||||
@@ -89,6 +103,58 @@ public class ConnectionModel extends GroupedObjectModel {
|
|||||||
this.protocol = protocol;
|
this.protocol = protocol;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the maximum number of connections that can be established to
|
||||||
|
* this connection concurrently.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* The maximum number of connections that can be established to this
|
||||||
|
* connection concurrently, zero if no restriction applies, or null if
|
||||||
|
* the default restrictions should be applied.
|
||||||
|
*/
|
||||||
|
public Integer getMaxConnections() {
|
||||||
|
return maxConnections;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the maximum number of connections that can be established to this
|
||||||
|
* connection concurrently.
|
||||||
|
*
|
||||||
|
* @param maxConnections
|
||||||
|
* The maximum number of connections that can be established to this
|
||||||
|
* connection concurrently, zero if no restriction applies, or null if
|
||||||
|
* the default restrictions should be applied.
|
||||||
|
*/
|
||||||
|
public void setMaxConnections(Integer maxConnections) {
|
||||||
|
this.maxConnections = maxConnections;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the maximum number of connections that can be established to
|
||||||
|
* this connection concurrently by any one user.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* The maximum number of connections that can be established to this
|
||||||
|
* connection concurrently by any one user, zero if no restriction
|
||||||
|
* applies, or null if the default restrictions should be applied.
|
||||||
|
*/
|
||||||
|
public Integer getMaxConnectionsPerUser() {
|
||||||
|
return maxConnectionsPerUser;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the maximum number of connections that can be established to this
|
||||||
|
* connection concurrently by any one user.
|
||||||
|
*
|
||||||
|
* @param maxConnectionsPerUser
|
||||||
|
* The maximum number of connections that can be established to this
|
||||||
|
* connection concurrently by any one user, zero if no restriction
|
||||||
|
* applies, or null if the default restrictions should be applied.
|
||||||
|
*/
|
||||||
|
public void setMaxConnectionsPerUser(Integer maxConnectionsPerUser) {
|
||||||
|
this.maxConnectionsPerUser = maxConnectionsPerUser;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getIdentifier() {
|
public String getIdentifier() {
|
||||||
|
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2013 Glyptodon LLC
|
* Copyright (C) 2015 Glyptodon LLC
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -24,17 +24,25 @@ package org.glyptodon.guacamole.auth.jdbc.connection;
|
|||||||
|
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
import com.google.inject.Provider;
|
import com.google.inject.Provider;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import org.glyptodon.guacamole.auth.jdbc.tunnel.GuacamoleTunnelService;
|
import org.glyptodon.guacamole.auth.jdbc.tunnel.GuacamoleTunnelService;
|
||||||
import org.glyptodon.guacamole.GuacamoleException;
|
import org.glyptodon.guacamole.GuacamoleException;
|
||||||
import org.glyptodon.guacamole.auth.jdbc.base.ModeledGroupedDirectoryObject;
|
import org.glyptodon.guacamole.auth.jdbc.base.ModeledGroupedDirectoryObject;
|
||||||
|
import org.glyptodon.guacamole.form.Field;
|
||||||
|
import org.glyptodon.guacamole.form.Form;
|
||||||
|
import org.glyptodon.guacamole.form.NumericField;
|
||||||
import org.glyptodon.guacamole.net.GuacamoleTunnel;
|
import org.glyptodon.guacamole.net.GuacamoleTunnel;
|
||||||
import org.glyptodon.guacamole.net.auth.Connection;
|
import org.glyptodon.guacamole.net.auth.Connection;
|
||||||
import org.glyptodon.guacamole.net.auth.ConnectionRecord;
|
import org.glyptodon.guacamole.net.auth.ConnectionRecord;
|
||||||
import org.glyptodon.guacamole.protocol.GuacamoleClientInformation;
|
import org.glyptodon.guacamole.protocol.GuacamoleClientInformation;
|
||||||
import org.glyptodon.guacamole.protocol.GuacamoleConfiguration;
|
import org.glyptodon.guacamole.protocol.GuacamoleConfiguration;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An implementation of the Connection object which is backed by a database
|
* An implementation of the Connection object which is backed by a database
|
||||||
@@ -46,6 +54,40 @@ import org.glyptodon.guacamole.protocol.GuacamoleConfiguration;
|
|||||||
public class ModeledConnection extends ModeledGroupedDirectoryObject<ConnectionModel>
|
public class ModeledConnection extends ModeledGroupedDirectoryObject<ConnectionModel>
|
||||||
implements Connection {
|
implements Connection {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logger for this class.
|
||||||
|
*/
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(ModeledConnection.class);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name of the attribute which controls the maximum number of
|
||||||
|
* concurrent connections.
|
||||||
|
*/
|
||||||
|
public static final String MAX_CONNECTIONS_NAME = "max-connections";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name of the attribute which controls the maximum number of
|
||||||
|
* concurrent connections per user.
|
||||||
|
*/
|
||||||
|
public static final String MAX_CONNECTIONS_PER_USER_NAME = "max-connections-per-user";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* All attributes related to restricting user accounts, within a logical
|
||||||
|
* form.
|
||||||
|
*/
|
||||||
|
public static final Form CONCURRENCY_LIMITS = new Form("concurrency", Arrays.<Field>asList(
|
||||||
|
new NumericField(MAX_CONNECTIONS_NAME),
|
||||||
|
new NumericField(MAX_CONNECTIONS_PER_USER_NAME)
|
||||||
|
));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* All possible attributes of connection objects organized as individual,
|
||||||
|
* logical forms.
|
||||||
|
*/
|
||||||
|
public static final Collection<Form> ATTRIBUTES = Collections.unmodifiableCollection(Arrays.asList(
|
||||||
|
CONCURRENCY_LIMITS
|
||||||
|
));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Service for managing connections.
|
* Service for managing connections.
|
||||||
*/
|
*/
|
||||||
@@ -127,12 +169,35 @@ public class ModeledConnection extends ModeledGroupedDirectoryObject<ConnectionM
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<String, String> getAttributes() {
|
public Map<String, String> getAttributes() {
|
||||||
return Collections.<String, String>emptyMap();
|
|
||||||
|
Map<String, String> attributes = new HashMap<String, String>();
|
||||||
|
|
||||||
|
// Set connection limit attribute
|
||||||
|
attributes.put(MAX_CONNECTIONS_NAME, NumericField.format(getModel().getMaxConnections()));
|
||||||
|
|
||||||
|
// Set per-user connection limit attribute
|
||||||
|
attributes.put(MAX_CONNECTIONS_PER_USER_NAME, NumericField.format(getModel().getMaxConnectionsPerUser()));
|
||||||
|
|
||||||
|
return attributes;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setAttributes(Map<String, String> attributes) {
|
public void setAttributes(Map<String, String> attributes) {
|
||||||
// Drop all attributes - none currently supported
|
|
||||||
|
// Translate connection limit attribute
|
||||||
|
try { getModel().setMaxConnections(NumericField.parse(attributes.get(MAX_CONNECTIONS_NAME))); }
|
||||||
|
catch (NumberFormatException e) {
|
||||||
|
logger.warn("Not setting maximum connections: {}", e.getMessage());
|
||||||
|
logger.debug("Unable to parse numeric attribute.", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Translate per-user connection limit attribute
|
||||||
|
try { getModel().setMaxConnectionsPerUser(NumericField.parse(attributes.get(MAX_CONNECTIONS_PER_USER_NAME))); }
|
||||||
|
catch (NumberFormatException e) {
|
||||||
|
logger.warn("Not setting maximum connections per user: {}", e.getMessage());
|
||||||
|
logger.debug("Unable to parse numeric attribute.", e);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -43,6 +43,20 @@ public class ConnectionGroupModel extends GroupedObjectModel {
|
|||||||
*/
|
*/
|
||||||
private ConnectionGroup.Type type;
|
private ConnectionGroup.Type type;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The maximum number of connections that can be established to this
|
||||||
|
* connection group concurrently, zero if no restriction applies, or
|
||||||
|
* null if the default restrictions should be applied.
|
||||||
|
*/
|
||||||
|
private Integer maxConnections;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The maximum number of connections that can be established to this
|
||||||
|
* connection group concurrently by any one user, zero if no restriction
|
||||||
|
* applies, or null if the default restrictions should be applied.
|
||||||
|
*/
|
||||||
|
private Integer maxConnectionsPerUser;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new, empty connection group.
|
* Creates a new, empty connection group.
|
||||||
*/
|
*/
|
||||||
@@ -91,6 +105,60 @@ public class ConnectionGroupModel extends GroupedObjectModel {
|
|||||||
this.type = type;
|
this.type = type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the maximum number of connections that can be established to
|
||||||
|
* this connection group concurrently.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* The maximum number of connections that can be established to this
|
||||||
|
* connection group concurrently, zero if no restriction applies, or
|
||||||
|
* null if the default restrictions should be applied.
|
||||||
|
*/
|
||||||
|
public Integer getMaxConnections() {
|
||||||
|
return maxConnections;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the maximum number of connections that can be established to this
|
||||||
|
* connection group concurrently.
|
||||||
|
*
|
||||||
|
* @param maxConnections
|
||||||
|
* The maximum number of connections that can be established to this
|
||||||
|
* connection group concurrently, zero if no restriction applies, or
|
||||||
|
* null if the default restrictions should be applied.
|
||||||
|
*/
|
||||||
|
public void setMaxConnections(Integer maxConnections) {
|
||||||
|
this.maxConnections = maxConnections;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the maximum number of connections that can be established to
|
||||||
|
* this connection group concurrently by any one user.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* The maximum number of connections that can be established to this
|
||||||
|
* connection group concurrently by any one user, zero if no
|
||||||
|
* restriction applies, or null if the default restrictions should be
|
||||||
|
* applied.
|
||||||
|
*/
|
||||||
|
public Integer getMaxConnectionsPerUser() {
|
||||||
|
return maxConnectionsPerUser;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the maximum number of connections that can be established to this
|
||||||
|
* connection group concurrently by any one user.
|
||||||
|
*
|
||||||
|
* @param maxConnectionsPerUser
|
||||||
|
* The maximum number of connections that can be established to this
|
||||||
|
* connection group concurrently by any one user, zero if no
|
||||||
|
* restriction applies, or null if the default restrictions should be
|
||||||
|
* applied.
|
||||||
|
*/
|
||||||
|
public void setMaxConnectionsPerUser(Integer maxConnectionsPerUser) {
|
||||||
|
this.maxConnectionsPerUser = maxConnectionsPerUser;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getIdentifier() {
|
public String getIdentifier() {
|
||||||
|
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2013 Glyptodon LLC
|
* Copyright (C) 2015 Glyptodon LLC
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -23,26 +23,69 @@
|
|||||||
package org.glyptodon.guacamole.auth.jdbc.connectiongroup;
|
package org.glyptodon.guacamole.auth.jdbc.connectiongroup;
|
||||||
|
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import org.glyptodon.guacamole.auth.jdbc.connection.ConnectionService;
|
import org.glyptodon.guacamole.auth.jdbc.connection.ConnectionService;
|
||||||
import org.glyptodon.guacamole.auth.jdbc.tunnel.GuacamoleTunnelService;
|
import org.glyptodon.guacamole.auth.jdbc.tunnel.GuacamoleTunnelService;
|
||||||
import org.glyptodon.guacamole.GuacamoleException;
|
import org.glyptodon.guacamole.GuacamoleException;
|
||||||
import org.glyptodon.guacamole.auth.jdbc.base.ModeledGroupedDirectoryObject;
|
import org.glyptodon.guacamole.auth.jdbc.base.ModeledGroupedDirectoryObject;
|
||||||
|
import org.glyptodon.guacamole.form.Field;
|
||||||
|
import org.glyptodon.guacamole.form.Form;
|
||||||
|
import org.glyptodon.guacamole.form.NumericField;
|
||||||
import org.glyptodon.guacamole.net.GuacamoleTunnel;
|
import org.glyptodon.guacamole.net.GuacamoleTunnel;
|
||||||
import org.glyptodon.guacamole.net.auth.ConnectionGroup;
|
import org.glyptodon.guacamole.net.auth.ConnectionGroup;
|
||||||
import org.glyptodon.guacamole.protocol.GuacamoleClientInformation;
|
import org.glyptodon.guacamole.protocol.GuacamoleClientInformation;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An implementation of the ConnectionGroup object which is backed by a
|
* An implementation of the ConnectionGroup object which is backed by a
|
||||||
* database model.
|
* database model.
|
||||||
*
|
*
|
||||||
* @author James Muehlner
|
* @author James Muehlner
|
||||||
|
* @author Michael Jumper
|
||||||
*/
|
*/
|
||||||
public class ModeledConnectionGroup extends ModeledGroupedDirectoryObject<ConnectionGroupModel>
|
public class ModeledConnectionGroup extends ModeledGroupedDirectoryObject<ConnectionGroupModel>
|
||||||
implements ConnectionGroup {
|
implements ConnectionGroup {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logger for this class.
|
||||||
|
*/
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(ModeledConnectionGroup.class);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name of the attribute which controls the maximum number of
|
||||||
|
* concurrent connections.
|
||||||
|
*/
|
||||||
|
public static final String MAX_CONNECTIONS_NAME = "max-connections";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name of the attribute which controls the maximum number of
|
||||||
|
* concurrent connections per user.
|
||||||
|
*/
|
||||||
|
public static final String MAX_CONNECTIONS_PER_USER_NAME = "max-connections-per-user";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* All attributes related to restricting user accounts, within a logical
|
||||||
|
* form.
|
||||||
|
*/
|
||||||
|
public static final Form CONCURRENCY_LIMITS = new Form("concurrency", Arrays.<Field>asList(
|
||||||
|
new NumericField(MAX_CONNECTIONS_NAME),
|
||||||
|
new NumericField(MAX_CONNECTIONS_PER_USER_NAME)
|
||||||
|
));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* All possible attributes of connection group objects organized as
|
||||||
|
* individual, logical forms.
|
||||||
|
*/
|
||||||
|
public static final Collection<Form> ATTRIBUTES = Collections.unmodifiableCollection(Arrays.asList(
|
||||||
|
CONCURRENCY_LIMITS
|
||||||
|
));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Service for managing connections.
|
* Service for managing connections.
|
||||||
*/
|
*/
|
||||||
@@ -112,12 +155,35 @@ public class ModeledConnectionGroup extends ModeledGroupedDirectoryObject<Connec
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<String, String> getAttributes() {
|
public Map<String, String> getAttributes() {
|
||||||
return Collections.<String, String>emptyMap();
|
|
||||||
|
Map<String, String> attributes = new HashMap<String, String>();
|
||||||
|
|
||||||
|
// Set connection limit attribute
|
||||||
|
attributes.put(MAX_CONNECTIONS_NAME, NumericField.format(getModel().getMaxConnections()));
|
||||||
|
|
||||||
|
// Set per-user connection limit attribute
|
||||||
|
attributes.put(MAX_CONNECTIONS_PER_USER_NAME, NumericField.format(getModel().getMaxConnectionsPerUser()));
|
||||||
|
|
||||||
|
return attributes;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setAttributes(Map<String, String> attributes) {
|
public void setAttributes(Map<String, String> attributes) {
|
||||||
// Drop all attributes - none currently supported
|
|
||||||
|
// Translate connection limit attribute
|
||||||
|
try { getModel().setMaxConnections(NumericField.parse(attributes.get(MAX_CONNECTIONS_NAME))); }
|
||||||
|
catch (NumberFormatException e) {
|
||||||
|
logger.warn("Not setting maximum connections: {}", e.getMessage());
|
||||||
|
logger.debug("Unable to parse numeric attribute.", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Translate per-user connection limit attribute
|
||||||
|
try { getModel().setMaxConnectionsPerUser(NumericField.parse(attributes.get(MAX_CONNECTIONS_PER_USER_NAME))); }
|
||||||
|
catch (NumberFormatException e) {
|
||||||
|
logger.warn("Not setting maximum connections per user: {}", e.getMessage());
|
||||||
|
logger.debug("Unable to parse numeric attribute.", e);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -160,7 +160,7 @@ public class ConfigurableGuacamoleTunnelService
|
|||||||
int count = multiset.count(value);
|
int count = multiset.count(value);
|
||||||
|
|
||||||
// Bail out if the maximum has already been reached
|
// Bail out if the maximum has already been reached
|
||||||
if (count >= max || max == 0)
|
if (count >= max && max != 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Attempt to add one more value
|
// Attempt to add one more value
|
||||||
@@ -200,14 +200,23 @@ public class ConfigurableGuacamoleTunnelService
|
|||||||
// Return the first unreserved connection
|
// Return the first unreserved connection
|
||||||
for (ModeledConnection connection : sortedConnections) {
|
for (ModeledConnection connection : sortedConnections) {
|
||||||
|
|
||||||
|
// Determine per-user limits on this connection
|
||||||
|
Integer connectionMaxConnectionsPerUser = connection.getModel().getMaxConnectionsPerUser();
|
||||||
|
if (connectionMaxConnectionsPerUser == null)
|
||||||
|
connectionMaxConnectionsPerUser = connectionDefaultMaxConnectionsPerUser;
|
||||||
|
|
||||||
|
// Determine overall limits on this connection
|
||||||
|
Integer connectionMaxConnections = connection.getModel().getMaxConnections();
|
||||||
|
if (connectionMaxConnections == null)
|
||||||
|
connectionMaxConnections = connectionDefaultMaxConnections;
|
||||||
|
|
||||||
// Attempt to aquire connection according to per-user limits
|
// Attempt to aquire connection according to per-user limits
|
||||||
Seat seat = new Seat(username, connection.getIdentifier());
|
Seat seat = new Seat(username, connection.getIdentifier());
|
||||||
if (tryAdd(activeSeats, seat,
|
if (tryAdd(activeSeats, seat, connectionMaxConnectionsPerUser)) {
|
||||||
connectionDefaultMaxConnectionsPerUser)) {
|
|
||||||
|
|
||||||
// Attempt to aquire connection according to overall limits
|
// Attempt to aquire connection according to overall limits
|
||||||
if (tryAdd(activeConnections, connection.getIdentifier(),
|
if (tryAdd(activeConnections, connection.getIdentifier(),
|
||||||
connectionDefaultMaxConnections))
|
connectionMaxConnections))
|
||||||
return connection;
|
return connection;
|
||||||
|
|
||||||
// Acquire failed - retry with next connection
|
// Acquire failed - retry with next connection
|
||||||
@@ -222,7 +231,7 @@ public class ConfigurableGuacamoleTunnelService
|
|||||||
|
|
||||||
// Too many connections by this user
|
// Too many connections by this user
|
||||||
if (userSpecificFailure)
|
if (userSpecificFailure)
|
||||||
throw new GuacamoleClientTooManyException("Cannot connect. Connection group already in use by this user.");
|
throw new GuacamoleClientTooManyException("Cannot connect. Connection already in use by this user.");
|
||||||
|
|
||||||
// Too many connections, but not necessarily due purely to this user
|
// Too many connections, but not necessarily due purely to this user
|
||||||
else
|
else
|
||||||
@@ -243,14 +252,24 @@ public class ConfigurableGuacamoleTunnelService
|
|||||||
// Get username
|
// Get username
|
||||||
String username = user.getUser().getIdentifier();
|
String username = user.getUser().getIdentifier();
|
||||||
|
|
||||||
|
// Determine per-user limits on this connection group
|
||||||
|
Integer connectionGroupMaxConnectionsPerUser = connectionGroup.getModel().getMaxConnectionsPerUser();
|
||||||
|
if (connectionGroupMaxConnectionsPerUser == null)
|
||||||
|
connectionGroupMaxConnectionsPerUser = connectionGroupDefaultMaxConnectionsPerUser;
|
||||||
|
|
||||||
|
// Determine overall limits on this connection group
|
||||||
|
Integer connectionGroupMaxConnections = connectionGroup.getModel().getMaxConnections();
|
||||||
|
if (connectionGroupMaxConnections == null)
|
||||||
|
connectionGroupMaxConnections = connectionGroupDefaultMaxConnections;
|
||||||
|
|
||||||
// Attempt to aquire connection group according to per-user limits
|
// Attempt to aquire connection group according to per-user limits
|
||||||
Seat seat = new Seat(username, connectionGroup.getIdentifier());
|
Seat seat = new Seat(username, connectionGroup.getIdentifier());
|
||||||
if (tryAdd(activeGroupSeats, seat,
|
if (tryAdd(activeGroupSeats, seat,
|
||||||
connectionGroupDefaultMaxConnectionsPerUser)) {
|
connectionGroupMaxConnectionsPerUser)) {
|
||||||
|
|
||||||
// Attempt to aquire connection group according to overall limits
|
// Attempt to aquire connection group according to overall limits
|
||||||
if (tryAdd(activeGroups, connectionGroup.getIdentifier(),
|
if (tryAdd(activeGroups, connectionGroup.getIdentifier(),
|
||||||
connectionGroupDefaultMaxConnections))
|
connectionGroupMaxConnections))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Acquire failed
|
// Acquire failed
|
||||||
|
@@ -25,9 +25,7 @@ package org.glyptodon.guacamole.auth.jdbc.user;
|
|||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
import java.sql.Date;
|
import java.sql.Date;
|
||||||
import java.sql.Time;
|
import java.sql.Time;
|
||||||
import java.text.DateFormat;
|
|
||||||
import java.text.ParseException;
|
import java.text.ParseException;
|
||||||
import java.text.SimpleDateFormat;
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Calendar;
|
import java.util.Calendar;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
@@ -268,40 +266,39 @@ public class ModeledUser extends ModeledDirectoryObject<UserModel> implements Us
|
|||||||
return userPermissionService.getPermissionSet(getCurrentUser(), this);
|
return userPermissionService.getPermissionSet(getCurrentUser(), this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Converts the given date into a string which follows the format used by
|
public Map<String, String> getAttributes() {
|
||||||
* date attributes.
|
|
||||||
*
|
|
||||||
* @param date
|
|
||||||
* The date value to format, which may be null.
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
* The formatted date, or null if the provided time was null.
|
|
||||||
*/
|
|
||||||
private String formatDate(Date date) {
|
|
||||||
DateFormat dateFormat = new SimpleDateFormat(DateField.FORMAT);
|
|
||||||
return date == null ? null : dateFormat.format(date);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
Map<String, String> attributes = new HashMap<String, String>();
|
||||||
* Converts the given time into a string which follows the format used by
|
|
||||||
* time attributes.
|
// Set disabled attribute
|
||||||
*
|
attributes.put(DISABLED_ATTRIBUTE_NAME, getModel().isDisabled() ? "true" : null);
|
||||||
* @param time
|
|
||||||
* The time value to format, which may be null.
|
// Set password expired attribute
|
||||||
*
|
attributes.put(EXPIRED_ATTRIBUTE_NAME, getModel().isExpired() ? "true" : null);
|
||||||
* @return
|
|
||||||
* The formatted time, or null if the provided time was null.
|
// Set access window start time
|
||||||
*/
|
attributes.put(ACCESS_WINDOW_START_ATTRIBUTE_NAME, TimeField.format(getModel().getAccessWindowStart()));
|
||||||
private String formatTime(Time time) {
|
|
||||||
DateFormat timeFormat = new SimpleDateFormat(TimeField.FORMAT);
|
// Set access window end time
|
||||||
return time == null ? null : timeFormat.format(time);
|
attributes.put(ACCESS_WINDOW_END_ATTRIBUTE_NAME, TimeField.format(getModel().getAccessWindowEnd()));
|
||||||
|
|
||||||
|
// Set account validity start date
|
||||||
|
attributes.put(VALID_FROM_ATTRIBUTE_NAME, DateField.format(getModel().getValidFrom()));
|
||||||
|
|
||||||
|
// Set account validity end date
|
||||||
|
attributes.put(VALID_UNTIL_ATTRIBUTE_NAME, DateField.format(getModel().getValidUntil()));
|
||||||
|
|
||||||
|
// Set timezone attribute
|
||||||
|
attributes.put(TIMEZONE_ATTRIBUTE_NAME, getModel().getTimeZone());
|
||||||
|
|
||||||
|
return attributes;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses the given string into a corresponding date. The string must
|
* Parses the given string into a corresponding date. The string must
|
||||||
* follow the standard format used by date attributes, as defined by
|
* follow the standard format used by date attributes, as defined by
|
||||||
* DATE_FORMAT and as would be produced by formatDate().
|
* DateField.FORMAT and as would be produced by DateField.format().
|
||||||
*
|
*
|
||||||
* @param dateString
|
* @param dateString
|
||||||
* The date string to parse, which may be null.
|
* The date string to parse, which may be null.
|
||||||
@@ -318,19 +315,19 @@ public class ModeledUser extends ModeledDirectoryObject<UserModel> implements Us
|
|||||||
throws ParseException {
|
throws ParseException {
|
||||||
|
|
||||||
// Return null if no date provided
|
// Return null if no date provided
|
||||||
if (dateString == null || dateString.isEmpty())
|
java.util.Date parsedDate = DateField.parse(dateString);
|
||||||
|
if (parsedDate == null)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
// Parse date according to format
|
// Convert to SQL Date
|
||||||
DateFormat dateFormat = new SimpleDateFormat(DateField.FORMAT);
|
return new Date(parsedDate.getTime());
|
||||||
return new Date(dateFormat.parse(dateString).getTime());
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses the given string into a corresponding time. The string must
|
* Parses the given string into a corresponding time. The string must
|
||||||
* follow the standard format used by time attributes, as defined by
|
* follow the standard format used by time attributes, as defined by
|
||||||
* TIME_FORMAT and as would be produced by formatTime().
|
* TimeField.FORMAT and as would be produced by TimeField.format().
|
||||||
*
|
*
|
||||||
* @param timeString
|
* @param timeString
|
||||||
* The time string to parse, which may be null.
|
* The time string to parse, which may be null.
|
||||||
@@ -347,67 +344,15 @@ public class ModeledUser extends ModeledDirectoryObject<UserModel> implements Us
|
|||||||
throws ParseException {
|
throws ParseException {
|
||||||
|
|
||||||
// Return null if no time provided
|
// Return null if no time provided
|
||||||
if (timeString == null || timeString.isEmpty())
|
java.util.Date parsedDate = TimeField.parse(timeString);
|
||||||
|
if (parsedDate == null)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
// Parse time according to format
|
// Convert to SQL Time
|
||||||
DateFormat timeFormat = new SimpleDateFormat(TimeField.FORMAT);
|
return new Time(parsedDate.getTime());
|
||||||
return new Time(timeFormat.parse(timeString).getTime());
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Parses the given string into a time zone ID string. As these strings are
|
|
||||||
* equivalent, the only transformation currently performed by this function
|
|
||||||
* is to ensure that a blank time zone string is parsed into null.
|
|
||||||
*
|
|
||||||
* @param timeZone
|
|
||||||
* The time zone string to parse, which may be null.
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
* The ID of the time zone corresponding to the given string, or null
|
|
||||||
* if the given time zone string was null or blank.
|
|
||||||
*/
|
|
||||||
private String parseTimeZone(String timeZone) {
|
|
||||||
|
|
||||||
// Return null if no time zone provided
|
|
||||||
if (timeZone == null || timeZone.isEmpty())
|
|
||||||
return null;
|
|
||||||
|
|
||||||
// Otherwise, assume time zone is valid
|
|
||||||
return timeZone;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Map<String, String> getAttributes() {
|
|
||||||
|
|
||||||
Map<String, String> attributes = new HashMap<String, String>();
|
|
||||||
|
|
||||||
// Set disabled attribute
|
|
||||||
attributes.put(DISABLED_ATTRIBUTE_NAME, getModel().isDisabled() ? "true" : null);
|
|
||||||
|
|
||||||
// Set password expired attribute
|
|
||||||
attributes.put(EXPIRED_ATTRIBUTE_NAME, getModel().isExpired() ? "true" : null);
|
|
||||||
|
|
||||||
// Set access window start time
|
|
||||||
attributes.put(ACCESS_WINDOW_START_ATTRIBUTE_NAME, formatTime(getModel().getAccessWindowStart()));
|
|
||||||
|
|
||||||
// Set access window end time
|
|
||||||
attributes.put(ACCESS_WINDOW_END_ATTRIBUTE_NAME, formatTime(getModel().getAccessWindowEnd()));
|
|
||||||
|
|
||||||
// Set account validity start date
|
|
||||||
attributes.put(VALID_FROM_ATTRIBUTE_NAME, formatDate(getModel().getValidFrom()));
|
|
||||||
|
|
||||||
// Set account validity end date
|
|
||||||
attributes.put(VALID_UNTIL_ATTRIBUTE_NAME, formatDate(getModel().getValidUntil()));
|
|
||||||
|
|
||||||
// Set timezone attribute
|
|
||||||
attributes.put(TIMEZONE_ATTRIBUTE_NAME, getModel().getTimeZone());
|
|
||||||
|
|
||||||
return attributes;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setAttributes(Map<String, String> attributes) {
|
public void setAttributes(Map<String, String> attributes) {
|
||||||
|
|
||||||
@@ -446,7 +391,7 @@ public class ModeledUser extends ModeledDirectoryObject<UserModel> implements Us
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Translate timezone attribute
|
// Translate timezone attribute
|
||||||
getModel().setTimeZone(parseTimeZone(attributes.get(TIMEZONE_ATTRIBUTE_NAME)));
|
getModel().setTimeZone(TimeZoneField.parse(attributes.get(TIMEZONE_ATTRIBUTE_NAME)));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -29,10 +29,11 @@ import org.glyptodon.guacamole.auth.jdbc.connection.ConnectionDirectory;
|
|||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
import com.google.inject.Provider;
|
import com.google.inject.Provider;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
|
||||||
import org.glyptodon.guacamole.GuacamoleException;
|
import org.glyptodon.guacamole.GuacamoleException;
|
||||||
import org.glyptodon.guacamole.auth.jdbc.base.RestrictedObject;
|
import org.glyptodon.guacamole.auth.jdbc.base.RestrictedObject;
|
||||||
import org.glyptodon.guacamole.auth.jdbc.activeconnection.ActiveConnectionDirectory;
|
import org.glyptodon.guacamole.auth.jdbc.activeconnection.ActiveConnectionDirectory;
|
||||||
|
import org.glyptodon.guacamole.auth.jdbc.connection.ModeledConnection;
|
||||||
|
import org.glyptodon.guacamole.auth.jdbc.connectiongroup.ModeledConnectionGroup;
|
||||||
import org.glyptodon.guacamole.form.Form;
|
import org.glyptodon.guacamole.form.Form;
|
||||||
import org.glyptodon.guacamole.net.auth.ActiveConnection;
|
import org.glyptodon.guacamole.net.auth.ActiveConnection;
|
||||||
import org.glyptodon.guacamole.net.auth.Connection;
|
import org.glyptodon.guacamole.net.auth.Connection;
|
||||||
@@ -140,12 +141,12 @@ public class UserContext extends RestrictedObject
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Collection<Form> getConnectionAttributes() {
|
public Collection<Form> getConnectionAttributes() {
|
||||||
return Collections.<Form>emptyList();
|
return ModeledConnection.ATTRIBUTES;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Collection<Form> getConnectionGroupAttributes() {
|
public Collection<Form> getConnectionGroupAttributes() {
|
||||||
return Collections.<Form>emptyList();
|
return ModeledConnectionGroup.ATTRIBUTES;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -15,6 +15,24 @@
|
|||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"CONNECTION_ATTRIBUTES" : {
|
||||||
|
|
||||||
|
"FIELD_HEADER_MAX_CONNECTIONS" : "Maximum number of connections:",
|
||||||
|
"FIELD_HEADER_MAX_CONNECTIONS_PER_USER" : "Maximum number of connections per user:",
|
||||||
|
|
||||||
|
"SECTION_HEADER_CONCURRENCY" : "Concurrency Limits"
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
"CONNECTION_GROUP_ATTRIBUTES" : {
|
||||||
|
|
||||||
|
"FIELD_HEADER_MAX_CONNECTIONS" : "Maximum number of connections:",
|
||||||
|
"FIELD_HEADER_MAX_CONNECTIONS_PER_USER" : "Maximum number of connections per user:",
|
||||||
|
|
||||||
|
"SECTION_HEADER_CONCURRENCY" : "Concurrency Limits (Balancing Groups)"
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
"USER_ATTRIBUTES" : {
|
"USER_ATTRIBUTES" : {
|
||||||
|
|
||||||
"FIELD_HEADER_DISABLED" : "Login disabled:",
|
"FIELD_HEADER_DISABLED" : "Login disabled:",
|
||||||
|
@@ -32,6 +32,10 @@ CREATE TABLE `guacamole_connection_group` (
|
|||||||
`type` enum('ORGANIZATIONAL',
|
`type` enum('ORGANIZATIONAL',
|
||||||
'BALANCING') NOT NULL DEFAULT 'ORGANIZATIONAL',
|
'BALANCING') NOT NULL DEFAULT 'ORGANIZATIONAL',
|
||||||
|
|
||||||
|
-- Concurrency limits
|
||||||
|
`max_connections` int(11),
|
||||||
|
`max_connections_per_user` int(11),
|
||||||
|
|
||||||
PRIMARY KEY (`connection_group_id`),
|
PRIMARY KEY (`connection_group_id`),
|
||||||
UNIQUE KEY `connection_group_name_parent` (`connection_group_name`, `parent_id`),
|
UNIQUE KEY `connection_group_name_parent` (`connection_group_name`, `parent_id`),
|
||||||
|
|
||||||
@@ -54,6 +58,10 @@ CREATE TABLE `guacamole_connection` (
|
|||||||
`parent_id` int(11),
|
`parent_id` int(11),
|
||||||
`protocol` varchar(32) NOT NULL,
|
`protocol` varchar(32) NOT NULL,
|
||||||
|
|
||||||
|
-- Concurrency limits
|
||||||
|
`max_connections` int(11),
|
||||||
|
`max_connections_per_user` int(11),
|
||||||
|
|
||||||
PRIMARY KEY (`connection_id`),
|
PRIMARY KEY (`connection_id`),
|
||||||
UNIQUE KEY `connection_name_parent` (`connection_name`, `parent_id`),
|
UNIQUE KEY `connection_name_parent` (`connection_name`, `parent_id`),
|
||||||
|
|
||||||
|
@@ -39,3 +39,17 @@ ALTER TABLE guacamole_user ADD COLUMN valid_until DATE;
|
|||||||
--
|
--
|
||||||
|
|
||||||
ALTER TABLE guacamole_user ADD COLUMN timezone VARCHAR(64);
|
ALTER TABLE guacamole_user ADD COLUMN timezone VARCHAR(64);
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Add connection concurrency limits
|
||||||
|
--
|
||||||
|
|
||||||
|
ALTER TABLE guacamole_connection ADD COLUMN max_connections INT(11);
|
||||||
|
ALTER TABLE guacamole_connection ADD COLUMN max_connections_per_user INT(11);
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Add connection group concurrency limits
|
||||||
|
--
|
||||||
|
|
||||||
|
ALTER TABLE guacamole_connection_group ADD COLUMN max_connections INT(11);
|
||||||
|
ALTER TABLE guacamole_connection_group ADD COLUMN max_connections_per_user INT(11);
|
||||||
|
@@ -32,6 +32,8 @@
|
|||||||
<result column="connection_name" property="name" jdbcType="VARCHAR"/>
|
<result column="connection_name" property="name" jdbcType="VARCHAR"/>
|
||||||
<result column="parent_id" property="parentIdentifier" jdbcType="INTEGER"/>
|
<result column="parent_id" property="parentIdentifier" jdbcType="INTEGER"/>
|
||||||
<result column="protocol" property="protocol" jdbcType="VARCHAR"/>
|
<result column="protocol" property="protocol" jdbcType="VARCHAR"/>
|
||||||
|
<result column="max_connections" property="maxConnections" jdbcType="INTEGER"/>
|
||||||
|
<result column="max_connections_per_user" property="maxConnectionsPerUser" jdbcType="INTEGER"/>
|
||||||
</resultMap>
|
</resultMap>
|
||||||
|
|
||||||
<!-- Select all connection identifiers -->
|
<!-- Select all connection identifiers -->
|
||||||
@@ -77,7 +79,9 @@
|
|||||||
connection_id,
|
connection_id,
|
||||||
connection_name,
|
connection_name,
|
||||||
parent_id,
|
parent_id,
|
||||||
protocol
|
protocol,
|
||||||
|
max_connections,
|
||||||
|
max_connections_per_user
|
||||||
FROM guacamole_connection
|
FROM guacamole_connection
|
||||||
WHERE connection_id IN
|
WHERE connection_id IN
|
||||||
<foreach collection="identifiers" item="identifier"
|
<foreach collection="identifiers" item="identifier"
|
||||||
@@ -94,7 +98,9 @@
|
|||||||
guacamole_connection.connection_id,
|
guacamole_connection.connection_id,
|
||||||
connection_name,
|
connection_name,
|
||||||
parent_id,
|
parent_id,
|
||||||
protocol
|
protocol,
|
||||||
|
max_connections,
|
||||||
|
max_connections_per_user
|
||||||
FROM guacamole_connection
|
FROM guacamole_connection
|
||||||
JOIN guacamole_connection_permission ON guacamole_connection_permission.connection_id = guacamole_connection.connection_id
|
JOIN guacamole_connection_permission ON guacamole_connection_permission.connection_id = guacamole_connection.connection_id
|
||||||
WHERE guacamole_connection.connection_id IN
|
WHERE guacamole_connection.connection_id IN
|
||||||
@@ -114,7 +120,9 @@
|
|||||||
connection_id,
|
connection_id,
|
||||||
connection_name,
|
connection_name,
|
||||||
parent_id,
|
parent_id,
|
||||||
protocol
|
protocol,
|
||||||
|
max_connections,
|
||||||
|
max_connections_per_user
|
||||||
FROM guacamole_connection
|
FROM guacamole_connection
|
||||||
WHERE
|
WHERE
|
||||||
<if test="parentIdentifier != null">parent_id = #{parentIdentifier,jdbcType=VARCHAR}</if>
|
<if test="parentIdentifier != null">parent_id = #{parentIdentifier,jdbcType=VARCHAR}</if>
|
||||||
@@ -136,12 +144,16 @@
|
|||||||
INSERT INTO guacamole_connection (
|
INSERT INTO guacamole_connection (
|
||||||
connection_name,
|
connection_name,
|
||||||
parent_id,
|
parent_id,
|
||||||
protocol
|
protocol,
|
||||||
|
max_connections,
|
||||||
|
max_connections_per_user
|
||||||
)
|
)
|
||||||
VALUES (
|
VALUES (
|
||||||
#{object.name,jdbcType=VARCHAR},
|
#{object.name,jdbcType=VARCHAR},
|
||||||
#{object.parentIdentifier,jdbcType=VARCHAR},
|
#{object.parentIdentifier,jdbcType=VARCHAR},
|
||||||
#{object.protocol,jdbcType=VARCHAR}
|
#{object.protocol,jdbcType=VARCHAR},
|
||||||
|
#{object.maxConnections,jdbcType=INTEGER},
|
||||||
|
#{object.maxConnectionsPerUser,jdbcType=INTEGER}
|
||||||
)
|
)
|
||||||
|
|
||||||
</insert>
|
</insert>
|
||||||
@@ -151,7 +163,9 @@
|
|||||||
UPDATE guacamole_connection
|
UPDATE guacamole_connection
|
||||||
SET connection_name = #{object.name,jdbcType=VARCHAR},
|
SET connection_name = #{object.name,jdbcType=VARCHAR},
|
||||||
parent_id = #{object.parentIdentifier,jdbcType=VARCHAR},
|
parent_id = #{object.parentIdentifier,jdbcType=VARCHAR},
|
||||||
protocol = #{object.protocol,jdbcType=VARCHAR}
|
protocol = #{object.protocol,jdbcType=VARCHAR},
|
||||||
|
max_connections = #{object.maxConnections,jdbcType=INTEGER},
|
||||||
|
max_connections_per_user = #{object.maxConnectionsPerUser,jdbcType=INTEGER}
|
||||||
WHERE connection_id = #{object.objectID,jdbcType=INTEGER}
|
WHERE connection_id = #{object.objectID,jdbcType=INTEGER}
|
||||||
</update>
|
</update>
|
||||||
|
|
||||||
|
@@ -33,6 +33,8 @@
|
|||||||
<result column="parent_id" property="parentIdentifier" jdbcType="INTEGER"/>
|
<result column="parent_id" property="parentIdentifier" jdbcType="INTEGER"/>
|
||||||
<result column="type" property="type" jdbcType="VARCHAR"
|
<result column="type" property="type" jdbcType="VARCHAR"
|
||||||
javaType="org.glyptodon.guacamole.net.auth.ConnectionGroup$Type"/>
|
javaType="org.glyptodon.guacamole.net.auth.ConnectionGroup$Type"/>
|
||||||
|
<result column="max_connections" property="maxConnections" jdbcType="INTEGER"/>
|
||||||
|
<result column="max_connections_per_user" property="maxConnectionsPerUser" jdbcType="INTEGER"/>
|
||||||
</resultMap>
|
</resultMap>
|
||||||
|
|
||||||
<!-- Select all connection group identifiers -->
|
<!-- Select all connection group identifiers -->
|
||||||
@@ -78,7 +80,9 @@
|
|||||||
connection_group_id,
|
connection_group_id,
|
||||||
connection_group_name,
|
connection_group_name,
|
||||||
parent_id,
|
parent_id,
|
||||||
type
|
type,
|
||||||
|
max_connections,
|
||||||
|
max_connections_per_user
|
||||||
FROM guacamole_connection_group
|
FROM guacamole_connection_group
|
||||||
WHERE connection_group_id IN
|
WHERE connection_group_id IN
|
||||||
<foreach collection="identifiers" item="identifier"
|
<foreach collection="identifiers" item="identifier"
|
||||||
@@ -95,7 +99,9 @@
|
|||||||
guacamole_connection_group.connection_group_id,
|
guacamole_connection_group.connection_group_id,
|
||||||
connection_group_name,
|
connection_group_name,
|
||||||
parent_id,
|
parent_id,
|
||||||
type
|
type,
|
||||||
|
max_connections,
|
||||||
|
max_connections_per_user
|
||||||
FROM guacamole_connection_group
|
FROM guacamole_connection_group
|
||||||
JOIN guacamole_connection_group_permission ON guacamole_connection_group_permission.connection_group_id = guacamole_connection_group.connection_group_id
|
JOIN guacamole_connection_group_permission ON guacamole_connection_group_permission.connection_group_id = guacamole_connection_group.connection_group_id
|
||||||
WHERE guacamole_connection_group.connection_group_id IN
|
WHERE guacamole_connection_group.connection_group_id IN
|
||||||
@@ -115,7 +121,9 @@
|
|||||||
connection_group_id,
|
connection_group_id,
|
||||||
connection_group_name,
|
connection_group_name,
|
||||||
parent_id,
|
parent_id,
|
||||||
type
|
type,
|
||||||
|
max_connections,
|
||||||
|
max_connections_per_user
|
||||||
FROM guacamole_connection_group
|
FROM guacamole_connection_group
|
||||||
WHERE
|
WHERE
|
||||||
<if test="parentIdentifier != null">parent_id = #{parentIdentifier,jdbcType=VARCHAR}</if>
|
<if test="parentIdentifier != null">parent_id = #{parentIdentifier,jdbcType=VARCHAR}</if>
|
||||||
@@ -137,12 +145,16 @@
|
|||||||
INSERT INTO guacamole_connection_group (
|
INSERT INTO guacamole_connection_group (
|
||||||
connection_group_name,
|
connection_group_name,
|
||||||
parent_id,
|
parent_id,
|
||||||
type
|
type,
|
||||||
|
max_connections,
|
||||||
|
max_connections_per_user
|
||||||
)
|
)
|
||||||
VALUES (
|
VALUES (
|
||||||
#{object.name,jdbcType=VARCHAR},
|
#{object.name,jdbcType=VARCHAR},
|
||||||
#{object.parentIdentifier,jdbcType=VARCHAR},
|
#{object.parentIdentifier,jdbcType=VARCHAR},
|
||||||
#{object.type,jdbcType=VARCHAR}
|
#{object.type,jdbcType=VARCHAR},
|
||||||
|
#{object.maxConnections,jdbcType=INTEGER},
|
||||||
|
#{object.maxConnectionsPerUser,jdbcType=INTEGER}
|
||||||
)
|
)
|
||||||
|
|
||||||
</insert>
|
</insert>
|
||||||
@@ -152,7 +164,9 @@
|
|||||||
UPDATE guacamole_connection_group
|
UPDATE guacamole_connection_group
|
||||||
SET connection_group_name = #{object.name,jdbcType=VARCHAR},
|
SET connection_group_name = #{object.name,jdbcType=VARCHAR},
|
||||||
parent_id = #{object.parentIdentifier,jdbcType=VARCHAR},
|
parent_id = #{object.parentIdentifier,jdbcType=VARCHAR},
|
||||||
type = #{object.type,jdbcType=VARCHAR}
|
type = #{object.type,jdbcType=VARCHAR},
|
||||||
|
max_connections = #{object.maxConnections,jdbcType=INTEGER},
|
||||||
|
max_connections_per_user = #{object.maxConnectionsPerUser,jdbcType=INTEGER}
|
||||||
WHERE connection_group_id = #{object.objectID,jdbcType=INTEGER}
|
WHERE connection_group_id = #{object.objectID,jdbcType=INTEGER}
|
||||||
</update>
|
</update>
|
||||||
|
|
||||||
|
@@ -63,6 +63,10 @@ CREATE TABLE guacamole_connection_group (
|
|||||||
type guacamole_connection_group_type
|
type guacamole_connection_group_type
|
||||||
NOT NULL DEFAULT 'ORGANIZATIONAL',
|
NOT NULL DEFAULT 'ORGANIZATIONAL',
|
||||||
|
|
||||||
|
-- Concurrency limits
|
||||||
|
max_connections integer,
|
||||||
|
max_connections_per_user integer,
|
||||||
|
|
||||||
PRIMARY KEY (connection_group_id),
|
PRIMARY KEY (connection_group_id),
|
||||||
|
|
||||||
CONSTRAINT connection_group_name_parent
|
CONSTRAINT connection_group_name_parent
|
||||||
@@ -90,6 +94,10 @@ CREATE TABLE guacamole_connection (
|
|||||||
parent_id integer,
|
parent_id integer,
|
||||||
protocol varchar(32) NOT NULL,
|
protocol varchar(32) NOT NULL,
|
||||||
|
|
||||||
|
-- Concurrency limits
|
||||||
|
max_connections integer,
|
||||||
|
max_connections_per_user integer,
|
||||||
|
|
||||||
PRIMARY KEY (connection_id),
|
PRIMARY KEY (connection_id),
|
||||||
|
|
||||||
CONSTRAINT connection_name_parent
|
CONSTRAINT connection_name_parent
|
||||||
|
@@ -39,3 +39,17 @@ ALTER TABLE guacamole_user ADD COLUMN valid_until date;
|
|||||||
--
|
--
|
||||||
|
|
||||||
ALTER TABLE guacamole_user ADD COLUMN timezone varchar(64);
|
ALTER TABLE guacamole_user ADD COLUMN timezone varchar(64);
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Add connection concurrency limits
|
||||||
|
--
|
||||||
|
|
||||||
|
ALTER TABLE guacamole_connection ADD COLUMN max_connections integer;
|
||||||
|
ALTER TABLE guacamole_connection ADD COLUMN max_connections_per_user integer;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Add connection group concurrency limits
|
||||||
|
--
|
||||||
|
|
||||||
|
ALTER TABLE guacamole_connection_group ADD COLUMN max_connections integer;
|
||||||
|
ALTER TABLE guacamole_connection_group ADD COLUMN max_connections_per_user integer;
|
||||||
|
@@ -32,6 +32,8 @@
|
|||||||
<result column="connection_name" property="name" jdbcType="VARCHAR"/>
|
<result column="connection_name" property="name" jdbcType="VARCHAR"/>
|
||||||
<result column="parent_id" property="parentIdentifier" jdbcType="INTEGER"/>
|
<result column="parent_id" property="parentIdentifier" jdbcType="INTEGER"/>
|
||||||
<result column="protocol" property="protocol" jdbcType="VARCHAR"/>
|
<result column="protocol" property="protocol" jdbcType="VARCHAR"/>
|
||||||
|
<result column="max_connections" property="maxConnections" jdbcType="INTEGER"/>
|
||||||
|
<result column="max_connections_per_user" property="maxConnectionsPerUser" jdbcType="INTEGER"/>
|
||||||
</resultMap>
|
</resultMap>
|
||||||
|
|
||||||
<!-- Select all connection identifiers -->
|
<!-- Select all connection identifiers -->
|
||||||
@@ -77,7 +79,9 @@
|
|||||||
connection_id,
|
connection_id,
|
||||||
connection_name,
|
connection_name,
|
||||||
parent_id,
|
parent_id,
|
||||||
protocol
|
protocol,
|
||||||
|
max_connections,
|
||||||
|
max_connections_per_user
|
||||||
FROM guacamole_connection
|
FROM guacamole_connection
|
||||||
WHERE connection_id IN
|
WHERE connection_id IN
|
||||||
<foreach collection="identifiers" item="identifier"
|
<foreach collection="identifiers" item="identifier"
|
||||||
@@ -94,7 +98,9 @@
|
|||||||
guacamole_connection.connection_id,
|
guacamole_connection.connection_id,
|
||||||
connection_name,
|
connection_name,
|
||||||
parent_id,
|
parent_id,
|
||||||
protocol
|
protocol,
|
||||||
|
max_connections,
|
||||||
|
max_connections_per_user
|
||||||
FROM guacamole_connection
|
FROM guacamole_connection
|
||||||
JOIN guacamole_connection_permission ON guacamole_connection_permission.connection_id = guacamole_connection.connection_id
|
JOIN guacamole_connection_permission ON guacamole_connection_permission.connection_id = guacamole_connection.connection_id
|
||||||
WHERE guacamole_connection.connection_id IN
|
WHERE guacamole_connection.connection_id IN
|
||||||
@@ -114,7 +120,9 @@
|
|||||||
connection_id,
|
connection_id,
|
||||||
connection_name,
|
connection_name,
|
||||||
parent_id,
|
parent_id,
|
||||||
protocol
|
protocol,
|
||||||
|
max_connections,
|
||||||
|
max_connections_per_user
|
||||||
FROM guacamole_connection
|
FROM guacamole_connection
|
||||||
WHERE
|
WHERE
|
||||||
<if test="parentIdentifier != null">parent_id = #{parentIdentifier,jdbcType=INTEGER}::integer</if>
|
<if test="parentIdentifier != null">parent_id = #{parentIdentifier,jdbcType=INTEGER}::integer</if>
|
||||||
@@ -136,12 +144,16 @@
|
|||||||
INSERT INTO guacamole_connection (
|
INSERT INTO guacamole_connection (
|
||||||
connection_name,
|
connection_name,
|
||||||
parent_id,
|
parent_id,
|
||||||
protocol
|
protocol,
|
||||||
|
max_connections,
|
||||||
|
max_connections_per_user
|
||||||
)
|
)
|
||||||
VALUES (
|
VALUES (
|
||||||
#{object.name,jdbcType=VARCHAR},
|
#{object.name,jdbcType=VARCHAR},
|
||||||
#{object.parentIdentifier,jdbcType=INTEGER}::integer,
|
#{object.parentIdentifier,jdbcType=INTEGER}::integer,
|
||||||
#{object.protocol,jdbcType=VARCHAR}
|
#{object.protocol,jdbcType=VARCHAR},
|
||||||
|
#{object.maxConnections,jdbcType=INTEGER},
|
||||||
|
#{object.maxConnectionsPerUser,jdbcType=INTEGER}
|
||||||
)
|
)
|
||||||
|
|
||||||
</insert>
|
</insert>
|
||||||
@@ -151,7 +163,9 @@
|
|||||||
UPDATE guacamole_connection
|
UPDATE guacamole_connection
|
||||||
SET connection_name = #{object.name,jdbcType=VARCHAR},
|
SET connection_name = #{object.name,jdbcType=VARCHAR},
|
||||||
parent_id = #{object.parentIdentifier,jdbcType=INTEGER}::integer,
|
parent_id = #{object.parentIdentifier,jdbcType=INTEGER}::integer,
|
||||||
protocol = #{object.protocol,jdbcType=VARCHAR}
|
protocol = #{object.protocol,jdbcType=VARCHAR},
|
||||||
|
max_connections = #{object.maxConnections,jdbcType=INTEGER},
|
||||||
|
max_connections_per_user = #{object.maxConnectionsPerUser,jdbcType=INTEGER}
|
||||||
WHERE connection_id = #{object.objectID,jdbcType=INTEGER}::integer
|
WHERE connection_id = #{object.objectID,jdbcType=INTEGER}::integer
|
||||||
</update>
|
</update>
|
||||||
|
|
||||||
|
@@ -33,6 +33,8 @@
|
|||||||
<result column="parent_id" property="parentIdentifier" jdbcType="INTEGER"/>
|
<result column="parent_id" property="parentIdentifier" jdbcType="INTEGER"/>
|
||||||
<result column="type" property="type" jdbcType="VARCHAR"
|
<result column="type" property="type" jdbcType="VARCHAR"
|
||||||
javaType="org.glyptodon.guacamole.net.auth.ConnectionGroup$Type"/>
|
javaType="org.glyptodon.guacamole.net.auth.ConnectionGroup$Type"/>
|
||||||
|
<result column="max_connections" property="maxConnections" jdbcType="INTEGER"/>
|
||||||
|
<result column="max_connections_per_user" property="maxConnectionsPerUser" jdbcType="INTEGER"/>
|
||||||
</resultMap>
|
</resultMap>
|
||||||
|
|
||||||
<!-- Select all connection group identifiers -->
|
<!-- Select all connection group identifiers -->
|
||||||
@@ -78,7 +80,9 @@
|
|||||||
connection_group_id,
|
connection_group_id,
|
||||||
connection_group_name,
|
connection_group_name,
|
||||||
parent_id,
|
parent_id,
|
||||||
type
|
type,
|
||||||
|
max_connections,
|
||||||
|
max_connections_per_user
|
||||||
FROM guacamole_connection_group
|
FROM guacamole_connection_group
|
||||||
WHERE connection_group_id IN
|
WHERE connection_group_id IN
|
||||||
<foreach collection="identifiers" item="identifier"
|
<foreach collection="identifiers" item="identifier"
|
||||||
@@ -95,7 +99,9 @@
|
|||||||
guacamole_connection_group.connection_group_id,
|
guacamole_connection_group.connection_group_id,
|
||||||
connection_group_name,
|
connection_group_name,
|
||||||
parent_id,
|
parent_id,
|
||||||
type
|
type,
|
||||||
|
max_connections,
|
||||||
|
max_connections_per_user
|
||||||
FROM guacamole_connection_group
|
FROM guacamole_connection_group
|
||||||
JOIN guacamole_connection_group_permission ON guacamole_connection_group_permission.connection_group_id = guacamole_connection_group.connection_group_id
|
JOIN guacamole_connection_group_permission ON guacamole_connection_group_permission.connection_group_id = guacamole_connection_group.connection_group_id
|
||||||
WHERE guacamole_connection_group.connection_group_id IN
|
WHERE guacamole_connection_group.connection_group_id IN
|
||||||
@@ -115,7 +121,9 @@
|
|||||||
connection_group_id,
|
connection_group_id,
|
||||||
connection_group_name,
|
connection_group_name,
|
||||||
parent_id,
|
parent_id,
|
||||||
type
|
type,
|
||||||
|
max_connections,
|
||||||
|
max_connections_per_user
|
||||||
FROM guacamole_connection_group
|
FROM guacamole_connection_group
|
||||||
WHERE
|
WHERE
|
||||||
<if test="parentIdentifier != null">parent_id = #{parentIdentifier,jdbcType=INTEGER}::integer</if>
|
<if test="parentIdentifier != null">parent_id = #{parentIdentifier,jdbcType=INTEGER}::integer</if>
|
||||||
@@ -137,12 +145,16 @@
|
|||||||
INSERT INTO guacamole_connection_group (
|
INSERT INTO guacamole_connection_group (
|
||||||
connection_group_name,
|
connection_group_name,
|
||||||
parent_id,
|
parent_id,
|
||||||
type
|
type,
|
||||||
|
max_connections,
|
||||||
|
max_connections_per_user
|
||||||
)
|
)
|
||||||
VALUES (
|
VALUES (
|
||||||
#{object.name,jdbcType=VARCHAR},
|
#{object.name,jdbcType=VARCHAR},
|
||||||
#{object.parentIdentifier,jdbcType=INTEGER}::integer,
|
#{object.parentIdentifier,jdbcType=INTEGER}::integer,
|
||||||
#{object.type,jdbcType=VARCHAR}::guacamole_connection_group_type
|
#{object.type,jdbcType=VARCHAR}::guacamole_connection_group_type,
|
||||||
|
#{object.maxConnections,jdbcType=INTEGER},
|
||||||
|
#{object.maxConnectionsPerUser,jdbcType=INTEGER}
|
||||||
)
|
)
|
||||||
|
|
||||||
</insert>
|
</insert>
|
||||||
@@ -152,7 +164,9 @@
|
|||||||
UPDATE guacamole_connection_group
|
UPDATE guacamole_connection_group
|
||||||
SET connection_group_name = #{object.name,jdbcType=VARCHAR},
|
SET connection_group_name = #{object.name,jdbcType=VARCHAR},
|
||||||
parent_id = #{object.parentIdentifier,jdbcType=INTEGER}::integer,
|
parent_id = #{object.parentIdentifier,jdbcType=INTEGER}::integer,
|
||||||
type = #{object.type,jdbcType=VARCHAR}::guacamole_connection_group_type
|
type = #{object.type,jdbcType=VARCHAR}::guacamole_connection_group_type,
|
||||||
|
max_connections = #{object.maxConnections,jdbcType=INTEGER},
|
||||||
|
max_connections_per_user = #{object.maxConnectionsPerUser,jdbcType=INTEGER}
|
||||||
WHERE connection_group_id = #{object.objectID,jdbcType=INTEGER}::integer
|
WHERE connection_group_id = #{object.objectID,jdbcType=INTEGER}::integer
|
||||||
</update>
|
</update>
|
||||||
|
|
||||||
|
@@ -22,6 +22,11 @@
|
|||||||
|
|
||||||
package org.glyptodon.guacamole.form;
|
package org.glyptodon.guacamole.form;
|
||||||
|
|
||||||
|
import java.text.DateFormat;
|
||||||
|
import java.text.ParseException;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a date field. The field may contain only date values which
|
* Represents a date field. The field may contain only date values which
|
||||||
* conform to a standard pattern, defined by DateField.FORMAT.
|
* conform to a standard pattern, defined by DateField.FORMAT.
|
||||||
@@ -45,4 +50,48 @@ public class DateField extends Field {
|
|||||||
super(name, Field.Type.DATE);
|
super(name, Field.Type.DATE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts the given date into a string which follows the format used by
|
||||||
|
* date fields.
|
||||||
|
*
|
||||||
|
* @param date
|
||||||
|
* The date value to format, which may be null.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* The formatted date, or null if the provided time was null.
|
||||||
|
*/
|
||||||
|
public static String format(Date date) {
|
||||||
|
DateFormat dateFormat = new SimpleDateFormat(DateField.FORMAT);
|
||||||
|
return date == null ? null : dateFormat.format(date);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses the given string into a corresponding date. The string must
|
||||||
|
* follow the standard format used by date fields, as defined by FORMAT
|
||||||
|
* and as would be produced by format().
|
||||||
|
*
|
||||||
|
* @param dateString
|
||||||
|
* The date string to parse, which may be null.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* The date corresponding to the given date string, or null if the
|
||||||
|
* provided date string was null or blank.
|
||||||
|
*
|
||||||
|
* @throws ParseException
|
||||||
|
* If the given date string does not conform to the standard format
|
||||||
|
* used by date fields.
|
||||||
|
*/
|
||||||
|
public static Date parse(String dateString)
|
||||||
|
throws ParseException {
|
||||||
|
|
||||||
|
// Return null if no date provided
|
||||||
|
if (dateString == null || dateString.isEmpty())
|
||||||
|
return null;
|
||||||
|
|
||||||
|
// Parse date according to format
|
||||||
|
DateFormat dateFormat = new SimpleDateFormat(DateField.FORMAT);
|
||||||
|
return dateFormat.parse(dateString);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -39,4 +39,50 @@ public class NumericField extends Field {
|
|||||||
super(name, Field.Type.NUMERIC);
|
super(name, Field.Type.NUMERIC);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formats the given integer in the format required by a numeric field.
|
||||||
|
*
|
||||||
|
* @param i
|
||||||
|
* The integer to format, which may be null.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* A string representation of the given integer, or null if the given
|
||||||
|
* integer was null.
|
||||||
|
*/
|
||||||
|
public static String format(Integer i) {
|
||||||
|
|
||||||
|
// Return null if no value provided
|
||||||
|
if (i == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
// Convert to string
|
||||||
|
return i.toString();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses the given string as an integer, where the given string is in the
|
||||||
|
* format required by a numeric field.
|
||||||
|
*
|
||||||
|
* @param str
|
||||||
|
* The string to parse as an integer, which may be null.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* The integer representation of the given string, or null if the given
|
||||||
|
* string was null.
|
||||||
|
*
|
||||||
|
* @throws NumberFormatException
|
||||||
|
* If the given string is not in a parseable format.
|
||||||
|
*/
|
||||||
|
public static Integer parse(String str) throws NumberFormatException {
|
||||||
|
|
||||||
|
// Return null if no value provided
|
||||||
|
if (str == null || str.isEmpty())
|
||||||
|
return null;
|
||||||
|
|
||||||
|
// Parse as integer
|
||||||
|
return new Integer(str);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -22,6 +22,11 @@
|
|||||||
|
|
||||||
package org.glyptodon.guacamole.form;
|
package org.glyptodon.guacamole.form;
|
||||||
|
|
||||||
|
import java.text.DateFormat;
|
||||||
|
import java.text.ParseException;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a time field. The field may contain only time values which
|
* Represents a time field. The field may contain only time values which
|
||||||
* conform to a standard pattern, defined by TimeField.FORMAT.
|
* conform to a standard pattern, defined by TimeField.FORMAT.
|
||||||
@@ -45,4 +50,48 @@ public class TimeField extends Field {
|
|||||||
super(name, Field.Type.TIME);
|
super(name, Field.Type.TIME);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses the given string into a corresponding time. The string must
|
||||||
|
* follow the standard format used by time fields, as defined by
|
||||||
|
* FORMAT and as would be produced by format().
|
||||||
|
*
|
||||||
|
* @param timeString
|
||||||
|
* The time string to parse, which may be null.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* The time corresponding to the given time string, or null if the
|
||||||
|
* provided time string was null or blank.
|
||||||
|
*
|
||||||
|
* @throws ParseException
|
||||||
|
* If the given time string does not conform to the standard format
|
||||||
|
* used by time fields.
|
||||||
|
*/
|
||||||
|
public static Date parse(String timeString)
|
||||||
|
throws ParseException {
|
||||||
|
|
||||||
|
// Return null if no time provided
|
||||||
|
if (timeString == null || timeString.isEmpty())
|
||||||
|
return null;
|
||||||
|
|
||||||
|
// Parse time according to format
|
||||||
|
DateFormat timeFormat = new SimpleDateFormat(TimeField.FORMAT);
|
||||||
|
return timeFormat.parse(timeString);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts the given time into a string which follows the format used by
|
||||||
|
* time fields.
|
||||||
|
*
|
||||||
|
* @param time
|
||||||
|
* The time value to format, which may be null.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* The formatted time, or null if the provided time was null.
|
||||||
|
*/
|
||||||
|
public static String format(Date time) {
|
||||||
|
DateFormat timeFormat = new SimpleDateFormat(TimeField.FORMAT);
|
||||||
|
return time == null ? null : timeFormat.format(time);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -40,4 +40,27 @@ public class TimeZoneField extends Field {
|
|||||||
super(name, Field.Type.TIMEZONE);
|
super(name, Field.Type.TIMEZONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses the given string into a time zone ID string. As these strings are
|
||||||
|
* equivalent, the only transformation currently performed by this function
|
||||||
|
* is to ensure that a blank time zone string is parsed into null.
|
||||||
|
*
|
||||||
|
* @param timeZone
|
||||||
|
* The time zone string to parse, which may be null.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* The ID of the time zone corresponding to the given string, or null
|
||||||
|
* if the given time zone string was null or blank.
|
||||||
|
*/
|
||||||
|
public static String parse(String timeZone) {
|
||||||
|
|
||||||
|
// Return null if no time zone provided
|
||||||
|
if (timeZone == null || timeZone.isEmpty())
|
||||||
|
return null;
|
||||||
|
|
||||||
|
// Otherwise, assume time zone is valid
|
||||||
|
return timeZone;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -34,7 +34,7 @@ angular.module('form').controller('numberFieldController', ['$scope',
|
|||||||
|
|
||||||
// Update string value in model when typed value is changed
|
// Update string value in model when typed value is changed
|
||||||
$scope.$watch('typedValue', function typedValueChanged(typedValue) {
|
$scope.$watch('typedValue', function typedValueChanged(typedValue) {
|
||||||
$scope.model = (typedValue ? typedValue.toString() : '');
|
$scope.model = ((typedValue || typedValue === 0) ? typedValue.toString() : '');
|
||||||
});
|
});
|
||||||
|
|
||||||
}]);
|
}]);
|
||||||
|
Reference in New Issue
Block a user