Soloist Tutorial

About SOLoist

SOLoist is a Java-based software framework intended for

  • model-driven development (MDD) using UML,
  • rapid prototyping and implementation, and
  • execution

of object-oriented information systems (OOIS), such as enterprise business and other kinds of database applications for Web 2.0, based on high-level, executable UML models.

About This Tutorial

The purpose of this Tutorial is to present a selected subset of main features of SOLoist, using a tiny demonstrative example called Company Organization. In that example, you are going to build a simple human-resource information system for a fictive company. The demo application will be fully functional, with a friendly graphical user interface and many powerful features. The demo application may be arbitrarily extended, and you are invited to do that on your own.

It is important to note that this Tutorial does not explain the basic UML concepts and some basic principles of the system development and execution in SOLoist. You are referred to the following book for further reading (referred to as the Book):

Milicev, D. Model-Driven Development with Executable UML, John Wiley and Sons, July 2009, ISBN 9780470481639

The Tutorial will be presented in two parts – the first one is focused on UML modeling and the executable nature of UML models in SOLoist, while the second part is focused on building customizable GUIs in SOLoist. Each part will introduce new elements of the demo application and refer to a section in the Book for in-depth explanations of the used concepts.

In this Tutorial, you will be provided with detailed instructions for developing and executing the accompanying demo application.

Construction Principles

SOLoist is an implementation of an executable profile of UML named OOIS UML, thoroughly described in the book:

Milicev D., Model-Driven Development with Executable UML, Wiley/Wrox, July 2009, ISBN 9780470481639

System development with SOLoist encompasses the following phases, languages, and tools:

  • Modeling in UML with StarUML (a free, open-source UML modeling tool, http://staruml.sourceforge.net/en/). Other modeling tools will be supported in future versions.
  • Generation of the application’s source code in Java using a customized SOLoist code generator for StarUML.
  • Generation of the relational database schema for the application’s persistent storage. SOLoist generates schemas for most of the popular database management systems, including Oracle, Microsoft SQL Server, Microsoft Access, Sybase, MySQL, and PostgreSQL.
  • Integration of the generated code with the SOLoist runtime environment (the Kernel). The runtime environment is a middleware component that maps the UML-based object semantics into the relational database and ensures object persistence without any additional developer’s effort and completely transparently. The runtime environment is provided as a library that is linked with the generated application code.
  • The SOLoist runtime environment uses Google Web Toolkit (GWT) for providing Web 2.0 based GUIs, but adds significant value and a new paradigm for building GUIs efficiently.

System execution relies on the following components:

  • Apache Tomcat as the application server.
  • Relational database management system (RDBMS) for persistent storage. Oracle, Sybase SQL Anywhere, MySQL are supported, and any other RDBMS that supports the usual relational paradigm, SQL (Structured Query Language), and ODBC can be easily supported, too.
  • Applications are accessible in all modern Web browsers.

Requirements

This tutorial assumes you have followed installation instructions and installed all the required software.

You are required to download Company Organization Project - a preconfigured project with all the required libraries and an empty model. In this tutorial, we will turn this project into a full-featured application.

Once you download this project, you need to import it into your Eclipse workspace and set it up on the server (if you don't know how to do that, see Running Empty Project).

Since you will be generating SOLoist code with StarUML, you will need to set up the path where the .java files will be generated. Start StarUML and open CompanyOrganization.uml which is located in the model folder of the downloaded project. Right click on the CompanyOrganization model, and select Tagged Values. Select JavaSOL tab and in CodeGenerationPath field, enter the absolute path to the src directory of the Company Organization project (e.g. D:\CompanyOrganization\src), and press ENTER.

Now you are all set to dive into SOLoist programming!

Classes and Attributes

In this step, we will develop a simple UML model with just a few key abstractions from the problem domain. We will learn:

  • How SOLoist supports UML classes and attributes
  • What built-in data types are supported in SOLoist
  • How to create SOLoist objects, and set their attributes in Java code

The Model

We will start with a simple conceptual model of the problem domain, covering two key abstractions: Department and Employee, as shown in the following UML class diagram:

Company Organization Domain Model

The meanings of the classes and their attributes are self-explanatory. The types of the attributes (except for Gender) are some of the SOLoist built-in data types. The enumeration Gender is domain-specific (custom).

For more information

To learn about the concepts used in this step, read:

  • Chapter 5, Section “Classes and Attributes” on page 49
  • Chapter 8 on page 181
  • Chapter 9 on page 247

in the Book.

Development Procedure

1. Open the CompanyOrganization.uml model which resides in model folder of the Company Organization project. This is an empty template model, pre-equipped with built-in SOLoist profiles and model libraries.

2. Create the presented UML model of the problem domain. Organize the model of this domain in a separate package named CompanyOrganization, and stereotype that package as <<domain>>.

When deleting an element from a diagram, StarUML doesn't delete it from the model. To delete an element from the model, select it and click CTRL + DELETE, or right-click on it and select Delete From Model.

Important note: If you want to skip some steps later in the described procedures and take the completed pieces of application, you must ensure that all your previous steps have strictly followed the given instructions, in particular, the naming of all items and elements of your application. This includes the name of the application itself, its UML package (it must be CompanyOrganization), classes, attributes, associations, association ends, etc. Every single character is important, and names are case- and space-sensitive. This is because the ready-to-use artifacts, such as GUI configurations, that you may take to skip some steps, refer to those names in the application you have created, and may not work properly if you have used other names. Of course, if you have followed the procedure and developed the application yourself, and you do not want to use the prepared artifacts to skip some steps, you are free to experiment with different naming.

If you want to skip this step:

To skip this step, download the completed StarUML model.

3. Save or export the developed UML model to the XMI format (File->Export->XMI) and save it inside the src folder of the Eclipse's Company Organization Project, overwriting companyorganization.xmi. Make sure you check XMI 1.1 for UML 1.3 in the Export to XMI dialog.

Exporting to XMI

If you want to skip this step:

To skip this step, download the XMI file for the completed model and copy it over the companyorganization.xmi.

4. Generate SOLoist code by right clicking on the CompanyOrganization package->SOLoist Plug-In->Generate SOLoist Code. This will create a package companyorganization in the src folder of your project, and three Java classes corresponding to those created in the model.

5. Now let's try to create a couple of instances of the created classes. Open up ObjectSpaceInit.java. This class is used for pre-populating the object space, and it gets called each time the server is started, after the database schema has been generated. Generating the database schema removes all other objects form the object space.

public void init() {
	
	// Adding departments
	rootDepartment = new Department();
	rootDepartment.name.set(Text.fromString("SOL"));
	Department d2 = new Department();
	d2.name.set(Text.fromString("Headquarters"));
	Department d3 = new Department();
	d3.name.set(Text.fromString("Research & Development"));
	Department d4 = new Department();
	d4.name.set(Text.fromString("Production"));
	Department d5 = new Department();
	d5.name.set(Text.fromString("Commerce"));
	Department d6 = new Department();
	d6.name.set(Text.fromString("Development"));
	Department d7 = new Department();
	d7.name.set(Text.fromString("Quality Assurance"));
	Department d8 = new Department();
	d8.name.set(Text.fromString("Marketing"));
	Department d9 = new Department();
	d9.name.set(Text.fromString("Sales"));
	Department d10 = new Department();
	d10.name.set(Text.fromString("Support"));
	
	// Adding employees
	Employee e1 = new Employee();
	e1.name.set(Text.fromString("Dragan Milicev"));
	e1.gender.set(Gender.MALE);
	e1.isMarried.set(new Boolean(true));
	e1.numberOfChildren.set(new Integer(3));

	Employee e2 = new Employee();
	e2.name.set(Text.fromString("Zarko Mijailovic"));
	e2.gender.set(Gender.MALE);

	Employee e3 = new Employee();
	e3.name.set(Text.fromString("Milorad Pop-Tosic"));
	e3.gender.set(Gender.MALE);

	Employee e4 = new Employee();
	e4.name.set(Text.fromString("Nemanja Kojic"));
	e4.gender.set(Gender.MALE);

	Employee e5 = new Employee();
	e5.name.set(Text.fromString("Sanja Mitrovic"));
	e5.gender.set(Gender.FEMALE);

	Employee e6 = new Employee();
	e6.name.set(Text.fromString("Andrija Misevic"));
	e6.gender.set(Gender.MALE);

	Employee e7 = new Employee();
	e7.name.set(Text.fromString("Pedja Radenkovic"));
	e7.gender.set(Gender.MALE);

	Employee e8 = new Employee();
	e8.name.set(Text.fromString("Srdjan Lukovic"));
	e8.gender.set(Gender.MALE);

	Employee e9 = new Employee();
	e9.name.set(Text.fromString("Vukasin Milovanovic"));
	e9.gender.set(Gender.MALE);

	Employee e10 = new Employee();
	e10.name.set(Text.fromString("Ljubica Kuzelka"));
	e10.gender.set(Gender.FEMALE);
}

Here we created a couple of instances of Department and Employees, and set their attributes. Note that we create the departments like all regular Java objects, and we set their attributes by using the set method to which we pass either a built-in SOLoist datatype instance, or an enumeration instance (such as Gender.MALE).

Since we still have not worked on the UI, you won't be able to see those objects in action. If you are interested, inspect the database to see that these objects are there when you start the server, or use the SOLoist explorer (the OQL Browser for SOLoist applications).

Now, let's move on to adding associations.

If you want to skip step 5:

Download the complete ObjectSpaceInit.java.

Associations

In this step, we will extend our starting UML model with a few associations. We will learn:

  • How SOLoist supports UML associations

The Model

We will extend the starting model with two associations, shown in the following UML class diagram:

Domain Model

  • The association organization conceptualizes the fact that Departments can be organized hierarchically, meaning that one Department (the “super Department,” superDept) can be comprised of zero to arbitrarily many other Departments (the “sub-Departments,” subDepts). This association is a composition, with superDepts being the whole end (denoted with the filled diamond).
  • The association assignment conceptualizes the fact that Employees can be assigned to Departments, being their members.

For more information:

To learn about the concepts used in this step, read:

  • Chapter 5, Section “Associations” on page 62
  • Chapter 10 on page 281

in the Book.

Development Procedure

1. Add the described associations to the UML model in StarUML.

If you want to skip this step:

To skip this step, download the completed StarUML model.

2. Save or export the developed UML model to the XMI format. Changing the XMI file influences the process of regenerating the database schema because the database schema needs to reflect the classes and associations which are inside the model. This happens on server startup.

If you want to skip this step:

To skip this step, download the XMI file for the completed model.

3. Since you changed the classes in the UML model you have to generate SOLoist code again. As you already did in step 4 in previous chapter, you generate SOLoist code by right clicking on the CompanyOrganization package->SOLoist Plug-In->Generate SOLoist Code. This will generate .java files for all classes in the selected package.

After exporting new version of xmi file or generating SOLoist code you should refresh the Project Explorer in your Eclipse to make sure that it is working with the latest version of files from the filesystem.

4. Now we will show you how to link instances created in step 5 of the previous chapter, by adding associations to the object space. We will extend ObjectSpaceInit.java and link the created instances by setting references between them. After restarting the server updated database will be generated and all instances of classes with assigned associations will be recreated. Let's extend the existing code in ObjectSpaceInit.java with the code shown below.

 

public void init() {
	// Adding subdepartments		
	rootDepartment.subDepts.add(d2);
	rootDepartment.subDepts.add(d3);
	rootDepartment.subDepts.add(d4);
	rootDepartment.subDepts.add(d5);
	d4.subDepts.add(d6);
	d4.subDepts.add(d7);
	d5.subDepts.add(d8);
	d5.subDepts.add(d9);
	d5.subDepts.add(d10);
	
	// Adding employees to their departments
	d2.members.add(e1);
	d3.members.add(e2);
	d6.members.add(e3);
	d7.members.add(e4);
	d5.members.add(e5);
	d4.members.add(e6);
	d10.members.add(e7);
	d8.members.add(e8);
	d9.members.add(e9);
	d3.members.add(e10);	
}

Here we create links by associations between instances of Department and Employee. Note that associations are created in regular Java code by using the add method which we pass a SOLoist object.

Since we still have not worked on the UI, you won't be able to see those objects in action. If you are interested, inspect the database to see that these objects are there when you start the server, or use the SOLoist explorer (the OQL Browser for SOLoist applications).

Now we can move on to creating the UI.

If you want to skip step 4:

Download the complete ObjectSpaceInit.java.

Building the UI

We will now construct a quick and simple starting Web UI for our application. We will learn:

  • that user interfaces built in SOLoist are based on configurations of UI components and their pin bindings
  • how to configure UI at runtime
  • what are commands and how to configure them.

UI Design

The GUI will have the following general appearance:

Home page

The main structure of the GUI is as follows:

  • The title bar (1) at the top of the screen is a static graphic.
  • The central panel (2) brings the main presentational contents.
  • The command bar (3) with the set of commands that is sensitive to the current contents of the central panel.
  • The status bar (4) at the bottom shows the status of the AJAX calls dispatched from the Web client as follows:
    • Each yellow circle represents one initiated AJAX call (request for information).
    • Each blue circle represents one dispatched command.
    • When an initiated request (an AJAX call or a dispatched command) is completed successfully, the circle changes its color to green and disappears after a few seconds.
    • When an initiated request (an AJAX call or a dispatched command) fails, the circle changes its color to red and disappears after a few seconds.

The central panel will have two screens. The first one, shown in the picture above, is the home page screen which is initially displayed. Its elements are as follows. The tree view on the left (5) renders the hierarchical structure of Departments and their sub-departments. When a Department is selected in the tree view, its name and description can be edited in the text boxes on the right (6). Additionally, the list of Employees assigned to the selected Department gets displayed on the right (7).

The list of command buttons in the command bar of this panel is as follows:

  • "Create Employee": when a Department is selected in the tree view (and only then), this command will create a new Employee and assign it to the selected Department.
  • "Create Subdepartment": when a Department is selected in the tree view (and only then), this command will create a new Department as a sub-department of the selected Department.
  • Each Employee in the list can be double-clicked, and then the second panel in the central panel area gets shown:

Employee

This panel has two tabs. The tab "General" shown above displays all attributes of the selected Employee. In addition, it displays an iconic and short textual representation of the selected object (8), as well as for the Department of that Employee (9). These two representations are provided for convenience only; for instance, double-clicking on the icon for the Department displays the first panel with that Department selected in the tree view. This is a convenient informational and navigational shortcut available in SOLoist. The widgets for the contract and photo (10) have buttons for browsing and selecting a file for upload, uploading the file, and removing the file.

Employee

The second tab displays the same iconic representation of the Employee (11) and the same hierarchy of all Departments (12) in the company as shown in the screenshot below, but with checkboxes next to the icons of Departments. The checked box indicates the Department to which the Employee is currently assigned. Checking another Department moves (assigns) that Employee to that Department. Again, double-clicking any Department displays the first panel with that Department selected in the tree view.

Top-level Structure

As in many other GUI frameworks, SOLoist GUI is built from components organized into containment hierarchies. GUI components are objects of SOLoist built-in classes. (Please note that we sometimes use the same term component to refer to a built-in class, not to its instance. However, this imprecision will not make confusion, because it will be clear from the context whether component refers to a class or an object.)

For example, the top-level structure of the presented GUI can be regarded like this:

Completed Appliaction Layout

Explanations of the components used in this example:

  • GUIApplicationComponent: the obligatory, root component object of the composition hierarchy must be of this type. There can be many components of this type in one system, each representing one application; one URL maps to one application component object. Its only child is normally a panel.
  • GUIPanelComponent: a generic panel container of other components (including panels) that can render its subcomponents according to its layout definition, which can be, e.g., horizontal or vertical.
  • GUIDeckComponent: a container of other components that overlay each other. A deck component shows one and only one of its children at any time: when one child shows up, it overlays and hides the others, etc.

Now, let's develop the described initial structure of the GUI by writing the initialization code.

1. Open up ApplicationInit.java in your IDE and type in or copy the code which follows.

public class ApplicationInit implements Initializer {

private static final String COPYRIGHT_TEXT = "Copyright &copy; 2011";
private static final String TITLE = "SOLoist Demo - Company Organization";

private GUIApplicationComponent application;
private GUIContext context;
private GUIPanelComponent mainPanel;

private GUIPanelComponent titlePanel;
private GUIDeckComponent centralPanel;

public void init() throws InitializerFailedException {

application = new GUIApplicationComponent();
SoloistServiceServlet.registerApplication(application);
application.setName("CompanyOrganization");
application.setTitle("Company Organization");

context = new GUIContext();
DefaultContextInit.getRoot().addContext(context);
application.setContext(context);

mainPanel = GUIPanelComponent.createVertical(application);
mainPanel.setStyle("main_panel");

// Title
titlePanel = GUIPanelComponent.createHorizontal(mainPanel);
titlePanel.setStyle("titlePanel");
titlePanel.setCellAlignment(null, VerticalAlignment.TOP);
titlePanel.setCellSize("100%", "80");

// Logo
GUIImageComponent leftLogoComponent = GUIImageComponent.create(titlePanel, "res/img/logo-left.png");
leftLogoComponent.setStyle("logo-image");
leftLogoComponent.setCellSize("200", null);

GUILabelComponent pageTitle = GUILabelComponent.create(titlePanel, TITLE, null);
pageTitle.setStyle("page_title");

GUILinkComponent poweredBy = GUILinkComponent.create(titlePanel, "Powered by SOLoist", "http://www.soloist4uml.com/");
poweredBy.setStyle("poweredBy");

poweredBy.setCellAlignment(null, VerticalAlignment.BOTTOM);

// Central panel
centralPanel = GUIDeckComponent.create(GUIPanelComponent.createFlow(mainPanel));
centralPanel.setStyle("centralPanel");

// Bottom panel
GUIPanelComponent bottomPanel = GUIPanelComponent.createVertical(mainPanel);
bottomPanel.setStyle("bottomPanel");

GUIPanelComponent statusBarPanel = GUIPanelComponent.createHorizontal(bottomPanel);
statusBarPanel.setStyle("status_bar_panel");
statusBarPanel.setCellSize("100%", null);

GUILabelComponent statusBarLabel = GUILabelComponent.create(statusBarPanel, "Status bar: ", null);
statusBarLabel.setCellSize("75px", null);
statusBarLabel.setStyle("status_bar_label");

GUIStatusBarComponent statusBar = GUIStatusBarComponent.create(statusBarPanel);
statusBar.setStyle("status_bar");

GUIHTMLComponent html = GUIHTMLComponent.create(statusBarPanel, COPYRIGHT_TEXT);
html.setStyle("footer_panel");
html.setCellAlignment(HorizontalAlignment.LEFT, null);
html.setSize("400", null);
}
}

If you want to skip this step:

To skip this step, download the complete ApplicationInit.java.

2. Start up the server and visit you application in the browser. You should see something like this.

Initial Application Layout

In these lines, we occasionally set the style name (e.g. html.setStyle("footer_panel")) of GUI components (any SOLoist GUI component can have a style name) that refers to a CSS class that fine-tunes the appearance of that component in the browser. Of course, if the styleName is omitted, the component will use the default style. It is possible to set multiple classes separating them with single space character in the "style" String. If String argument begins with the # sign, HTML id attribute will be set. You can set multiple class names and id at the same time, for example setStyle("topPanel greenBorder #news") would set "news" as id, and "topPanel" and "greenBorder" as CSS classes. Multiple values for id attribute are not allowed and will be ignored (last occurence will be used).

By using CSS styles, we can take advantage of all the features of CSS to build graphically attractive UIs without spoiling our basic component structure with it (basically separating the layout from its look and feel). In addition, this makes GUI design more flexible, because changing the CSS does not require reinitialization of the application GUI structure (just reloading it in the browser). For this example, we will use the this CSS file.

We have now created the skeleton of our future application. Let's continue to refine it.

Departments Panel

Let’s now get to the Departments panel that appears within the central panel (deck) and whose internal structure looks like this:

Department Panel

Apart from a few panel components that enforce the given layout and the static labels, the essential components that provide the functionality are the following:

  • The Department tree view on the left. This is a component of a very generic, flexible kind called GUIInput in SOLoist. Generally, it can provide the contents (i.e., a collection of objects) obtained in various ways and render the contents in different ways (e.g., as a list or a tree). In this particular case, its content is obtained via the collection input pin (ipCollection()) which provides root Department object. Its display kind is set to tree view by calling GUIInput.createTree() factory method. In addition, it has an input pin value (ipValue()) for selecting the object provided on it. It also have an output pin named value (opValue()) that gives (as output value) the element currently selected in the tree. The component will render the tree starting from the specified root Department object, according to the subnodes GUI item setting for Departments (to be explained later).
  • The name and responsibility slot editors on the right. These are components of type GUIEdit in SOLoist. You just have to specify the property such a component is bound to (the attributes name and responsibility of the class Department in this case), and the component will display the widget corresponding to the type of the property (text boxes in this case). In addition, its input pin named element (ipElement()) will be bound to the pin value of the tree view on the left, so when a Department object is selected in the tree view, it will be provided to the element input pin of the slot editors, which in turn will edit the slots.
  • The list of member Employees of the selected Department on the right. This is again the same generic GUIInput component, but slightly differently configured now. It is configured to render the contents as a list of objects (GUIInput.createList()). Its contents are now provided from a slot (Department::members in this case) of the object provided to its slot value element input pin (ipSlotValueElement()). It will read the slot Department::members of the object provided to its slot value element input pin. As with the text boxes above, its input pin will be bound to the same value output pin of the tree view, so that when a Department object is selected in the tree view, it will be sent to the input pin of the members component, which in turn will provide the collection of members of the selected Department.

In addition, we will have to create the bindings between the components in the panel for the described data flow, as indicated in the figure. We use GUIComponentBinding.create() factory methods for that. The goal is to connect the output pin of source component with the input pin of destination component. Output pins should be obtained by calling one of the source component's methods that start with the prefix op, for example opValue(), opClick(), opEnterPressed(), etc. The destination component's input pin should be obtained by calling one of its methods that start with the prefix ip, for example ipElement(), ipLabel(), ipHTML(), etc. Generally, output pins reflect events in the GUI, while inputs pins reflect actions.

As a kind of a summary, the discussed GUI structure can be obtained with the following Java code fragment. The only thing you need to do is to call the two methods from within init method in ApplicationInit.java.

public void initDepartmentPanel() {

departmentPanel = GUIPanelComponent.createVertical(GUIPanelComponent.createFlow(centralPanel));
departmentPanel.setStyle("tablePanels");

GUILabelComponent labelEmployeeTitle = GUILabelComponent.create(departmentPanel, "Departments details", null);
labelEmployeeTitle.setStyle("titleLabel");

GUIPanelComponent departmentDetailsPanel = GUIPanelComponent.createHorizontal(departmentPanel);
departmentDetailsPanel.setStyle("tablePanels");

// Department panel
GUIPanelComponent treePanel = GUIPanelComponent.createVertical(departmentDetailsPanel);
treePanel.setStyle("tablePanels");

GUILabelComponent.create(treePanel, "Select Department: ", null);

departmentTreeView = GUIInput.createTree(treePanel);
departmentTreeView.setStyle("departments");
GUIComponentBinding.create(rootDeptSAP.opValue(), departmentTreeView.ipCollection());

GUIPanelComponent detailsPanel = GUIPanelComponent.createTable(departmentDetailsPanel);
detailsPanel.setStyle("tablePanels");

GUILabelComponent.create(detailsPanel, "Name: ", 0, 0);

GUIEdit nameSlot = GUIEdit.createField(detailsPanel, Department.PROPERTIES.name, 0, 1);
nameSlot.setSize("300px", "25px");

GUILabelComponent lab3 = GUILabelComponent.create(detailsPanel, "Responsibility: ", 1, 0);
lab3.setCellAlignment(null, VerticalAlignment.TOP);
GUIEdit respSlotComponent = GUIEdit.createField(detailsPanel, Department.PROPERTIES.responsibility, 1, 1);
respSlotComponent.setMultiline(true);
respSlotComponent.setSize("300px", "100px");

GUILabelComponent.create(detailsPanel, "Members: ", 3, 0);

membersList = GUIInput.createList(detailsPanel, Department.PROPERTIES.members);
membersList.setLayoutData(TableLayoutData.create(4, 0, 1, 2));
membersList.setStyle("members");
// end Department panel

// Bindings for departments
GUIComponentBinding.create(departmentTreeView.opValue(), nameSlot.ipElement());
GUIComponentBinding.create(departmentTreeView.opValue(), respSlotComponent.ipElement());
GUIComponentBinding.create(departmentTreeView.opValue(), membersList.ipSlotValueElement());

} public void initStyle() {

// Object setting for department
GUIObjectSetting departmentOS = GUIObjectSetting.create(application.getContext(), Department.CLASSIFIER);
GUIPictureFeature.createSmallIcon(departmentOS, "res/img/CompanyOrganizationIcons/3.01_Department1.png");
GUITextFeature.createName(departmentOS, "name");
GUINavigatorFeature.createSubnodes(departmentOS, "subDepts");

// Object setting for employee
GUIObjectSetting employeeOS = GUIObjectSetting.create(application.getContext(), Employee.CLASSIFIER);
GUIPictureFeature.createSmallIcon(employeeOS, "res/img/CompanyOrganizationIcons/3.03_MaleEmployee2.png");
GUITextFeature.createName(employeeOS, "name");

// Tooltip for department
GUITextFeature tooltipDepr = new GUITextFeature();
tooltipDepr.setFixed(true);
tooltipDepr.setTextValue("Double click to see details");
departmentOS.addFeature(tooltipDepr);

}

The initStyle method will be explained in detail in the last section of this tutorial. For now don't bother with it.

If you want to skip this step:

To skip this step, download the completed AppliacitonInit.java.

After editing ApplicationInit.java your application should look like this:

Department Panel

 

Note:

If you run the application configured without initStyle() function, you will not get the final desired appearance because:

  • the tree view will not render a tree of Departments
  • the objects will not be displayed with their proper icons, but with the default object icons.
In SOLoist, these and similar aspects are specified through GUI style configuration that we’ll do later.

Now, let's add commands to departments panel.

Commands

Let’s now configure the command buttons on the Departments page:

  • Command "Create Employee": This one should create a new employee and immediately link it as a member of the Department selected in the tree view.
  • Command "Create Subdepartment": This one should create a new Department and immediately link it as a subdepartment of the Department selected in the tree view.

To support flexible configuration of parameterized commands in the GUI, SOLoist takes the following approach, illustrated through the example of the first mentioned command button, as shown in the figure below:

Commands

A command button is a GUI component of kind GUIButtonComponent, configured in the GUI as any other component.

A button component has an associated command prototype as an object of a class derived from the SOLoist built-in class Command. In this case, this will be a prototype object of the built-in (generic) command class CmdCreateObjectAndLinkToObject, which, when executed, will create a new object of the specified class and then link it to the specified slot of the object provided on its input pin.

The command button's input pins (named firstParam and secondParam) can be bound to an output pin of another component. In this case, the output pin value of the tree view will be bound to the input pin firstParam of the button.

When pressed, the button will clone the prototype command object and provide the value available on its firtParam input pin to the adequate input pin of the command. In this case, this will be the input pin of the command that determines the object to a slot of which the newly created object will be linked. The command is then executed and its optional output value, placed on its output pin, is offered to the output pin of the button (not used in this case).

As a kind of a summary, the configuration needed for this example can be specified in the following Java method. This method should be called from initDepartmentPanel() method.

public void initDepartmentButtons() {

GUIButtonComponent btnCreateEmpl = GUIButtonComponent.create(departmentButtonPanel, "Create Employee");
btnCreateEmpl.setStyle("button");

CmdCreateObjectAndLinkToObject createEmplCommand = new CmdCreateObjectAndLinkToObject();
createEmplCommand.setClassName(Employee.CLASSIFIER);
createEmplCommand.setAssocEndName(Department.PROPERTIES.members);
createEmplCommand.setType(Department.CLASSIFIER);
btnCreateEmpl.setCommand(createEmplCommand);

GUIComponentBinding.create(departmentTreeView.opValue(), btnCreateEmpl, CmdCreateObjectAndLinkToObject.PROPERTIES.target);

GUIButtonComponent btnCreateSubDepr = GUIButtonComponent.create(departmentButtonPanel, "Create Subdepartment");
btnCreateSubDepr.setStyle("button");

CmdCreateObjectAndLinkToObject createSubDeprCommand = new CmdCreateObjectAndLinkToObject();
createSubDeprCommand.setClassName(Department.CLASSIFIER);
createSubDeprCommand.setAssocEndName(Department.PROPERTIES.subDepts);
createSubDeprCommand.setType(Department.CLASSIFIER);
btnCreateSubDepr.setCommand(createSubDeprCommand);

GUIComponentBinding.create(departmentTreeView.opValue(), btnCreateSubDepr,
CmdCreateObjectAndLinkToObject.PROPERTIES.target);

}

If you want to skip this step:

To skip this step, download the completed ApplicationInit.java.

After this changes you can run the application and experiment with new buttons.

Now let's move on to creating the employee panel.

Employee Panel

Now we will create the employee panel which shows all the details about an employee. This panel appears when user double-clicks on an employee. Double-click configuration is a part of the GUI style configuration and it will be explained in the next step. For now, you should just copy initStyle() method and call it from init() method.

Employee panel will have two tabs and for that purpose GUITabComponent will be used as a container. We suggest you to try and create these two tabs on your own (shown on the pictures below).

First Tab

Second Tab

Below is a complete code for obtaining those two panels.

 
public void initEmployeePanelTab() {

employeePanel = GUIPanelComponent.createVertical(centralPanel);
employeePanel.setStyle("tablePanels");

GUIButtonComponent backLink = GUIButtonComponent.createLink(employeePanel, "Home page");
GUIComponentBinding.create(backLink.opClick(), departmentPanel.ipShow());
backLink.setStyle("button-link");

GUIPanelComponent employeeSinglePanel = GUIPanelComponent.createHorizontal(employeePanel);
employeeSinglePanel.setStyle("tablePanels");

GUILabelComponent labelEmployeeTitle = GUILabelComponent.create(employeeSinglePanel, "Employee details ");
labelEmployeeTitle.setStyle("titleLabel");

GUISingleElementComponent singleEmployee = GUISingleElementComponent.create(employeeSinglePanel);

// Employees tab
employeePanelTab = GUITabComponent.create(employeePanel, "General", "Department");

// General tab
GUIPanelComponent wrapPanelGeneral = GUIPanelComponent.createHorizontal(employeePanelTab);

GUIPanelComponent panelGeneral = GUIPanelComponent.createTable(wrapPanelGeneral);
panelGeneral.setStyle("tablePanels");

GUILabelComponent.create(panelGeneral, "Name: ", 0, 0);
GUIEdit employeeNameSlotComponent = GUIEdit.createField(panelGeneral, Employee.PROPERTIES.name, 0, 1);

GUILabelComponent.create(panelGeneral, "Date of birth: ", 1, 0);
GUIEdit dateSlotComponent = GUIEdit.createField(panelGeneral, Employee.PROPERTIES.dateOfBirth, 1, 1);
dateSlotComponent.setSize("167px", null);

GUILabelComponent.create(panelGeneral, "Is married: ", 2, 0);
GUIEdit isMarriedSlotComponent = GUIEdit.createField(panelGeneral, Employee.PROPERTIES.isMarried, 2, 1);

GUILabelComponent.create(panelGeneral, "Gender: ", 3, 0);
GUIEdit genderSlotComponent = GUIEdit.createField(panelGeneral, Employee.PROPERTIES.gender, 3, 1);

GUILabelComponent.create(panelGeneral, "Number of children: ", 4, 0);
GUIEdit numOfChSlotComponent = GUIEdit.createField(panelGeneral, Employee.PROPERTIES.numberOfChildren, 4, 1);

GUILabelComponent.create(panelGeneral, "Department: ", 5, 0);
GUIInput departmentGeneralList = GUIInput.createList(panelGeneral, Employee.PROPERTIES.dept);
departmentGeneralList.setLayoutData(TableLayoutData.create(5, 1));

GUILabelComponent.create(panelGeneral, "Contract: ", 6, 0);
GUIPanelComponent panelFile = GUIFactory.createFileComponent(Employee.PROPERTIES.contract, true, panelGeneral,
TableLayoutData.create(6, 1));

GUIPanelComponent wrapPicturePanel = GUIPanelComponent.createVertical(wrapPanelGeneral);
wrapPicturePanel.setStyle("photo");
GUILabelComponent.create(wrapPicturePanel, "Photo: ", TableLayoutData.create(0, 2));
GUIPanelComponent panelPicture = GUIFactory.createFileComponent(Employee.PROPERTIES.photo, true, wrapPicturePanel, null);

// Department tab
GUIPanelComponent panelDepartment = GUIPanelComponent.createVertical(employeePanelTab);
panelDepartment.setStyle("tablePanels");

GUILabelComponent.create(panelDepartment, "Membership: ");
GUIEdit membershipSlotComponent = GUIEdit.createTree(panelDepartment, Employee.PROPERTIES.dept);
membershipSlotComponent.setStyle("membership");
GUIComponentBinding.create(rootDeptSAP.opValue(), membershipSlotComponent.ipCollection());

// Bindings for employees
GUIComponentBinding.create(employeePanelTab.opRelay1(), singleEmployee.ipElement());

// General tab
GUIComponentBinding.create(employeePanelTab.opRelay1(), employeeNameSlotComponent.ipElement());
GUIComponentBinding.create(employeePanelTab.opRelay1(), dateSlotComponent.ipElement());
GUIComponentBinding.create(employeePanelTab.opRelay1(), isMarriedSlotComponent.ipElement());
GUIComponentBinding.create(employeePanelTab.opRelay1(), genderSlotComponent.ipElement());
GUIComponentBinding.create(employeePanelTab.opRelay1(), numOfChSlotComponent.ipElement());
GUIComponentBinding.create(employeePanelTab.opRelay1(), panelFile.ipRelay1());
GUIComponentBinding.create(employeePanelTab.opRelay1(), departmentGeneralList.ipSlotValueElement());
GUIComponentBinding.create(employeePanelTab.opRelay1(), panelPicture.ipRelay1());

// Department tab
GUIComponentBinding.create(employeePanelTab.opRelay1(), membershipSlotComponent.ipElement());
} public void initStyle() {

// Object setting for department
GUIObjectSetting departmentOS = GUIObjectSetting.create(application.getContext(), Department.CLASSIFIER);
GUIPictureFeature.createSmallIcon(departmentOS, "res/img/CompanyOrganizationIcons/3.01_Department1.png");
GUITextFeature.createName(departmentOS, "name");
GUINavigatorFeature.createSubnodes(departmentOS, "subDepts");

// Object setting for employee
GUIObjectSetting employeeOS = GUIObjectSetting.create(application.getContext(), Employee.CLASSIFIER);
GUIPictureFeature.createSmallIcon(employeeOS, "res/img/CompanyOrganizationIcons/3.03_MaleEmployee2.png");
GUITextFeature.createName(employeeOS, "name");

// Tooltip for department
GUITextFeature tooltipDepr = new GUITextFeature();
tooltipDepr.setFixed(true);
tooltipDepr.setTextValue("Double click to see details");
departmentOS.addFeature(tooltipDepr);

// Tooltip for employee
GUITextFeature tooltipEmpl = new GUITextFeature();
tooltipEmpl.setFixed(true);
tooltipEmpl.setTextValue("Double click to see details");
employeeOS.addFeature(tooltipEmpl);

// Bindings feature for department (double click)
GUIBindingsFeature bfDepartment = new GUIBindingsFeature();
GUIComponentBinding.create(bfDepartment.opDoubleClick(), departmentPanel.ipRelay1());
GUIComponentBinding.create(bfDepartment.opDoubleClick(), departmentPanel.ipShow());
departmentOS.addFeature(bfDepartment);

// Bindings feature for employee (double click)
GUIBindingsFeature bfEmployee = new GUIBindingsFeature();
GUIComponentBinding.create(bfEmployee.opDoubleClick(), employeePanelTab.ipShow());
GUIComponentBinding.create(bfEmployee.opDoubleClick(), employeePanelTab.ipRelay1());
employeeOS.addFeature(bfEmployee);

}

If you want to skip this step:

To skip this step, download the completed ApplicationInit.java.

With this step you have almost completed CompanyOrganization application. There is just one thing left to explain - GUI style configuration. We will do that in the next step.

UI Style Configuration

In this step, we will complete the construction of our GUI with the following:

  • setting the icon and caption that appear next to the icon for objects of class Department,
  • setting the icon and caption that appear next to the icon for objects of class Employee,
  • specifying that a Department will have its subdepartments as its subnodes when rendered in tree views,
  • specifying that a double-click on an Employee shows up the Employee details panel, with the clicked Employee provided on its input pin,
  • specifying that a double-click on a Department shows up the Department details panel, with the clicked Department provided on its input pin.

In SOLoist, these and similar specifications are configured through the GUI style configuration. For each type of GUI items, such as objects of a certain class, the GUI item setting for that kind of item can specify how the icon, the caption, the tooltip, the subnodes, and other GUI style features are obtained. SOLoist provides a reach set of features that can be specified through GUI item settings. For example, you can specify that the icon for one particular object, or for all objects of some class are fixed, or are obtained from a particular pictorial attribute of the object. The similar holds for the caption rendered next to the icon – it can be a fixed text, or a text obtained dynamically from a certain attribute of the object, etc. All these are added as GUI item features for the particular GUI item setting. The following Java code fragment illustrates how this can be done through code:

public void initStyle() {

// Object setting for department
GUIObjectSetting departmentOS = GUIObjectSetting.create(application.getContext(), Department.CLASSIFIER);
GUIPictureFeature.createSmallIcon(departmentOS, "res/img/CompanyOrganizationIcons/3.01_Department1.png");
GUITextFeature.createName(departmentOS, "name");
GUINavigatorFeature.createSubnodes(departmentOS, "subDepts");

// Object setting for employee
GUIObjectSetting employeeOS = GUIObjectSetting.create(application.getContext(), Employee.CLASSIFIER);
GUIPictureFeature.createSmallIcon(employeeOS, "res/img/CompanyOrganizationIcons/3.03_MaleEmployee2.png");
GUITextFeature.createName(employeeOS, "name");

// Tooltip for department
GUITextFeature tooltipDepr = new GUITextFeature();
tooltipDepr.setFixed(true);
tooltipDepr.setTextValue("Double click to see details");
departmentOS.addFeature(tooltipDepr);

// Tooltip for employee
GUITextFeature tooltipEmpl = new GUITextFeature();
tooltipEmpl.setFixed(true);
tooltipEmpl.setTextValue("Double click to see details");
employeeOS.addFeature(tooltipEmpl);

// Bindings feature for department (double click)
GUIBindingsFeature bfDepartment = new GUIBindingsFeature();
GUIComponentBinding.create(bfDepartment.opDoubleClick(), departmentPanel.ipRelay1());
GUIComponentBinding.create(bfDepartment.opDoubleClick(), departmentPanel.ipShow());
departmentOS.addFeature(bfDepartment);

// Bindings feature for employee (double click)
GUIBindingsFeature bfEmployee = new GUIBindingsFeature();
GUIComponentBinding.create(bfEmployee.opDoubleClick(), employeePanelTab.ipShow());
GUIComponentBinding.create(bfEmployee.opDoubleClick(), employeePanelTab.ipRelay1());
employeeOS.addFeature(bfEmployee);

}

Using the SOLoist Explorer

SOLoist Explorer is a simple tool that provides an HTTP interface for:

  • writing and executing OQL queries (results in HTML or Excel sheet; see generated SQL, etc.)
  • browsing the UML model
  • browsing the object space.

SOLoist Explorer is provided as a servlet in the SOLoist library and can be easily included by adding the servlet to web.xml of your SOLoist application.

<servlet>
        <servlet-name>OQLServlet</servlet-name>
        <servlet-class>rs.sol.soloist.server.server.OQLServlet</servlet-class>
</servlet>

<servlet-mapping>	
        <servlet-name>OQLServlet</servlet-name>
        <url-pattern>/oql</url-pattern>
</servlet-mapping>

The servlet declaration registers the OQLServlet, while the servlet-mapping declaration establishes a url which maps to the servlet.

After editing the web.xml and restarting the server, SOLoist OQL Browser will be accessible at the given URL, e.g. http://localhost:8080/CompanyOrganization/oql.

When you access the SOLoist Explorer you should click on the HELP link on the top of the page and you will get all available commands that you can use in this OQL browser.

More details about advanced installation and troubleshooting can be found at SOLoist wiki page.