Preface

CUBA Platform is an open source framework intended to streamline the development process of business applications. It combines proven architecture, ready-to-use enterprise-grade components and productive tooling, so you can deliver modern web applications faster than ever before.

In this quick start we will scratch the surface of CUBA and develop a very simple, but fully functional conference planner application. It will show three main things for creating any web application: how to design data model, how to manipulate data, how to create business logic and, finally, how to create a user interface. As an example, we will create a simple, but fully functional version of an application that will help with session planning for a conference. In fact, this tutorial will be just enough for you to start your own CUBA application, so let's get started. In this tutorial, we will use CUBA Studio,so please install it before you begin and accept trial subscription to have access to the visual designers.

Sample code repo: https://github.com/cuba-platform/sample-session-planner.

Creating an empty project

Let’s create an empty CUBA project and name it SessionPlanner using the corresponding Intellij IDEA menu.
We will use Java 8 as the default JDK.

Data Model Creation

The first task - creating entities. The business domain model has only two classes: Speaker and Session. The relation is one-to-many. One speaker can conduct many sessions.

For a starter, let’s create the Speaker entity. To do this, we can use a link on project’s welcome page:

Or just right-click on the “Data Model” node in CUBA Project Tree on the left side of the screen and select “New -> Entity”

Enter entity’s name - Speaker and create attributes according to the specification:

NameTypeMandatoryOther constraints
firstNameString (255)Yes
lastNameString (255)
emailString (1024)Yes“Email” validator

In CUBA we use standard JPA entities, so you can create them using either code editor or visual designer. Just click “+” icon and add attributes to the entity, CUBA Studio will generate class members for you.

In CUBA, you can specify a format for a proper entity display as a string in the UI using “Instance Name”. For the speaker, we will select both the first and last names.

If we have a look at the “Text” tab on the bottom of the entity designer, we can see just a JPA-annotated java class. The generated code can be updated if needed, designer will be updated accordingly when you switch to the “Designer” tab.

Let’s move further and create the Session entity and link it to our Speaker class. Fields specification table follows. The session end time will be a calculated value, being one hour from the start.

NameTypeMandatory
topicString (255)Yes
startDateDateTimeYes
endDateDateTime
speakerAssociation to “Speaker” entity, many-to-one cardinalityYes
descriptionString (2000)
The principle is pretty much the same, but the “speaker” attribute will be selected in the UI, so we need to specify “lookup” in “Lookup Actions” section of the editor and “Dropdown” as a Lookup type. Finally, the field definition should look like this:

Creating Calculated Attribute

In CUBA, you can add field calculation logic using an entity’s lifecycle callbacks. You need to create a method and mark it with a proper java annotation. The method will be invoked automatically. Let’s add a method that will be invoked at the “PrePersist” lifecycle phase. To do this, you can click on button “Lifecycle Callbacks” on the top on the entity designer window and select the desired lifecycle stage.

Name the method “updateEndDate” and mark it with @PreUpdate annotation in addition to @PrePersist. We will create a separate method for the session end time calculation, to reuse it in the application later.

public static Date calculateEndDate(Date startDate) {
  return Date.from(startDate.toInstant().plus(1, ChronoUnit.HOURS));
}

And then we’ll invoke the method from the lifecycle handler that we created in the previous step:

@PrePersist
@PreUpdate
public void updateEndDate() {
endDate = calculateEndDate(startDate);
}

That’s it. Our domain model has been created.

Database Creation

By default, CUBA Studio uses HSQL database during the development stage and generates SQL scripts specific for this RDBMS. Select “CUBA -> Generate Database Scripts” menu to create SQL for the database creation. You can review the generated scripts in the pop-up window before saving them as project files. Please note that those scripts are a part of the project, you can find them under “Main Data Store” node in the project tree.

You can modify the scripts if you want to add some specific things like additional indexes or insert statements for the initial data.

Click on “Save and close” button to save scripts. To apply those scripts and create the database, just select “CUBA -> Create Database” menu. Apart from application tables, CUBA creates system tables where we store information about users, roles, tasks, etc.

That’s it. The database has been created. Now we can create a simple UI for CRUD operations over the data in the database.

Generating CRUD Screens

CUBA Studio contains a UI screen generation wizard that helps us to create basic, but useful UI screens:

  • Browser - to show the list of entities in a grid
  • Editor - to edit one entity instance using a form-like user interface

First, we will create screens to work with speakers. Since this entity is pretty simple, we will use the default parameters for the screen creation. Start a wizard by clicking on “Create Screen” menu item in “Screens” menu on the top of the entity designer.

You can also start screen generation wizard by right-clicking on an entity in CUBA Project tree and selecting “New -> Screen” from the context menu.

For the “Speaker” entity, we will create default browser and editor. Select “Browser and Editor” on “Screen Templates” tab in the wizard and click “Next”

Leave default parameters on the next screen and click “Next”.

In the next step, you can change the screen titles and application menu item name. Then click “Finish” to generate CRUD screens.

As you can see, each screen consists of two parts: a controller, written in java, which is responsible for internal screen logic and events handling and an XML layout that defines the screen appearance. In our case, the browser will consist of files “speaker-browse.xml” and “SpeakerBrowse.java” and editor - “speaker-edit.xml” and “SpeakerEdit.java” accordingly. Source files are located under “Generic UI -> Screens” menu in CUBA Project tree.

Please note the “DATA” section in the XML screen descriptors - it defines how the data is fetched from the database.

<data readOnly="true">
   <collection id="speakersDc"
               class="com.company.sessionplanner.entity.Speaker"
               view="_local">
       <loader id="speakersDl">
           <query>
               <![CDATA[select e from sessionplanner_Speaker e]]>
           </query>
       </loader>
   </collection>
</data>

You can easily navigate between screen controller, descriptor and linked entities with CUBA Studio using buttons on the top of the window:

Creating a browser and editor for sessions

Run the screen generation wizard, select “Entity browser and editor screens” option and click “Next”.
On the next screen we will create “Browse view” and “Edit view” for the screens. In CUBA Platform, Entity View specifies which fields will be fetched from the database. This information can be used not only during the screen creation but also for the proper API design.

Create a separate view for the “Session” entity to include the speaker reference for the browser screen. In the “Browse View” dropdown select “Create view…”

In the popup window enter “session-browse-view” in the “Name” field and select “speaker” attribute in attributes selection tree. The final configuration will look like this:

By selecting “speaker” attribute we instruct CUBA to fetch data from the “Speaker” table to show a speaker’s first and last names on the session browser screen.

Press “OK” to save the new entity view.

For a session, we do not expose its end date for editing, because we will generate it. Create “session-edit-view” as an Edit view, but in the “Extends” dropdown select “_minimal” and then select all fields except “endDate”. This instructs CUBA not to fetch this attribute value from the database, but it will be saved during pre-persist or pre-update lifecycle phase. See “Creating Calculated Attribute” section for reference. Also, a widget on the screen will not be generated. Your view editor should look like this:

Press “OK” to save new entity view.

Now we’re OK to finish screen generation. Press “Next”, edit screen headers if needed. Now we need to tweak the editor screen a bit. If you open a session editor descriptor, you will see that “description” field is generated as a single-line text field.

Let’s convert it to multi-line. The easiest way is to open the XML text and change tag “textField” to “textArea” for the tag with id=“descriptionField”

<form id="form" dataContainer="sessionDc">
   <column width="250px">
       <textField id="topicField" property="topic"/>
       <dateField id="startDateField" property="startDate"/>
       <lookupPickerField id="speakerField" optionsContainer="speakersDc" property="speaker">
           <actions>
               <action id="lookup" type="picker_lookup"/>
           </actions>
       </lookupPickerField>
       <textArea id="descriptionField" property="description"/>
   </column>
</form>

Also please note that “endDate” is not shown on the editor screen, but you can see it on the session browser screen.

Running the application in development mode

To run the CUBA application you can use either run configuration on the top on the IDE window

Or select “CUBA -> Start Application Server” from the main menu.

After some time you can access the application using the browser. The URL will be displayed in the Run toolbox in IDEA. In our case it will be http://localhost:8080/app . Open the URL in your web browser and log into the application using “admin” as a username. The password is “admin” by default. You can find screens for entities manipulation under “Application” menu.
Then let’s add some data to the database: a couple of speakers and two sessions for them scheduled for the rest of the week. You can try to enter invalid email for a speaker and see that email validator works as expected.

Generated screens are good for basic operations, but in the real world, UI is usually more complex.

Customizing the User Interface

Let’s add a calendar view to browse visits in addition to the grid view. For the browser screen, we’ll add tab control, put a calendar on it and provide a functionality to edit and reschedule sessions using this calendar like in the picture below:

Open session-browse.xml file in designer and find “Tab Sheet” container in the component palette

Drag the TabSheet under a “filter” component in the component tree, then assign an id - “sessionsTabs” to it using “Properties” tab in the palette screen.

It is recommended to assign an id for each control, so we can refer to it. Components tree should look like this:

Drag two “Tab” components under “sessionsTabs” and assign ids “calendarTab” and “tableTab” to them. Then set captions “Sessions Calendar” and “Sessions Table” for these tabs accordingly.

Then collapse “sessionsTable” component and drag it to “tableTab”.

You will see that layout is broken because of our changes.

To fix this, we need to use a proper component that will expand to all screen area. Select “layout” component and set “expand” property to “sessionTabs” instead of invalid property “sessionsTable”, this will expand the tabsheet to the whole screen.

Now find “Calendar” component in the components palette and drag it on the “calendarTab” component in layout. Assign an id - “sessionsCalendar” to it.

Set “expand” property value for the “tableTab” container to “sessionsTable” and for “calendarTab” to “sessionsCalendar”.

In CUBA, UI components can be bound to entities and their properties.

Let’s bind the calendar to the data collection fetched in the screen. Just assign its “dataContainer” property to “sessionsDc”. Then bind:

  • startDateProperty to a session’s start Date
  • endDateProperty to a session’s end Date
  • captionProperty to a session’s topic
  • And descriptionProperty to a session’s description

To practice with the XML editor along with layout designer, we can update the descriptor and make the calendar to show only working hours and add navigation buttons. Try it, the XML editor supports autocomplete, so you won’t enter wrong attribute names. The calendar tag should look like this after you add three properties marked with bold font in the snippet below:

<calendar id="sessionsCalendar" dataContainer="sessionsDc" startDateProperty="startDate"
         endDateProperty="endDate" captionProperty="topic" descriptionProperty="description"
   firstVisibleHourOfDay="8" lastVisibleHourOfDay="18" navigationButtonsVisible="true"/>

To see the changes in the UI, you don’t need to restart the application, just reopen the screen in the application. CUBA framework supports hot deploy for screens. Now you can see sessions are displayed on the calendar.

Using Screen API

When we interact with the UI, events are generated, therefore CUBA gives you an API that you can use to subscribe to those events to handle them. Let’s handle a click on a calendar’s entry and invoke the session editor. For screen manipulations, CUBA provides a screen builder API that we will use.

In the SessionBrowse controller code editor click on “Subscribe to Event” button on the top of the window

and select CalendarEventClickEvent in the event selection popup, then press OK.

The empty method will be generated for you.

@Subscribe("sessionsCalendar")
private void onSessionsCalendarCalendarEventClick(Calendar.CalendarEventClickEvent event) {

}

To use screen manipulation API, we need to add ScreenBuilders service into the controller. Click on “Inject” button on the top of the window and select screenBuilders service form Screen API section in the popup window.

Another way for the injection (and subscription) - press Alt+Insert key combination in the editor and select “Inject” in the popup menu:

To search for the service, just start typing its name and then use “Up” and “Down” keys to navigate between services available for injection.

After screen builder service is injected, invoking a screen becomes a chain of method calls.
We need an editor for the session class with this (browser) as a parent screen. Then we want to edit the session entity received within the event object. The editor will be opened in the dialog mode. When the editor is closed, we need to reload all data to the browser screen. After that, we should show the editor that we have just built. In the handler’s code it will look like this:

@Subscribe("sessionsCalendar")
private void onSessionsCalendarCalendarEventClick(Calendar.CalendarEventClickEvent event) {
   Screen screen = screenBuilders.editor(Session.class, this)
           .editEntity((Session) event.getEntity())
           .withLaunchMode(OpenMode.DIALOG).build();
   screen.addAfterCloseListener(afterCloseEvent -> {
       getScreenData().loadAll();
   });
   screen.show();
}

That’s it. You can reopen the sessions browser screen in the running application and invoke the editor by clicking on the session in the calendar .

The editor doesn’t look nice, so we need to adjust its width and height. In the IDE, open the screen’s XML descriptor, select “dialogMode” property and set “width” and “height” properties to “auto”.

Go to the application and reopen the editor by closing it and clicking on the session in the calendar again. Now it looks nicer.

Adding Business Logic

Now we will use CUBA Studio to create a service that implements business logic and use this service in a screen. It will be a service for session rescheduling that will check that one speaker doesn’t have two sessions at the same time.

Right-click on the service node in the CUBA project tree and select “New ->Service”. This will open a service creation dialog. Enter “SessionService” as an interface name, SessionServiceBean as an implementation name will be generated automatically.

Create a method “rescheduleSession” in the interface like in the code snippet below:

public interface SessionService {
   String NAME = "sessionplanner_SessionService";

   boolean rescheduleSession(Session session, Date newStartDate);
}

The method will accept a session and a new session date and time. If we are able to reschedule the session, we save it with a new start date and time and if not - just return false as the service execution result.

To implement the method, open code editor for the SessionServiceBean class, you can find it under “Middleware - Services” node in the CUBA project tree:

You’ll see that the class is empty and invalid:

Press Alt+Insert in class’ body and select “Implement methods in the popup menu:

Select “rescheduleSession” method, the code stub will be generated:

@Service(SessionService.NAME)
public class SessionServiceBean implements SessionService {

   @Override
   public boolean rescheduleSession(Session session, Date newStartDate) {
       return false;
   }
}

For the service, we will use CUBA API for data access - DataManager class. With this class we’ll create a JPQL query to check if there are any sessions scheduled for the speaker in a defined time span. Then we will check the query result and, depending on it, update the session with a new start date or just exit from the method with a corresponding result.

Inject the DataManager into the service by pressing Alt+Insert in the class body and select “Inject” from the popup menu:

Select DataManager in the popup window:

The method implementation is on the snippet below:

@Override
public boolean rescheduleSession(Session session, Date newStartDate) {

   Date newEndDate = Session.calculateEndDate(newStartDate);

   int sessionsSameTime = dataManager.load(Session.class)
           .query("select s from sessionplanner_Session s where " +
                   "s.startDate < :newEndDate and s.endDate > :newStartDate " +
                   "and s.speaker = :speaker " +
                   "and s.id <> :sessionId")
           .parameter("newStartDate", newStartDate)
           .parameter("newEndDate", newEndDate)
           .parameter("speaker", session.getSpeaker())
           .parameter("sessionId", session.getId())
           .list().size();

   if (sessionsSameTime == 0) {
       session.setStartDate(newStartDate);
       dataManager.commit(session);
       return true;
   }

   return false;
}

Please note that we’ve reused the method that was created earlier in the section “Creating Calculated Attribute”:

Date newEndDate = Session.calculateEndDate(newStartDate);

The service is ready, now let’s add it to the session browser screen. It will be invoked for the session drag-and-drop in the calendar. If the session cannot be rescheduled, we show an on-screen notification using the notification API that we will add to the screen.

Go to session browser screen’s controller and inject the newly created service into the screen controller code as we did for Screen API. Then inject the “Notifications” API in the same way. And finally, subscribe to the “CalendarEventMove” event for the calendar component similar to what we did for the session editor invocation.

Then we can create the following code in the event handler:

@Subscribe("sessionsCalendar")
private void onSessionsCalendarCalendarEventMove(Calendar.CalendarEventMoveEvent event) {

   Session session = ((EntityCalendarEvent<Session>)event.getCalendarEvent()).getEntity();

   if (!sessionService.rescheduleSession(session, event.getNewStart())) {
       notifications.create(Notifications.NotificationType.WARNING)
       .withCaption("Session "+session.getTopic()+" cannot be rescheduled to "+event.getNewStart()+" due to a conflict")
       .show();
   }

   getScreenData().loadAll();
}

To have the new service redeployed, we need to restart the application, we can use the same “Run” button in IDEA .

After restarting we can open sessions calendar - and voila! We have drag-and-drop rescheduling functionality for the calendar! Let’s test it by adding an extra session and trying to reschedule it.

Adding Branding

You can customize CUBA applications by changing default text in standard screens like main screen or login screen. Let’s update text according to the application business domain - conference planning.

Open main message pack file - “messages.properties” which is situated under “Generic UI - Main Message Pack” node in CUBA project tree.

It is a standard java .properties file, so you can edit it freely, adding new message keys and updating existing ones. Update messages to reflect the purpose of the application. The example is below:

application.caption = CUBA Session Planner
application.logoImage = branding/app-icon-menu.png
application.logoLabel = Session Planner

loginWindow.caption = Login
loginWindow.welcomeLabel = Welcome to Session Planner!
loginWindow.logoImage = branding/app-icon-login.png

menu-config.application-sessionplanner = Planner
menu-config.sessionplanner_Speaker.browse=Speakers
menu-config.sessionplanner_Session.browse=Sessions

With CUBA’s hot deploy feature, all we need is to log out and log in to the application again to see the changes.

Marketplace

The framework comes with a marketplace that contains plenty of components, so you can easily add useful features like charts or maps support to the existing application. You can install those components right from the studio using the “CUBA -> Marketplace” menu.

Conclusion

CUBA framework provides a lot of useful APIs to help you with creating business applications quickly. This quickstart shows only the basics of the CUBA framework and CUBA Studio. You can find more examples and tutorials on our website: cuba-platform.com.

Thank you for you interest to CUBA Platform. Enjoy development with CUBA!