Skip to content

Developer Guide

This section covers the data model, configuration reference, and extension points available to developers integrating User Profile into an application.

Data Model

User Profile stores one record per user. The entity is intentionally simple: a flat table keyed by a generated integer identifier and associated to its owner by username.

---
config:
  layout: elk
---
erDiagram
    up_user_profile {
        integer id PK
        varchar username
        varchar first_name
        varchar last_name
        varchar phone_number
        varchar address
        varchar email
        blob avatar
        timestamp creation_time
    }
Hold "Alt" / "Option" to enable pan & zoom

Database Schema

User Profile manages a single table. The schema below represents the DDL generated by Hibernate for a standard relational database. Column names follow Hibernate's default snake_case naming strategy.

CREATE SEQUENCE up_user_profile_seq
    START WITH 1
    INCREMENT BY 50;

CREATE TABLE up_user_profile (
    id             INTEGER        NOT NULL,
    username       VARCHAR(255),
    first_name     VARCHAR(255),
    last_name      VARCHAR(255),
    phone_number   VARCHAR(255),
    address        VARCHAR(255),
    email          VARCHAR(255),
    avatar         BLOB,
    creation_time  TIMESTAMP(6),
    CONSTRAINT pk_up_user_profile PRIMARY KEY (id)
);

The avatar column stores raw binary image data. Its exact SQL type depends on the database dialect: BLOB on MySQL and H2, BYTEA on PostgreSQL.

The up_user_profile_seq sequence is created by Hibernate when GenerationType.AUTO resolves to a sequence-based strategy, which is the default behaviour in Hibernate 6.

Module Overview

User Profile is structured as six Maven modules following the AppJars layered architecture:

Module Artifact ID Description
Model appjars-user-profile-model DTOs, filter and sort classes, auto-configuration
Business API appjars-user-profile-business UserProfileService interface
Business Impl appjars-user-profile-business-impl Service implementation with @Transactional operations
Data API appjars-user-profile-data UserProfileDao interface
Data Impl appjars-user-profile-data-impl JPA entity and DAO implementation
Flow UI appjars-user-profile-flow Vaadin views, forms, and route configuration

A monolithic application includes the three implementation modules (-business-impl, -data-impl, -flow). The API modules (-business, -data) are pulled in transitively.

Spring Auto-Configuration

User Profile registers itself through Spring Boot's auto-configuration mechanism. The entry point is UserProfileAutoConfiguration, declared in:

META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports

This class scans the com.appjars.userprofile package for all Spring components and JPA entities, so no additional @EntityScan or @ComponentScan declarations are required in the host application beyond those described in the Getting Started guide.

Configuration Properties

The following properties can be set in application.properties to customise the behaviour of User Profile:

Property Default Description
com.appjars.userprofile.url.profile up/profile URL path of the personal profile view
com.appjars.userprofile.url.profile-list up/profile-list URL path of the administrator profiles list
com.appjars.userprofile.feat.redirect true When true, users without a profile are automatically redirected to the profile view on their first login
spring.servlet.multipart.max-file-size 1MB Maximum size of individual avatar image uploads. Set to 100MB to allow full-resolution images
spring.servlet.multipart.max-request-size 10MB Maximum size of multipart requests. Should match max-file-size

Service API

UserProfileService is the primary programmatic interface to User Profile. It extends CrudService and exposes the following methods specific to the module:

// Find a profile by username
Optional<UserProfileDto> findByUsername(String username);

// Retrieve a paginated, filtered, and sorted stream of profiles
Stream<UserProfileDto> getProfiles(int offset, int limit, ProfileFilter filter, List<ProfileSort> sortOrder);

// Count profiles matching a filter
Integer countProfiles(int offset, int limit, ProfileFilter filter);

Inject UserProfileService in any Spring-managed component to read or write profiles programmatically:

@Autowired
private UserProfileService userProfileService;

public Optional<UserProfileDto> getCurrentUserProfile(String username) {
    return userProfileService.findByUsername(username);
}

Events

UserProfileService publishes a ProfileUpdatedEvent Spring application event each time a profile is created or updated. Subscribe to it in any Spring component using @EventListener:

@Component
public class ProfileSyncListener {

    @EventListener
    public void onProfileUpdated(ProfileUpdatedEvent event) {
        String username  = event.username();
        String firstName = event.firstName();
        String lastName  = event.lastName();
        String email     = event.email();
        // custom logic: update search index, sync to external system, etc.
    }
}

The event is a Java record and carries the username, first name, last name, and email of the updated profile. No event is published on deletion.

Free Mode Enforcement

Without a valid User Profile license, the appjar runs in free mode and limits the application to five profiles. The limit is enforced in UserProfileServiceImpl, which throws a FreeLimitReachedException when save is called and five profiles already exist, or when update is called and the count is above five. Both views catch the exception and display an alert, so a custom caller of the service API must handle it as well.

The views also reflect the limit before the operation is attempted: they display a restrictions bar with a Profiles usage badge, disable the New profile button, and disable the Save button of the profile form. In addition, AuthenticationListener suppresses the post-login redirect while the limit is reached, so that users without a profile are not sent to a view where they cannot save one.

Customisation

Replacing Username Validation

The username entered in the profile form is validated by a UsernameValidator bean, a Vaadin Validator<String>. The default implementation, DefaultUsernameValidator, requires between 4 and 50 characters, rejects values containing spaces, and additionally applies email validation when the value contains an @ character.

The bean is registered with @ConditionalOnMissingBean, so declaring a UsernameValidator in the application replaces it:

@Bean
UsernameValidator usernameValidator() {
    return (value, context) -> value != null && value.startsWith("emp-")
        ? ValidationResult.ok()
        : ValidationResult.error("Username must start with 'emp-'");
}

Providing Profile Data from Another Source

Other AppJars obtain the first name, last name, and profile picture of a user through the UserProfileProvider interface. User Profile registers DefaultUserProfileProvider, which resolves that data through UserProfileService and maps the profile avatar to the profile picture.

The bean is registered with @ConditionalOnMissingBean, so an application that stores this information elsewhere — an LDAP directory, an external identity provider, a legacy table — can declare its own implementation:

@Bean
UserProfileProvider ldapUserProfileProvider(LdapTemplate ldapTemplate) {
    return username -> ...;
}

The interface returns the shared com.appjars.model.utils.UserProfileDto, which carries only the first name, last name, and profile picture. It is not the same class as the UserProfileDto of this appjar.

Assigning a Router Layout

By default, User Profile registers its views without a parent layout. To wrap them in the application's main layout, inject the RouteConfigurer bean and call setViewsRouterLayout in a @PostConstruct method:

@Autowired
private RouteConfigurer routeConfigurer;

@PostConstruct
public void configure() {
    routeConfigurer.setViewsRouterLayout(MainLayout.class);
}

Customising View URLs

The default URL paths can be overridden in application.properties:

com.appjars.userprofile.url.profile=myapp/my-profile
com.appjars.userprofile.url.profile-list=myapp/manage-profiles

Disabling the Profile Redirect

When com.appjars.userprofile.feat.redirect=true (the default), users who have not yet created a profile are automatically redirected to the My Profile view after login. To disable this behaviour:

com.appjars.userprofile.feat.redirect=false