1. Framework Model Architecture
The framework model layer serves as the bridge between the domain layer’s entity definitions and the UI layer’s visual components. It provides a comprehensive set of model classes that handle data retrieval, manipulation, and state management while maintaining strict separation of concerns.
1.1. Overview
The model layer implements a reactive MVC architecture where:
-
Models manage data and business logic through observable state
-
Views (UI layer) observe models for state changes
-
Controllers are the reactive bindings (like ComponentValue) that automatically synchronize models and views
All model components are built on Codion’s observable foundation, ensuring that any data or state change automatically propagates to interested observers.
1.2. Core Components
1.2.1. EntityModel
The EntityModel serves as the central coordinator, managing both edit and table models along with any detail models in master-detail relationships.
SwingEntityModel customerModel = new SwingEntityModel(Customer.TYPE, connectionProvider);
SwingEntityModel invoiceModel = new SwingEntityModel(Invoice.TYPE, connectionProvider);
// Establish master-detail relationship
customerModel.detailModels().add(invoiceModel);
1.2.2. EntityEditModel
The EntityEditModel handles single entity CRUD operations and maintains the current entity state.
SwingEntityModel customerModel = new SwingEntityModel(Customer.TYPE, connectionProvider);
EntityEditModel editModel = customerModel.editModel();
// Access entity values
Value<String> nameValue = editModel.editor().value(Customer.FIRSTNAME);
// Perform operations
editModel.insert();
editModel.update();
editModel.delete();
1.2.3. EntityTableModel
The EntityTableModel manages collections of entities, providing sorting, filtering, and selection capabilities.
SwingEntityModel customerModel = new SwingEntityModel(Customer.TYPE, connectionProvider);
EntityTableModel tableModel = customerModel.tableModel();
// Refresh data
tableModel.items().refresh();
// Access selection
Collection<Entity> selected = tableModel.selection().items().get();
1.2.4. EntityQueryModel
The EntityQueryModel controls how data is fetched from the database, including conditions, limits, and ordering.
SwingEntityModel customerModel = new SwingEntityModel(Customer.TYPE, connectionProvider);
SwingEntityTableModel tableModel = customerModel.tableModel();
EntityQueryModel queryModel = tableModel.queryModel();
// Configure query behavior
queryModel.limit().set(200);
queryModel.conditionRequired().set(true);
queryModel.orderBy().set(OrderBy.ascending(Customer.LASTNAME));
1.3. Observable Architecture
Every model component extends Codion’s observable foundation:
1.3.1. State Management
Models expose their state through observable values:
SwingEntityModel customerModel = new SwingEntityModel(Customer.TYPE, connectionProvider);
EntityEditModel editModel = customerModel.editModel();
EntityTableModel tableModel = customerModel.tableModel();
// Edit model states
State updateEnabled = editModel.updateEnabled();
State updateMultipleEnabled = editModel.updateMultipleEnabled();
ObservableState modified = editModel.editor().modified();
// Table model states
ObservableState refreshing = tableModel.items().refresher().active();
ObservableState hasSelection = tableModel.selection().empty().not();
// Combine states
ObservableState canDelete = State.and(hasSelection, refreshing.not());
1.3.2. Event System
Models emit events for all significant operations:
SwingEntityModel customerModel = new SwingEntityModel(Customer.TYPE, connectionProvider);
SwingEntityEditModel editModel = customerModel.editModel();
SwingEntityTableModel tableModel = customerModel.tableModel();
// Listen for entity changes
editModel.afterInsert().addConsumer(entities -> {
System.out.println("Inserted: " + entities);
});
// Listen for selection changes
tableModel.selection().item().addConsumer(selectedEntity -> {
if (selectedEntity != null) {
loadDetails(selectedEntity);
}
});
1.3.3. Value Observers
Entity values are observable and can be bound across models:
SwingEntityModel trackModel = new SwingEntityModel(Track.TYPE, connectionProvider);
EntityEditModel editModel = trackModel.editModel();
// Bind edit model value to UI state
EditorValue<BigDecimal> priceValue = editModel.editor().value(Track.UNITPRICE);
ObservableState priceValid = editModel.editor().value(Track.UNITPRICE).valid();
// React to value changes
priceValue.addConsumer(newPrice -> updateTotalPrice(newPrice));
// React to value edits
priceValue.edited().addConsumer(newPrice -> System.out.println("Price: " + newPrice));
1.4. Model Relationships
1.4.1. Master-Detail Pattern
The framework supports arbitrarily deep master-detail hierarchies:
// Three-level hierarchy
SwingEntityModel customerModel = new SwingEntityModel(Customer.TYPE, connectionProvider);
SwingEntityModel invoiceModel = new SwingEntityModel(Invoice.TYPE, connectionProvider);
SwingEntityModel invoiceLineModel = new SwingEntityModel(InvoiceLine.TYPE, connectionProvider);
customerModel.detailModels().add(invoiceModel);
invoiceModel.detailModels().add(invoiceLineModel);
// Selection cascades down the hierarchy
Entity customer = getCustomer(connectionProvider);
customerModel.tableModel().selection().item().set(customer);
// Invoices for selected customer are loaded
Entity invoice = invoiceModel.tableModel().items().visible().get(0);
invoiceModel.tableModel().selection().item().set(invoice);
// Invoice lines for selected invoice are loaded
1.5. Best Practices
1.5.1. Query Optimization
Always configure appropriate limits to prevent loading excessive data:
class CustomerTableModel extends SwingEntityTableModel {
public CustomerTableModel(EntityConnectionProvider connectionProvider) {
super(new SwingEntityEditModel(Customer.TYPE, connectionProvider));
// Prevent loading entire customer base
queryModel().limit().set(100);
queryModel().conditionRequired().set(true);
}
}
1.5.2. Event Handling
Use the event system to maintain consistency across models:
SwingEntityModel invoiceLineModel = new SwingEntityModel(InvoiceLine.TYPE, connectionProvider);
// Update summary when details change
invoiceLineModel.editModel().afterInsert().addConsumer(entities -> updateInvoiceTotal());
invoiceLineModel.editModel().afterUpdate().addConsumer(entities -> updateInvoiceTotal());
invoiceLineModel.editModel().afterDelete().addConsumer(entities -> updateInvoiceTotal());
1.5.3. Custom Data Sources
For specialized queries, consider custom data sources:
SwingEntityModel customerModel = new SwingEntityModel(Customer.TYPE, connectionProvider);
SwingEntityTableModel tableModel = customerModel.tableModel();
// Fetch only Customers with emails by default
tableModel.queryModel().dataSource().set(queryModel -> {
EntityConnection connection = queryModel.connectionProvider().connection();
return connection.select(and(
Customer.EMAIL.isNotNull(),
queryModel.condition().where(Conjunction.AND))
);
});
1.6. See Also
-
EntityModel - Detailed EntityModel documentation
-
EntityEditModel - Edit model specifics
-
EntityTableModel - Table model features
-
Domain Model - Entity definition layer
2. EntityModel
The application model layer consists of the EntityModel class and its associates; the EntityTableModel, which provides a table representation of entities and the EntityEditModel which provides the CRUD operations.
An EntityModel always contains an EntityEditModel instance and usually contains a EntityTableModel as well. A default edit model implementation is created automatically by the EntityTableModel if one is not supplied via a constructor argument.
public class AddressModel extends SwingEntityModel {
public AddressModel(EntityConnectionProvider connectionProvider) {
super(Address.TYPE, connectionProvider);
}
}
public class CustomerAddressModel extends SwingEntityModel {
public CustomerAddressModel(EntityConnectionProvider connectionProvider) {
super(new CustomerAddressTableModel(connectionProvider));
}
}
2.1. Detail models
An EntityModel can contain one or more detail models, usually based on foreign key relationships.
public class StoreApplicationModel extends SwingEntityApplicationModel {
public StoreApplicationModel(EntityConnectionProvider connectionProvider) {
super(connectionProvider, List.of(createCustomerModel(connectionProvider)));
}
private static SwingEntityModel createCustomerModel(EntityConnectionProvider connectionProvider) {
CustomerModel customerModel =
new CustomerModel(connectionProvider);
CustomerAddressModel customerAddressModel =
new CustomerAddressModel(connectionProvider);
customerModel.detailModels().add(customerAddressModel);
//populate the model with rows from the database
customerModel.tableModel().items().refresh();
return customerModel;
}
}
2.2. Event binding
The model layer classes expose a number of Event, State and Value observers.
The example below prints, to the standard output, all changes made to a given attribute value as well as a message indicating that a table refresh has started.
public class CustomerModel extends SwingEntityModel {
public CustomerModel(EntityConnectionProvider connectionProvider) {
super(new CustomerTableModel(connectionProvider));
bindEvents();
}
private void bindEvents() {
CustomerTableModel tableModel = (CustomerTableModel) tableModel();
tableModel.selection().items()
.addConsumer(selected ->
System.out.println("Items selected: " + selected));
tableModel.items().refresher().result()
.addListener(() -> System.out.println("Refresh successful"));
CustomerEditModel editModel = (CustomerEditModel) editModel();
editModel.afterInsert()
.addConsumer(inserted ->
System.out.println("Entities inserted" + inserted));
editModel.editor().value(Customer.FIRST_NAME).edited()
.addConsumer(firstName ->
System.out.println("First name changed to " + firstName));
}
}
3. EntityEditModel
The EntityEditModel interface defines the CRUD business logic used by the EntityEditPanel class when entities are being edited. The EntityEditModel works with a single entity instance, which can be retrieved and set via the EntityEditor instance accessible via the editor() method. EntityEditor exposes a number of methods for manipulating as well as querying the state of the entity being edited.
public class CustomerEditModel extends SwingEntityEditModel {
public CustomerEditModel(EntityConnectionProvider connectionProvider) {
super(Customer.TYPE, connectionProvider);
}
}
EntityConnectionProvider connectionProvider =
EntityConnectionProvider.builder()
.domainType(Store.DOMAIN)
.user(User.parse("scott:tiger"))
.clientType("StoreMisc")
.build();
CustomerEditModel editModel = new CustomerEditModel(connectionProvider);
EntityEditor editor = editModel.editor();
editor.value(Customer.ID).defaultValue()
.set(() -> UUID.randomUUID().toString());
//sets the defaults
editor.defaults();
//set the values
editor.value(Customer.FIRST_NAME).set("Björn");
editor.value(Customer.LAST_NAME).set("Sigurðsson");
editor.value(Customer.ACTIVE).set(true);
//inserts and returns the inserted entity
Entity customer = editModel.insert();
//modify some values
editor.value(Customer.FIRST_NAME).set("John");
editor.value(Customer.LAST_NAME).set("Doe");
//updates and returns the updated entity
customer = editModel.update();
//deletes the active entity
editModel.delete();
4. EntityTableModel
The EntityTableModel class provides a table representation of the underlying entities.
Every EntityTableModel contains a EntityEditModel instance. A default edit model implementation is created automatically by the EntityTableModel if one is not supplied via a constructor argument.
public class CustomerTableModel extends SwingEntityTableModel {
public CustomerTableModel(EntityConnectionProvider connectionProvider) {
super(new CustomerEditModel(connectionProvider));
}
}
public class CustomerAddressTableModel extends SwingEntityTableModel {
public CustomerAddressTableModel(EntityConnectionProvider connectionProvider) {
super(CustomerAddress.TYPE, connectionProvider);
}
}
5. EntityQueryModel
The EntityQueryModel manages how entities are fetched from the database for table models. It provides fine-grained control over query conditions, result limits, ordering, and custom data sources.
5.1. Overview
EntityQueryModel
acts as the data retrieval engine for EntityTableModel
, encapsulating:
-
Query conditions (WHERE and HAVING clauses)
-
Result limits to prevent excessive data loading
-
Custom ordering specifications
-
Attribute selection for optimization
-
Custom data sources for specialized queries
SwingEntityModel customerModel = new SwingEntityModel(Customer.TYPE, connectionProvider);
SwingEntityTableModel tableModel = customerModel.tableModel();
EntityQueryModel queryModel = tableModel.queryModel();
// Configure query behavior
queryModel.limit().set(200);
queryModel.conditionRequired().set(true);
queryModel.orderBy().set(OrderBy.ascending(Customer.LASTNAME));
5.2. Condition Management
5.2.1. Table Condition Model
The primary condition mechanism is the EntityTableConditionModel, which provides a flexible way to build complex queries:
SwingEntityModel customerModel = new SwingEntityModel(Customer.TYPE, connectionProvider);
EntityTableConditionModel conditionModel = customerModel.tableModel().queryModel().condition();
// Set condition values
conditionModel.get(Customer.EMAIL).set().isNotNull();
conditionModel.get(Customer.COUNTRY).set().equalTo("Iceland");
// The resulting query will include:
// WHERE email is not null AND country = 'Iceland'
5.2.2. Additional WHERE Conditions
Beyond the table condition model, you can add custom WHERE conditions:
SwingEntityModel customerModel = new SwingEntityModel(Customer.TYPE, connectionProvider);
EntityQueryModel queryModel = customerModel.tableModel().queryModel();
// Single additional condition
queryModel.where().conjunction().set(Conjunction.AND);
queryModel.where().set(() -> Customer.COUNTRY.equalTo("Iceland"));
// Multiple conditions with custom conjunction
queryModel.where().set(() -> Condition.or(
Customer.CITY.equalTo("Reykjavik"),
Customer.CITY.equalTo("Akureyri")
));
5.3. Query Limits
Prevent loading excessive data by setting query limits:
SwingEntityModel customerModel = new SwingEntityModel(Customer.TYPE, connectionProvider);
EntityQueryModel queryModel = customerModel.tableModel().queryModel();
// Set a specific limit
queryModel.limit().set(500);
// Remove limit (fetch all matching rows)
queryModel.limit().clear();
// Add a max limit validator
queryModel.limit().addValidator(newLimit -> {
if (newLimit > 10.000) {
throw new IllegalArgumentException("Limit may not exceed 10.000");
}
});
// Listen for limit changes
queryModel.limit().addConsumer(newLimit ->
System.out.println("Query limit changed to: " + newLimit));
5.4. Result Ordering
Specify how results should be ordered:
SwingEntityModel invoiceModel = new SwingEntityModel(Invoice.TYPE, connectionProvider);
EntityQueryModel queryModel = invoiceModel.tableModel().queryModel();
// Single column ordering
queryModel.orderBy().set(OrderBy.descending(Invoice.DATE));
// Multiple columns
queryModel.orderBy().set(OrderBy.builder()
.ascending(Invoice.BILLINGCOUNTRY)
.descending(Invoice.DATE)
.build()
);
5.5. Custom Data Sources
For complex queries that can’t be expressed through conditions, provide a custom data source:
SwingEntityModel customerModel = new SwingEntityModel(Customer.TYPE, connectionProvider);
EntityQueryModel entityQueryModel = customerModel.tableModel().queryModel();
entityQueryModel.dataSource().set(queryModel -> {
EntityConnection connection = queryModel.connectionProvider().connection();
// Custom query with complex joins or database-specific features
return connection.select(Select.where(customComplexCondition())
.attributes(Customer.ADDRESS, Customer.CITY, Customer.COUNTRY)
.build());
});
5.6. Condition Required
Prevent accidental full table scans:
SwingEntityModel customerModel = new SwingEntityModel(Customer.TYPE, connectionProvider);
EntityQueryModel queryModel = customerModel.tableModel().queryModel();
// Require at least one condition
queryModel.conditionRequired().set(true);
// Specify that a certain condition must be enabled
queryModel.conditionEnabled().set(queryModel.condition().get(Customer.SUPPORTREP_FK).enabled());
5.7. Attribute Management
Optimize queries by selecting only needed attributes:
SwingEntityModel albumModel = new SwingEntityModel(Album.TYPE, connectionProvider);
EntityQueryModel queryModel = albumModel.tableModel().queryModel();
// Exclude large columns by default
queryModel.attributes().excluded().add(Album.COVER);
// Include them only when needed
State detailView = State.state();
detailView.addConsumer(showDetails -> {
if (showDetails) {
queryModel.attributes().included().add(Album.COVER);
}
});
5.8. Configuration Properties
Property | Default | Description |
---|---|---|
|
1000 |
Default query limit |
|
false |
Whether queries require at least one condition |
|
empty |
Default attributes to exclude from queries |
6. EntitySearchModel
The EntitySearchModel is the model component underlying the EntitySearchField UI component. It provides entity search functionality with support for multi-column text searching and entity selection.
6.1. Overview
EntitySearchModel
provides:
-
Multi-column text searching with configurable wildcards
-
Single or multi-entity selection management
-
Result limiting to prevent excessive data retrieval
-
Case-sensitive or insensitive search options
-
The model component for
EntitySearchField
UI component -
Automatic updates when entities are modified
EntitySearchModel searchModel = EntitySearchModel.builder(Customer.TYPE, connectionProvider)
.searchColumns(List.of(Customer.FIRSTNAME, Customer.LASTNAME, Customer.EMAIL))
.limit(50)
.build();
// Perform search
searchModel.condition().set(() -> Customer.FIRSTNAME.equalTo("john"));
// Get search result
List<Entity> result = searchModel.search().result();
6.2. Search Configuration
6.2.1. Search Settings
Configure search behavior per column:
EntitySearchModel searchModel = EntitySearchModel.builder(Customer.TYPE, connectionProvider)
.searchColumns(List.of(Customer.FIRSTNAME, Customer.LASTNAME))
.build();
// Get settings for a specific column
EntitySearchModel.Settings settings = searchModel.settings().get(Customer.LASTNAME);
// Add wildcards automatically
settings.wildcardPrefix().set(true); // Adds % before search text
settings.wildcardPostfix().set(true); // Adds % after search text
// Replace spaces with wildcards
settings.spaceAsWildcard().set(true); // "john smith" → "john%smith"
// Case sensitivity
settings.caseSensitive().set(false); // Case-insensitive search
6.2.2. Wildcard Strategies
The search model supports different wildcard configurations:
-
Prefix search (autocomplete style):
wildcardPrefix(false)
,wildcardPostfix(true)
- "joh" → "joh%" -
Contains search:
wildcardPrefix(true)
,wildcardPostfix(true)
- "ohn" → "%ohn%" -
Exact search:
wildcardPrefix(false)
,wildcardPostfix(false)
- "john" → "john" -
Multi-word search:
spaceAsWildcard(true)
- "john reyk" → "%john%reyk%"
6.3. Selection Management
6.3.1. Single Selection Mode
For selecting one entity at a time:
EntitySearchModel searchModel = EntitySearchModel.builder(Album.TYPE, connectionProvider)
.searchColumns(List.of(Album.TITLE))
.build();
// Set selection programmatically
Entity album = getAlbum(connectionProvider);
searchModel.selection().entity().set(album);
// React to selection changes
searchModel.selection().entity().addConsumer(selectedAlbum -> {
if (selectedAlbum != null) {
displayAlbumDetails(selectedAlbum);
}
});
// Clear selection
searchModel.selection().clear();
6.3.2. Multi-Selection Mode
For selecting multiple entities:
EntitySearchModel searchModel = EntitySearchModel.builder(Track.TYPE, connectionProvider)
.searchColumns(List.of(Track.NAME))
.build();
// Get all selected entities
Collection<Entity> selectedTracks = searchModel.selection().entities().get();
// Add to selection
Entity track = getTrack(connectionProvider);
searchModel.selection().entities().add(track);
// Remove from selection
searchModel.selection().entities().remove(track);
// Replace entire selection
searchModel.selection().entities().set(List.of(track));
6.4. Configuration Properties
Property | Default | Description |
---|---|---|
|
true |
Whether search models react to entity edit events |
|
100 |
Default result limit for search models |
|
false |
Default wildcard prefix setting |
|
true |
Default wildcard postfix setting |
|
false |
Default space replacement setting |
|
false |
Default case sensitivity setting |
7. Model Linking
Model linking provides the mechanism for establishing master-detail relationships between entity models. The framework automatically synchronizes detail models based on master model selection and data changes.
7.1. Overview
The ModelLink API enables automatic detail model filtering based on master selection and propagation of data changes.
// Invoice -> InvoiceLines
SwingEntityModel invoiceModel = new SwingEntityModel(Invoice.TYPE, connectionProvider);
SwingEntityModel invoiceLineModel = new SwingEntityModel(InvoiceLine.TYPE, connectionProvider);
invoiceModel.detailModels().add(invoiceLineModel);
// Configure detail model for optimal performance
invoiceLineModel.tableModel().queryModel().conditionRequired().set(true); // Don't load all lines
invoiceLineModel.tableModel().queryModel().limit().set(1000); // Reasonable limit
7.2. Building Custom Links
Create links with specific behavior:
SwingEntityModel customerModel = new SwingEntityModel(Customer.TYPE, connectionProvider);
SwingEntityModel invoiceModel = new SwingEntityModel(Invoice.TYPE, connectionProvider);
ModelLink<SwingEntityModel, SwingEntityEditModel, SwingEntityTableModel> customLink =
customerModel.link(invoiceModel)
.active(true)
.onSelection(selectedCustomers -> {
// Custom selection logic
if (selectedCustomers.size() > 1) {
// Handle multi-selection differently
invoiceModel.tableModel().queryModel().condition().clear();
invoiceModel.tableModel().queryModel().where().set(() ->
Invoice.CUSTOMER_FK.in(selectedCustomers)
);
}
})
.build();
customerModel.detailModels().add(customLink);
7.3. Automatic Foreign Key Management
The ForeignKeyModelLink specializes ModelLink
for foreign key relationships:
SwingEntityModel customerModel = new SwingEntityModel(Customer.TYPE, connectionProvider);
SwingEntityModel invoiceModel = new SwingEntityModel(Invoice.TYPE, connectionProvider);
// ForeignKeyModelLink is created automatically when foreign key is detected
customerModel.detailModels().add(invoiceModel);
// Or configure explicitly
customerModel.detailModels().add(ForeignKeyModelLink.builder(invoiceModel, Invoice.CUSTOMER_FK)
// Clear foreign key value when master has no selection
.clearValueOnEmptySelection(true)
// Set foreign key value automatically on insert
.setValueOnInsert(true)
// Control when to refresh detail data
.refreshOnSelection(true)
// Filter detail records based on master selection
.setConditionOnInsert(true)
.build());
7.4. Simple One-to-Many
Classic master-detail relationship:
// Invoice -> InvoiceLines
SwingEntityModel invoiceModel = new SwingEntityModel(Invoice.TYPE, connectionProvider);
SwingEntityModel invoiceLineModel = new SwingEntityModel(InvoiceLine.TYPE, connectionProvider);
invoiceModel.detailModels().add(invoiceLineModel);
// Configure detail model for optimal performance
invoiceLineModel.tableModel().queryModel().conditionRequired().set(true); // Don't load all lines
invoiceLineModel.tableModel().queryModel().limit().set(1000); // Reasonable limit
7.5. Multi-Level Hierarchy
Deep master-detail chains:
// Customer -> Invoice -> InvoiceLine
SwingEntityModel customerModel = new SwingEntityModel(Customer.TYPE, connectionProvider);
SwingEntityModel invoiceModel = new SwingEntityModel(Invoice.TYPE, connectionProvider);
SwingEntityModel invoiceLineModel = new SwingEntityModel(InvoiceLine.TYPE, connectionProvider);
// Build hierarchy
customerModel.detailModels().add(invoiceModel);
invoiceModel.detailModels().add(invoiceLineModel);
// Configure each level
invoiceModel.tableModel().queryModel().conditionRequired().set(true);
invoiceLineModel.tableModel().queryModel().conditionRequired().set(true);
// Selection cascades down the hierarchy automatically
Entity customer = getCustomer(connectionProvider);
customerModel.tableModel().selection().item().set(customer);
// Invoices for customer are loaded
// When an invoice is selected, its lines are loaded
invoiceModel.tableModel().selection().indexes().increment();// selects first
8. EntityApplicationModel
The EntityApplicationModel class serves as the base for the application. Its main purpose is to hold references to the root EntityModel instances used by the application.
When extending this class you must provide a constructor with a single EntityConnectionProvider parameter, as seen below.
public class StoreApplicationModel extends SwingEntityApplicationModel {
public StoreApplicationModel(EntityConnectionProvider connectionProvider) {
super(connectionProvider, List.of(createCustomerModel(connectionProvider)));
}
private static SwingEntityModel createCustomerModel(EntityConnectionProvider connectionProvider) {
CustomerModel customerModel =
new CustomerModel(connectionProvider);
CustomerAddressModel customerAddressModel =
new CustomerAddressModel(connectionProvider);
customerModel.detailModels().add(customerAddressModel);
//populate the model with rows from the database
customerModel.tableModel().items().refresh();
return customerModel;
}
}
9. Application load testing
The application load testing harness is used to see how your application, server and database handle multiple concurrent users.
This is done by using the LoadTestModel and LoadTestPanel classes as shown below.
public class StoreLoadTest {
private static final class StoreApplicationModelFactory
implements Function<User, StoreApplicationModel> {
@Override
public StoreApplicationModel apply(User user) {
EntityConnectionProvider connectionProvider =
RemoteEntityConnectionProvider.builder()
.user(user)
.domainType(Store.DOMAIN)
.build();
return new StoreApplicationModel(connectionProvider);
}
}
private static class StoreScenarioPerformer
implements Performer<StoreApplicationModel> {
private static final Random RANDOM = new Random();
@Override
public void perform(StoreApplicationModel application) {
SwingEntityModel customerModel = application.entityModels().get(Customer.TYPE);
customerModel.tableModel().items().refresh();
selectRandomRow(customerModel.tableModel());
}
private static void selectRandomRow(EntityTableModel<?> tableModel) {
if (tableModel.items().visible().count() > 0) {
tableModel.selection().index().set(RANDOM.nextInt(tableModel.items().visible().count()));
}
}
}
public static void main(String[] args) {
LoadTest<StoreApplicationModel> loadTest =
LoadTest.builder(new StoreApplicationModelFactory(),
application -> application.connectionProvider().close())
.user(User.parse("scott:tiger"))
.scenarios(List.of(scenario(new StoreScenarioPerformer())))
.name("Store LoadTest - " + EntityConnectionProvider.CLIENT_CONNECTION_TYPE.get())
.build();
loadTestPanel(loadTestModel(loadTest)).run();
}
}