Added Node control page (without implementation)
This commit is contained in:
parent
0104db48f5
commit
e55a16265a
@ -0,0 +1,44 @@
|
||||
package ru.dragonestia.loadbalancer.web.component;
|
||||
|
||||
import com.vaadin.flow.component.grid.Grid;
|
||||
import com.vaadin.flow.component.html.H2;
|
||||
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
|
||||
import ru.dragonestia.loadbalancer.web.model.Node;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class NodeList extends VerticalLayout {
|
||||
|
||||
private final Grid<Node> nodesGrid;
|
||||
|
||||
public NodeList(List<Node> nodes) {
|
||||
super();
|
||||
|
||||
add(new H2("Nodes"));
|
||||
add(nodesGrid = createGrid());
|
||||
|
||||
update(nodes);
|
||||
}
|
||||
|
||||
private Grid<Node> createGrid() {
|
||||
var grid = new Grid<>(Node.class, false);
|
||||
grid.addColumn(Node::identifier).setHeader("Identifier");
|
||||
grid.addColumn(node -> node.method().getName()).setHeader("Mode");
|
||||
return grid;
|
||||
}
|
||||
|
||||
public void update(List<Node> nodes) {
|
||||
nodesGrid.setItems(nodes);
|
||||
}
|
||||
|
||||
private void registerNode(Node node) {
|
||||
// TODO: send request for register node and get all nodes
|
||||
|
||||
update(List.of(node));
|
||||
}
|
||||
|
||||
public interface OnRegister {
|
||||
|
||||
List<Node> register(Node node);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,109 @@
|
||||
package ru.dragonestia.loadbalancer.web.component;
|
||||
|
||||
import com.vaadin.flow.component.Component;
|
||||
import com.vaadin.flow.component.Unit;
|
||||
import com.vaadin.flow.component.button.Button;
|
||||
import com.vaadin.flow.component.button.ButtonVariant;
|
||||
import com.vaadin.flow.component.html.H2;
|
||||
import com.vaadin.flow.component.html.Span;
|
||||
import com.vaadin.flow.component.notification.Notification;
|
||||
import com.vaadin.flow.component.notification.NotificationVariant;
|
||||
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
|
||||
import com.vaadin.flow.component.radiobutton.RadioButtonGroup;
|
||||
import com.vaadin.flow.component.radiobutton.RadioGroupVariant;
|
||||
import com.vaadin.flow.component.textfield.Autocomplete;
|
||||
import com.vaadin.flow.component.textfield.TextField;
|
||||
import com.vaadin.flow.data.renderer.ComponentRenderer;
|
||||
import org.springframework.lang.Nullable;
|
||||
import ru.dragonestia.loadbalancer.web.model.Node;
|
||||
import ru.dragonestia.loadbalancer.web.model.type.LoadBalancingMethod;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
public class RegisterNode extends VerticalLayout {
|
||||
|
||||
private final Function<Node, Response> onSubmit;
|
||||
private final TextField identifierField;
|
||||
private final RadioButtonGroup<LoadBalancingMethod> modeRadio;
|
||||
|
||||
public RegisterNode(Function<Node, Response> onSubmit) {
|
||||
this.onSubmit = onSubmit;
|
||||
|
||||
add(new H2("Register node"));
|
||||
add(identifierField = createNodeIdentifierField());
|
||||
add(modeRadio = createModeRadio());
|
||||
add(createSubmitButton());
|
||||
}
|
||||
|
||||
private TextField createNodeIdentifierField() {
|
||||
var field = new TextField("Identifier");
|
||||
field.setMinWidth(20, Unit.REM);
|
||||
field.setPlaceholder("example-node-id");
|
||||
field.setHelperText("The field can contain only lowercase letters, numbers and a dash character");
|
||||
field.setPattern("^[a-z\\d-]+$");
|
||||
field.setRequired(true);
|
||||
field.setAutocomplete(Autocomplete.OFF);
|
||||
field.addValueChangeListener(event -> field.setValue(event.getValue().trim()));
|
||||
return field;
|
||||
}
|
||||
|
||||
private Button createSubmitButton() {
|
||||
var button = new Button("Register");
|
||||
button.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
|
||||
button.addClickListener(event -> onClick());
|
||||
return button;
|
||||
}
|
||||
|
||||
private RadioButtonGroup<LoadBalancingMethod> createModeRadio() {
|
||||
var radio = new RadioButtonGroup<LoadBalancingMethod>("Mode");
|
||||
radio.addThemeVariants(RadioGroupVariant.LUMO_VERTICAL);
|
||||
radio.setRenderer(new ComponentRenderer<Component, LoadBalancingMethod>(mode -> new Span(mode.getName())));
|
||||
radio.setItems(LoadBalancingMethod.SEQUENTIAL_FILLING,
|
||||
LoadBalancingMethod.ROUND_ROBIN,
|
||||
LoadBalancingMethod.LEAST_PICKED);
|
||||
|
||||
radio.setValue(LoadBalancingMethod.SEQUENTIAL_FILLING);
|
||||
return radio;
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
identifierField.clear();
|
||||
}
|
||||
|
||||
private @Nullable String validateForm(String identifier) {
|
||||
if (identifier.isEmpty()) {
|
||||
return "Node identifier cannot be empty";
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void onClick() {
|
||||
String nodeIdentifier = identifierField.getValue();
|
||||
|
||||
String error = null;
|
||||
if (identifierField.isInvalid() || (error = validateForm(nodeIdentifier)) != null) {
|
||||
if (identifierField.isInvalid()) {
|
||||
error = "Invalid node identifier format";
|
||||
}
|
||||
|
||||
Notification.show(error, 3000, Notification.Position.TOP_END)
|
||||
.addThemeVariants(NotificationVariant.LUMO_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
var node = new Node(nodeIdentifier, modeRadio.getValue());
|
||||
var response = onSubmit.apply(node);
|
||||
clear();
|
||||
if (response.error()) {
|
||||
Notification.show(response.reason(), 3000, Notification.Position.TOP_END)
|
||||
.addThemeVariants(NotificationVariant.LUMO_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
Notification.show("Node was successfully registered", 3000, Notification.Position.TOP_END)
|
||||
.addThemeVariants(NotificationVariant.LUMO_SUCCESS);
|
||||
}
|
||||
|
||||
public record Response(boolean error, @Nullable String reason) {}
|
||||
}
|
||||
@ -0,0 +1,46 @@
|
||||
package ru.dragonestia.loadbalancer.web.model;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import ru.dragonestia.loadbalancer.web.model.type.SlotLimit;
|
||||
|
||||
@Getter
|
||||
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public class Bucket {
|
||||
|
||||
private final String identifier;
|
||||
private final String nodeIdentifier;
|
||||
private final SlotLimit slots;
|
||||
private final String payload;
|
||||
private boolean locked = false;
|
||||
|
||||
public static Bucket create(String identifier, Node node, SlotLimit limit, String payload) {
|
||||
return new Bucket(identifier, node.identifier(), limit, payload);
|
||||
}
|
||||
|
||||
public void setLocked(boolean value) {
|
||||
locked = value;
|
||||
}
|
||||
|
||||
public boolean isAvailable(int usedSlots, int requiredSlots) {
|
||||
if (locked) return false;
|
||||
if (slots.isUnlimited()) return true;
|
||||
return slots.getSlots() >= usedSlots + requiredSlots;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return identifier.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object object) {
|
||||
if (object == this) return true;
|
||||
if (object == null) return false;
|
||||
if (object instanceof Bucket other) {
|
||||
return identifier.equals(other.identifier);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,22 @@
|
||||
package ru.dragonestia.loadbalancer.web.model;
|
||||
|
||||
import lombok.NonNull;
|
||||
import ru.dragonestia.loadbalancer.web.model.type.LoadBalancingMethod;
|
||||
|
||||
public record Node(@NonNull String identifier, @NonNull LoadBalancingMethod method) {
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return identifier.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object object) {
|
||||
if (object == this) return true;
|
||||
if (object == null) return false;
|
||||
if (object instanceof Node other) {
|
||||
return identifier.equals(other.identifier);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,21 @@
|
||||
package ru.dragonestia.loadbalancer.web.model;
|
||||
|
||||
import lombok.NonNull;
|
||||
|
||||
public record User(@NonNull String identifier) {
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return identifier.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object object) {
|
||||
if (object == this) return true;
|
||||
if (object == null) return false;
|
||||
if (object instanceof User other) {
|
||||
return identifier.equals(other.identifier);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,14 @@
|
||||
package ru.dragonestia.loadbalancer.web.model.type;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
@Getter
|
||||
@RequiredArgsConstructor
|
||||
public enum LoadBalancingMethod {
|
||||
SEQUENTIAL_FILLING("Sequential filling"),
|
||||
ROUND_ROBIN("Round Robin"),
|
||||
LEAST_PICKED("Least Picked");
|
||||
|
||||
private final String name;
|
||||
}
|
||||
@ -0,0 +1,27 @@
|
||||
package ru.dragonestia.loadbalancer.web.model.type;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
public class SlotLimit {
|
||||
|
||||
private final static int UNLIMITED_VALUE = -1;
|
||||
|
||||
private final int slots;
|
||||
|
||||
private SlotLimit(int slots) {
|
||||
this.slots = slots;
|
||||
}
|
||||
|
||||
public static SlotLimit unlimited() {
|
||||
return new SlotLimit(UNLIMITED_VALUE);
|
||||
}
|
||||
|
||||
public static SlotLimit of(int slots) {
|
||||
return new SlotLimit(slots);
|
||||
}
|
||||
|
||||
public boolean isUnlimited() {
|
||||
return slots == UNLIMITED_VALUE;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,36 @@
|
||||
package ru.dragonestia.loadbalancer.web.page;
|
||||
|
||||
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
|
||||
import com.vaadin.flow.router.Route;
|
||||
import lombok.Getter;
|
||||
import ru.dragonestia.loadbalancer.web.component.NodeList;
|
||||
import ru.dragonestia.loadbalancer.web.component.RegisterNode;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Getter
|
||||
@Route("/nodes")
|
||||
public class NodesPage extends VerticalLayout {
|
||||
|
||||
private final RegisterNode registerNode;
|
||||
private final NodeList nodeList;
|
||||
|
||||
public NodesPage() {
|
||||
super();
|
||||
|
||||
add(registerNode = createRegisterNodeElement());
|
||||
add(nodeList = createNodeListElement());
|
||||
}
|
||||
|
||||
protected RegisterNode createRegisterNodeElement() {
|
||||
return new RegisterNode(node -> {
|
||||
nodeList.update(List.of(node)); // remove it later
|
||||
return new RegisterNode.Response(false, "Some error"); // TODO
|
||||
});
|
||||
}
|
||||
|
||||
protected NodeList createNodeListElement() {
|
||||
// TODO: getting nodes list
|
||||
return new NodeList(List.of());
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user