Skip to content

Instantly share code, notes, and snippets.

@james-d
Created May 11, 2014 01:31
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save james-d/2cfba7a9edf6c00ac631 to your computer and use it in GitHub Desktop.
Save james-d/2cfba7a9edf6c00ac631 to your computer and use it in GitHub Desktop.
JavaFX client code for the Laboratory Information Management System demo. The key here is that the controller references a client-side data accessor, which uses a Jersey client to communicate with the web-based REST service.
package edu.marshall.genomics.lims.client;
import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.Entity;
import javax.ws.rs.core.GenericType;
import org.glassfish.jersey.client.ClientConfig;
import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider;
import edu.marshall.genomics.lims.entities.Investigator;
import edu.marshall.genomics.lims.entities.Project;
import edu.marshall.genomics.lims.entities.Sample;
public class DataAccessor {
private final Client restClient ;
private final String baseUrl ;
public DataAccessor(String baseUrl) {
this.baseUrl = baseUrl ;
ClientConfig config = new ClientConfig();
config.register(new JacksonJsonProvider());
this.restClient = ClientBuilder.newClient(config);
}
public List<Investigator> getAllInvestigators() {
return restClient.target(baseUrl)
.path("/investigators")
.request(APPLICATION_JSON)
.get(new GenericType<List<Investigator>>(){});
}
public Investigator getInvestigatorByEmail(String email) {
return restClient.target(baseUrl)
.path("/investigators/"+email)
.request(APPLICATION_JSON)
.get(Investigator.class);
}
public List<Project> getAllProjects() {
return restClient.target(baseUrl)
.path("/projects")
.request(APPLICATION_JSON)
.get(new GenericType<List<Project>>(){});
}
public List<Project> getProjectsByInvestigator(Investigator investigator) {
String path = "/projects/"+investigator.getId();
return restClient.target(baseUrl)
.path(path)
.request(APPLICATION_JSON)
.get(new GenericType<List<Project>>(){});
}
public List<Sample> getSamplesByProject(Project project) {
return restClient.target(baseUrl)
.path("/samples/"+project.getId())
.request(APPLICATION_JSON)
.get(new GenericType<List<Sample>>(){});
}
public void addInvestigators(List<Investigator> investigators) {
restClient.target(baseUrl)
.path("/investigators")
.request()
.post(Entity.entity(investigators, APPLICATION_JSON), new GenericType<ArrayList<Integer>>(){});
}
public void updateInvestigator(Investigator investigator) {
restClient.target(baseUrl)
.path("/investigators")
.request()
.put(Entity.entity(investigator, APPLICATION_JSON));
}
public List<Integer> addProjects(List<Project> projects) {
List<Integer> assignedIds = restClient.target(baseUrl)
.path("/projects")
.request(APPLICATION_JSON)
.post(Entity.entity(projects, APPLICATION_JSON), new GenericType<ArrayList<Integer>>(){});
return assignedIds ;
}
public void updateProject(Project project) {
restClient.target(baseUrl)
.path("/projects")
.request()
.put(Entity.entity(project, APPLICATION_JSON));
}
public void addSamples(List<Sample> samples) {
List<Integer> assignedIds = restClient.target(baseUrl)
.path("/samples")
.request(APPLICATION_JSON)
.post(Entity.entity(samples, APPLICATION_JSON), new GenericType<ArrayList<Integer>>(){});
Iterator<Sample> sampleIterator = samples.iterator();
for (int id : assignedIds)
sampleIterator.next().setId(id);
}
public void updateSample(Sample sample) {
restClient.target(baseUrl)
.path("/samples")
.request()
.put(Entity.entity(sample, APPLICATION_JSON));
}
}
package edu.marshall.genomics.lims.client;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import edu.marshall.genomics.lims.entities.Investigator;
import edu.marshall.genomics.lims.entities.Project;
import edu.marshall.genomics.lims.entities.Sample;
public class DataModel {
private final ObservableList<Investigator> investigators ;
private final ObservableList<Project> displayedProjects ;
private final ObservableList<Sample> displayedSamples ;
public DataModel() {
investigators = FXCollections.observableArrayList();
displayedProjects = FXCollections.observableArrayList();
displayedSamples = FXCollections.observableArrayList();
}
public ObservableList<Investigator> getInvestigators() {
return investigators;
}
public ObservableList<Project> getDisplayedProjects() {
return displayedProjects;
}
public ObservableList<Sample> getDisplayedSamples() {
return displayedSamples;
}
}
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.ColumnConstraints?>
<GridPane xmlns:fx="http://javafx.com/fxml"
fx:controller="edu.marshall.genomics.lims.client.InvestigatorDetailController"
hgap="10" vgap="5">
<Label text="First name:" GridPane.rowIndex="0"
GridPane.columnIndex="0" />
<Label text="Last name:" GridPane.rowIndex="1"
GridPane.columnIndex="0" />
<Label text="Email:" GridPane.rowIndex="2" GridPane.columnIndex="0" />
<Label text="Institution:" GridPane.rowIndex="3"
GridPane.columnIndex="0" />
<Label fx:id="firstNameLabel" GridPane.rowIndex="0"
GridPane.columnIndex="1" />
<Label fx:id="lastNameLabel" GridPane.rowIndex="1"
GridPane.columnIndex="1" />
<Label fx:id="emailLabel" GridPane.rowIndex="2"
GridPane.columnIndex="1" />
<Label fx:id="institutionLabel" GridPane.rowIndex="3"
GridPane.columnIndex="1" />
<TextField fx:id="firstNameTextField" GridPane.rowIndex="0"
GridPane.columnIndex="1" />
<TextField fx:id="lastNameTextField" GridPane.rowIndex="1"
GridPane.columnIndex="1" />
<TextField fx:id="emailTextField" GridPane.rowIndex="2"
GridPane.columnIndex="1" />
<TextField fx:id="institutionTextField" GridPane.rowIndex="3"
GridPane.columnIndex="1" />
<columnConstraints>
<ColumnConstraints hgrow="NEVER" halignment="RIGHT" />
<ColumnConstraints hgrow="ALWAYS" halignment="LEFT" />
</columnConstraints>
</GridPane>
package edu.marshall.genomics.lims.client;
import org.fxmisc.easybind.EasyBind;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.fxml.FXML;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import edu.marshall.genomics.lims.entities.Investigator;
public class InvestigatorDetailController {
@FXML
private Label firstNameLabel ;
@FXML
private Label lastNameLabel ;
@FXML
private Label emailLabel ;
@FXML
private Label institutionLabel ;
@FXML
private TextField firstNameTextField ;
@FXML
private TextField lastNameTextField ;
@FXML
private TextField emailTextField ;
@FXML
private TextField institutionTextField ;
private final BooleanProperty editMode = new SimpleBooleanProperty(this, "editMode");
private final ObjectProperty<Investigator> investigator = new SimpleObjectProperty<>();
public Investigator getInvestigator() {
return investigator.get();
}
public ObjectProperty<Investigator> investigatorProperty() {
return investigator;
}
public void initialize() {
firstNameLabel.visibleProperty().bind(editMode.not());
lastNameLabel.visibleProperty().bind(editMode.not());
emailLabel.visibleProperty().bind(editMode.not());
institutionLabel.visibleProperty().bind(editMode.not());
firstNameTextField.visibleProperty().bind(editMode);
lastNameTextField.visibleProperty().bind(editMode);
emailTextField.visibleProperty().bind(editMode);
institutionTextField.visibleProperty().bind(editMode);
firstNameLabel.textProperty().bind(EasyBind.select(investigator).selectObject(Investigator::firstNameProperty));
lastNameLabel.textProperty().bind(EasyBind.select(investigator).selectObject(Investigator::lastNameProperty));
emailLabel.textProperty().bind(EasyBind.select(investigator).selectObject(Investigator::emailProperty));
institutionLabel.textProperty().bind(EasyBind.select(investigator).selectObject(Investigator::institutionProperty));
}
}
package edu.marshall.genomics.lims.client;
import java.lang.reflect.Constructor;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class Lims extends Application {
@Override
public void start(Stage primaryStage) {
try {
DataModel dataModel = new DataModel();
FXMLLoader loader = new FXMLLoader(getClass().getResource("Main.fxml"));
loader.setControllerFactory(type -> {
try {
for (Constructor<?> constructor : type.getConstructors()) {
if (constructor.getParameterCount() == 1 && constructor.getParameterTypes()[0] == DataModel.class) {
return constructor.newInstance(dataModel);
}
}
return type.newInstance() ;
} catch (Exception e) {
e.printStackTrace();
return null ;
}
});
Parent root = loader.<Parent>load();
Scene scene = new Scene(root, 800, 600);
primaryStage.setScene(scene);
primaryStage.show();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.control.ListView?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import java.net.URL ?>
<?import javafx.geometry.Insets?>
<BorderPane xmlns:fx="http://javafx.com/fxml"
fx:controller="edu.marshall.genomics.lims.client.MainController">
<left>
<VBox spacing="5" minWidth="350">
<BorderPane.margin>
<Insets top="5" bottom="5" left="5" right="5" />
</BorderPane.margin>
<Label text="Investigators" styleClass="title" />
<ListView fx:id="investigatorList" />
<fx:include source="InvestigatorDetail.fxml" fx:id="investigatorDetail" />
</VBox>
</left>
<center>
<BorderPane>
<left>
<VBox spacing="5">
<BorderPane.margin>
<Insets top="5" bottom="5" left="5" right="5" />
</BorderPane.margin>
<Label text="Projects" styleClass="title" />
<ListView fx:id="projectList" />
<TextField fx:id="newProjectTextField" onAction="#addProject" />
<Button fx:id="newProjectButton" onAction="#addProject"
text="Add Project" />
</VBox>
</left>
<center>
<VBox spacing="5">
<BorderPane.margin>
<Insets top="5" bottom="5" left="5" right="5" />
</BorderPane.margin>
<Label text="Samples" styleClass="title" />
<ListView fx:id="sampleList" />
<TextField fx:id="newSampleTextField" onAction="#addSample" />
<Button fx:id="newSampleButton" onAction="#addSample" text="Add Sample" />
</VBox>
</center>
</BorderPane>
</center>
<stylesheets>
<URL value="@lims.css" />
</stylesheets>
</BorderPane>
package edu.marshall.genomics.lims.client;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.function.Function;
import java.util.function.Supplier;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.BooleanBinding;
import javafx.beans.value.ObservableValue;
import javafx.collections.ObservableList;
import javafx.concurrent.Task;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.control.TextField;
import org.fxmisc.easybind.EasyBind;
import edu.marshall.genomics.lims.entities.Investigator;
import edu.marshall.genomics.lims.entities.Project;
import edu.marshall.genomics.lims.entities.Sample;
public class MainController {
private final DataModel dataModel;
private final DataAccessor dataAccessor;
private final ExecutorService exec;
@FXML
private ListView<Investigator> investigatorList;
@FXML
private ListView<Project> projectList;
@FXML
private ListView<Sample> sampleList;
@FXML
private TextField newProjectTextField;
@FXML
private TextField newSampleTextField;
@FXML
private Button newProjectButton;
@FXML
private Button newSampleButton;
@FXML
private InvestigatorDetailController investigatorDetailController;
public MainController(DataModel dataModel) {
this.dataModel = dataModel;
this.dataAccessor = new DataAccessor("http://localhost:8080/LIMS-web/");
this.exec = Executors.newSingleThreadExecutor(runnable -> {
Thread thread = new Thread(runnable);
thread.setDaemon(true);
return thread;
});
}
public void initialize() {
bindListsToDataModel();
setUpButtonAndTextFieldDisableBindings();
setupListCellFactories();
loadInvestigators();
investigatorList.getSelectionModel().selectedItemProperty()
.addListener((obs, oldInv, newInv) -> {
dataModel.getDisplayedProjects().clear();
if (newInv != null) {
loadProjects(newInv);
}
});
projectList.getSelectionModel().selectedItemProperty()
.addListener((obs, oldProject, newProject) -> {
dataModel.getDisplayedSamples().clear();
if (newProject != null) {
loadSamples(newProject);
}
});
investigatorDetailController.investigatorProperty().bind(
investigatorList.getSelectionModel().selectedItemProperty());
}
private void bindListsToDataModel() {
investigatorList.setItems(dataModel.getInvestigators());
projectList.setItems(dataModel.getDisplayedProjects());
sampleList.setItems(dataModel.getDisplayedSamples());
}
private void setUpButtonAndTextFieldDisableBindings() {
disableNodesOnEmptySelectionOrTextField(investigatorList,
newProjectTextField, newProjectButton);
disableNodesOnEmptySelectionOrTextField(projectList,
newSampleTextField, newSampleButton);
}
private void disableNodesOnEmptySelectionOrTextField(ListView<?> listView,
TextField textField, Button button) {
BooleanBinding emptySelection = Bindings.isNull(listView
.getSelectionModel().selectedItemProperty());
textField.disableProperty().bind(emptySelection);
button.disableProperty().bind(
emptySelection.or(Bindings.isEmpty(textField.textProperty())));
}
private void setupListCellFactories() {
setUpListCellFactory(
investigatorList,
investigator -> EasyBind.combine(
investigator.firstNameProperty(),
investigator.lastNameProperty(),
(s1, s2) -> s1 + " " + s2));
setUpListCellFactory(projectList, Project::titleProperty);
setUpListCellFactory(sampleList, Sample::sampleIdProperty);
}
private <T> void setUpListCellFactory(ListView<T> listView,
Function<T, ObservableValue<String>> renderFunction) {
listView.setCellFactory(lv ->
{
ListCell<T> cell = new ListCell<>();
cell.textProperty().bind(
EasyBind.select(cell.itemProperty())
.selectObject(renderFunction));
return cell;
});
}
private void loadInvestigators() {
loadEntities(dataAccessor::getAllInvestigators,
dataModel.getInvestigators());
}
private void loadProjects(Investigator investigator) {
loadEntities(
() -> dataAccessor.getProjectsByInvestigator(investigator),
dataModel.getDisplayedProjects());
}
private void loadSamples(Project project) {
loadEntities(() -> dataAccessor.getSamplesByProject(project),
dataModel.getDisplayedSamples());
}
private <T> void loadEntities(Supplier<List<T>> supplier,
ObservableList<T> receivingList) {
Task<List<T>> task = new Task<List<T>>() {
@Override
protected List<T> call() throws Exception {
return supplier.get();
}
};
task.setOnSucceeded(event -> receivingList.setAll(task.getValue()));
task.setOnFailed(event -> handleException(task));
exec.submit(task);
}
private void handleException(Task<?> failedTask) {
// TODO
failedTask.getException().printStackTrace();
}
@FXML
public void addProject() {
Project project = new Project();
project.setTitle(newProjectTextField.getText());
Investigator investigator =
investigatorList.getSelectionModel().getSelectedItem();
project.setInvestigator(investigator);
Task<Void> addProjectTask = new Task<Void>() {
@Override
protected Void call() throws Exception {
dataAccessor.addProjects(Collections.singletonList(project));
return null;
}
};
addProjectTask.setOnSucceeded(event -> loadProjects(investigator));
addProjectTask.setOnFailed(event -> handleException(addProjectTask));
exec.submit(addProjectTask);
}
@FXML
public void addSample() {
Sample sample = new Sample();
sample.setSampleId(newSampleTextField.getText());
Project project = projectList.getSelectionModel().getSelectedItem();
sample.setProject(project);
Task<Void> addSampleTask = new Task<Void>() {
@Override
protected Void call() throws Exception {
dataAccessor.addSamples(Collections.singletonList(sample));
return null;
}
};
addSampleTask.setOnSucceeded(event -> loadSamples(project));
addSampleTask.setOnFailed(event -> handleException(addSampleTask));
exec.submit(addSampleTask);
newSampleTextField.setText("");
newSampleTextField.requestFocus();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment