Documentation : Java Advanced Tutorial

Main goal of this tutorial is to show how to organize web application database to easily synchronize with Mobeelizer cloud. We will show how to prepare set of data to synchronize and how to handle differential data returned from sync process.

This example also shows how to authenticate users of your application using Mobeelizer user database and how you can exploit mobeelizer roles to control users permissions. Last, but not least feature will be listing and creating Mobeelizer users in web application. 

In this document we assume that you are familiar yourself with:

  • JAVA language
  • Basic of Spring roo (see)
  • Key concepts of Mobeelizer platform (see)

Before we start creating new application I want to give you some extra information about application specification. The application will focus on creating orders by consumers and handling them by sellers. We will create two models in our system: order and order item. Our models will be in relation - each order will contain list of its items. There will be three groups of users in the application: consumers, sellers and admins.

  • consumers - have permissions to create, update, delete and read orders and their items. 
  • sellers - can read and update orders and their items. 
  • admins - can read and update orders and their items and also have permission to operate on users (read and create). 

Design Your app

Start working with the Mobeelizer. Create your FREE account.

Open the Mobeelizer App Designer and login with your account. Then, press CREATE NEW APPLICATION button and insert it's name Orders.

In your applications designer panel enter Models section on the left. Then create new model called OrderEntity.

Now we have to create new fields for Order model.

You have to also create another model called OrderItemEntity.

And its fields like follows:

Pay particular attention to the field orderEntity, it defines relation between our models. 

Before we configure our models credentials we have to create users groups. Open Groups & Roles table and create following groups:

There is one group more than described before. Our web application is standard Mobeelizer client, it means that we have to create separated user for it. This user have to have super admin credentials because he will synchronize whole web application database. There is separated group - superadmin - for him. 

We also have to create new device category called web

As you can see group and device category together creates role. And you have 2 x 4 = 8 roles now. Because in this tutorial we will create only web application back end you can disable all mobile roles or just remove mobile device category. 

Great! Groups & Roles is configured correctly now, so we can go back to Models and specify our models credentials.

For OrderEntity and OrderItemEntity there will be exactly the same credentials:

Our application is ready, let's deploy it to the test environment and configure it.

When you are on the test environment section, open Users tab, create super admin for our web application and a few users for test - one for each group.

Super admin credentials will be:

  • login: webApplication
  • password: webApplicationPassword
  • group: superadmin
  • isAdmin: yes

The application is modeled now and we can skip to creating spring roo project. 

Create Spring MVC application

In our application we will use HYPERSONIC IN MEMORY databse and HIBERNATE as a provider. There is lot of commands which we have to type in roo shell so we created script for you which will creates skeleton of our application.

Create new directory called Orders in your workspace folder, download script file and place it in just created folder.

Open roo shell in created directory and type:

 script script.roo

You can import created project into eclipse now. As you can see there are a few packages created:

  • com.mobeelizer.tutorial.orders.controllers - contains controllers of our entities.
  • com.mobeelizer.tutorial.orders.models - contains models classes.
  • com.mobeelizer.tutorial.orders.provider - contains class that will be responsible for authentication with mobeelizer.
  • com.mobeelizer.tutorial.orders.repository - jpa repositories of our models.  
  • com.mobeelizer.tutorial.orders.services - contains services that we will implement to perform operation like create, update, list etc.
  • com.mobeelizer.tutorial.orders.services.impl - implementation of our services. 

You can see that there is also generated view for us in jspx language. 

Keep roo shell running until we finish implementing all of our controllers. Roo is performing some changes in .aj files in the background.

Setup mobeelizer

Ok, our project is ready, it is time for mobeelizer now. Firstly we have to tell maven to download Java SDK and add it into the project. Open pom.xml file in xml editor and add new repository and then new dependency.

pom.xml
    <repositories>
	    <repository>        
	        <id>qcadoo-releases-repository</id>        
	        <url>http://nexus.qcadoo.org/content/repositories/releases</url>
	    </repository>        
    </repositories>
pom.xml
    <dependencies>
        <dependency>        
	        <groupId>com.mobeelizer</groupId>        
	        <artifactId>java-sdk</artifactId>        
	        <version>1.5.0</version>    
	    </dependency>
    </dependencies>

Next think to do is adding application definition file into project resources. To do this download definition from App Designer (Get XML button in Create section), rename it to 'application.xml' and put into src/main/resources folder. 

Integrate spring security with mobeelizer

In our example we want to authenticate users using mobeelizer. To implement it we have already generated class called MobeelizerAuthenticationProvider. We have to now extend AbstractUserDetailsAuthenticationProvider class in it and then specify it as authentication provider in spring security configuration xml. 

MobeelizerAuthenticationProvider will use MobeelizerService to authenticate users, so first step is to create no method in it:

MobeelizerService.java
public interface MobeelizerService {
	String authenticate(String username, String password);
}

When it is done we can implement our provider as follows:

MobeelizerAuthenticationProvider.java
public class MobeelizerAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider  {

	@Autowired
	private MobeelizerService mobeelizer;
	
	@Override
	protected void additionalAuthenticationChecks(UserDetails userDetails,
			UsernamePasswordAuthenticationToken authentication)
			throws AuthenticationException {
	}
	@Override
	protected UserDetails retrieveUser(String username,
			UsernamePasswordAuthenticationToken authentication)
			throws AuthenticationException {
		
		String password = (String) authentication.getCredentials();
        List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
		try{
			String role = mobeelizer.authenticate(username, password);
			authorities.add(new SimpleGrantedAuthority(role));
		}
		catch(IllegalStateException e){
			throw new BadCredentialsException(e.getMessage());
		}
		
		return new User(username,
                password, true, true, 
                true, 
                true, authorities);
	}
}

Before we will setup our provider in xml configuration file, we will implement MobeelizerService. Open MobeelizerServiceImpl class and implement MobeelizerService interface on it, remember to put annotation @Service befor class definition. Our service will use Mobeelizer class from Java SDK to provide operations on mobeelizer cloud. We have to create this class in MobeelizerServiceImpl class constructor. 

MobeelizerServiceImpl.java
@Service
public class MobeelizerServiceImpl implements MobeelizerService , MobeelizerSyncCallback {
	
	private static final String DEVICE = "web";
	
	private static final String DEVICE_ID = "dfgpo23rmdnsfvcx142"; // Unique identificatior of your device 
	
	private static final String USER = "webApplication";
	
	private static final String PASSWORD = "webApplicationPassword";
	
	private Mobeelizer mobeelizer;
	
	public MobeelizerServiceImpl(){
		MobeelizerConfiguration configuration = new MobeelizerConfiguration();
		configuration.setDefinition(getClass().getResourceAsStream("/application.xml"));
		configuration.setDevice(DEVICE);
		configuration.setDeviceIdentifier(DEVICE_ID);
		configuration.setPackageName("com.mobeelizer.tutorial.orders.model");
		configuration.setUser(USER);
		configuration.setPassword(PASSWORD);
		mobeelizer = new Mobeelizer(configuration);
	}
	
	public String authenticate(String login, String password){
		return mobeelizer.authenticate(login, password);
	}
}

As I wrote before web application is normal Mobeelizer client and need to have created user with super admin rights. When we are creating Mobeelizer object we have to put credentials to super user account in it. 

When provider is ready, we have to open applicationContext-security.xml file. (Placed in src/main/resources/META-INF/spring/ folder) In opened file we have to create new object of just implemented class and then replace generated authentication provider with our solution. 

applicationContext-security.xml
<beans:beans ...>
    ...
    <beans:bean class="com.mobeelizer.tutorial.orders.provider.MobeelizerAuthenticationProvider" id="mobeelizerProvider"> 
    </beans:bean>
    <authentication-manager alias="authenticationManager">
        <authentication-provider ref="mobeelizerProvider">
        </authentication-provider>
    </authentication-manager>
</beans:beans>

You can test it now, you have to have posibility login to the system using mobeelizer users credentials. 

You can run this project, typing in your shell console mvn clean install and when it finishes mvn jetty:run, your application should be available on this url: http://localhost:8080/orders .

Configure controllers

Spring roo is doing some changes in the background on .aj files and .jspx file, because of that we will configure controllers first and then we will close roo shell to change our view files. Last step will be creating buisness logic. 

  • OrderEntityController - provides CRUD operations on OrderEntity objects. We will create following interface here: 
    • /orders - list of orfers
    • /orders/{guid} - order details view, this view will also contains list of order items.
    • /orders?form - form to create new order
    • /orders, method POST - creats new order and redirect to order details view.
    • /orders/{guid}?form - form to update order fields.
    • /orders - method PUT - updates order.
    • /orders/{guid} - method DELETE - deltes order.
  • OrderItemController - provides CRUD operations on OrderItemEntity objects. We will create following interface here:
    • /orders/{orderGuid}/orderitems - order details view, this view will also contains list of order items.
    • /orders/{orderGuid}/orderitems/{itemGuid} - shows order item details view.
    • /orders/{orderGuid}/orderitems?form - form to create new order item object.
    • /orders/{orderGuid}/orderitems- method POST - creates new order item.
    • /orders/{orderGuid}/orderitems/{itemGuid}?form - form to update order item. 
    • /orders/{orderGuid}/orderitems- method PUT - updates order item object.
    • /orders/{orderGuid}/orderitems/{itemGuid} - method DELETE - deletes order item.

Interface of our system is clear now, let's create some code. Open OrderEntityController class and implement it lik this:

OrderEntityController.java
@RequestMapping("/orders")
@Controller
@RooWebScaffold(path = "orderentitys", formBackingObject = OrderEntity.class)
public class OrderEntityController {

	@RequestMapping(value = "/{guid}", produces = "text/html")
    public String show(@PathVariable("guid") String guid, Model uiModel) {
        return "redirect:/orders/"+guid+"/orderitems"; // order item controller shows the same information in following url
    }
	
	@RequestMapping(produces = "text/html")
    public String list(Model uiModel, HttpServletRequest request) {
        return "orderentitys/list";
    }
    
    @RequestMapping(params = "form", produces = "text/html")
    public String createForm(Model uiModel) {
        return "orderentitys/create";
    }
	
	@RequestMapping(method = RequestMethod.POST, produces = "text/html")
    public String create(@Valid OrderEntity orderEntity, BindingResult bindingResult, Model uiModel, HttpServletRequest httpServletRequest) {
        return "redirect:/orders/"; // TODO: add order guid into the url;
    }
	
	@RequestMapping(method = RequestMethod.PUT, produces = "text/html")
    public String update(@Valid OrderEntity orderEntity, BindingResult bindingResult, Model uiModel, HttpServletRequest httpServletRequest) {
        return "redirect:/orders/" ;// TODO: add order guid into the url
    }
	
	@RequestMapping(value = "/{guid}", params = "form", produces = "text/html")
    public String updateForm(@PathVariable("guid") String guid, Model uiModel) {
        return "orderentitys/update";
    }
	
	@RequestMapping(value = "/{guid}", method = RequestMethod.DELETE, produces = "text/html")
    public String delete(@PathVariable("guid") String guid, Model uiModel) {
        return "redirect:/orders";
    }
}

When OrderEntityController is ready we have to implement OrderItemEntityController now.

OrderItemEntityController.java
@RequestMapping("orders/{orderGuid}/orderitems")
@Controller
@RooWebScaffold(path = "orderitementitys", formBackingObject = OrderItemEntity.class)
public class OrderItemEntityController {
	
    @RequestMapping(value = "/{itemGuid}", produces = "text/html")
    public String show(@PathVariable("orderGuid") String orderGuid, @PathVariable("itemGuid") String itemGuid, Model uiModel) {
        return "orderitementitys/show";
    }
    
    @RequestMapping(produces = "text/html")
    public String list(@PathVariable("orderGuid") String orderGuid, Model uiModel) {
        return "orderitementitys/list";
    }
    
    @RequestMapping(params = "form", produces = "text/html")
    public String createForm(@PathVariable("orderGuid") String orderGuid, Model uiModel) {
        return "orderitementitys/create";
    }
    
    @RequestMapping(method = RequestMethod.POST, produces = "text/html")
    public String create(@Valid OrderItemEntity orderItemEntity, @PathVariable("orderGuid") String orderGuid, BindingResult bindingResult, Model uiModel, HttpServletRequest httpServletRequest) {
        return "redirect:/orders/" + orderGuid + "/orderitems/" + orderItemEntity.getGuid();
    }
    
    @RequestMapping(value = "/{itemGuid}", params = "form", produces = "text/html")
    public String updateForm(@PathVariable("orderGuid") String orderGuid, @PathVariable("itemGuid") String itemGuid, Model uiModel) {
        return "orderitementitys/update";
    }
    
    @RequestMapping(method = RequestMethod.PUT, produces = "text/html")
    public String update(@PathVariable("orderGuid") String orderGuid, @Valid OrderItemEntity orderItemEntity, BindingResult bindingResult, Model uiModel, HttpServletRequest httpServletRequest) {
        return "redirect:/orders/"+orderGuid + "/orderitems/"+ orderItemEntity.getGuid(); 
    }
    
    @RequestMapping(value = "/{itemGuid}", method = RequestMethod.DELETE, produces = "text/html")
    public String delete(@PathVariable("orderGuid") String orderGuid, @PathVariable("itemGuid") String itemGuid,  Model uiModel) {
        return "redirect:/orders/"+orderGuid+"/orderitems";
    }
}

We can see in roo shall that roo updated some .aj files, and for now that is all what we need from roo. 

Close roo shell now.

It is really important to close roo shell now, because roo will revert our changes in jspx files in the background.

Prepare view

Next step is to make some changes in generated jspx files. In our models there are some extra fields to store metadata like modified, deleted etc. We don't want to show it on our web page. We also made some changes in controllers interfaces and we have to apply them in view files.

Lets start with orders - open src/main/webapp/WEB-INF/views/orderentitys directory. There are our view definitions. We will start with list file. Because our web application will synchronize with mobeelizer with super admin rights we need to defines permissions to models by our self and we will use spring security for that. In this view we will create two variables which will define if current user have permissions to delete or crate entities. And then we will apply this info into table object. We also have to delete redundant fields, change path atribute in table object and set typeIdFieldName to guid. Everything what I just described should look like this:

orderentitys/list.jspx
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
<div xmlns:c="http://java.sun.com/jsp/jstl/core" xmlns:jsp="http://java.sun.com/JSP/Page" xmlns:page="urn:jsptagdir:/WEB-INF/tags/form" xmlns:sec="http://www.springframework.org/security/tags" xmlns:table="urn:jsptagdir:/WEB-INF/tags/form/fields" version="2.0">
    <jsp:directive.page contentType="text/html;charset=UTF-8"/>
    <sec:authorize access="hasRole('consumers-web')">
        <c:set value="true" var="delete"/>
        <c:set value="true" var="create"/>
    </sec:authorize>
    <sec:authorize access="hasAnyRole('admins-web','sellers-web')">
        <c:set value="false" var="delete"/>
        <c:set value="false" var="create"/>
    </sec:authorize>
    <jsp:output omit-xml-declaration="yes"/>
    <page:list id="pl_com_mobeelizer_tutorial_orders_model_OrderEntity" items="${orderentitys}" z="yfYfNFEzUBPprwYUDyPBqt36ECA=">
        <table:table create="${create}" data="${orderentitys}" delete="${delete}" id="l_com_mobeelizer_tutorial_orders_model_OrderEntity" path="/orders" typeIdFieldName="guid" z="user-managed">
            <table:column id="c_com_mobeelizer_tutorial_orders_model_OrderEntity_name" property="name" z="wHKsLHIW5GYmmj4Afj+B+zXPbOM="/>
            <table:column id="c_com_mobeelizer_tutorial_orders_model_OrderEntity_description" property="description" z="yQRItYaRhK2hbzJC1RKKrAZzB30="/>
            <table:column id="c_com_mobeelizer_tutorial_orders_model_OrderEntity_creationDate" property="creationDate" z="BvsZem/pNUIp2z6Yqx54Lpj4hfA="/>
        </table:table>
    </page:list>
</div>

Next file to edit is create.jspx. In this file we will define that fields like name, deliveryType and status are required, and we will delete metadata fields. We have to also change path attribute in form object. In real world it look like this:

orderentitys/create.jspx
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<div xmlns:c="http://java.sun.com/jsp/jstl/core" xmlns:field="urn:jsptagdir:/WEB-INF/tags/form/fields" xmlns:form="urn:jsptagdir:/WEB-INF/tags/form" xmlns:jsp="http://java.sun.com/JSP/Page" xmlns:spring="http://www.springframework.org/tags" version="2.0">
    <jsp:directive.page contentType="text/html;charset=UTF-8"/>
    <jsp:output omit-xml-declaration="yes"/>
    <form:create id="fc_com_mobeelizer_tutorial_orders_model_OrderEntity" modelAttribute="orderEntity" path="/orders" render="${empty dependencies}" z="user-managed">
        <field:input required="true" field="name" id="c_com_mobeelizer_tutorial_orders_model_OrderEntity_name" z="6OsGGuzagq/tqvMMEmN7ihnCkOU="/>
        <field:input field="description" id="c_com_mobeelizer_tutorial_orders_model_OrderEntity_description" z="q9uXezLRoYhHE+TWkWIjC3Wslg8="/>
        <field:input required="true" field="deliveryType" id="c_com_mobeelizer_tutorial_orders_model_OrderEntity_deliveryType" z="WznfkBMsm/MwFfz0ZauQEaG/MaY="/>
        <field:input required="true" field="status" id="c_com_mobeelizer_tutorial_orders_model_OrderEntity_status" z="oWHOJGjrAm0PRBS7bgFdRWQp6mU="/>
    </form:create>
    <form:dependency dependencies="${dependencies}" id="d_com_mobeelizer_tutorial_orders_model_OrderEntity" render="${not empty dependencies}" z="LKQle0O0LccwfCf222gS8nVkK8Y="/>
</div>

To finish order views we have to modify udate.jspx file. In this file we will again change path atribute and define that guid is our id field. We will also remove metadata fields and mark which fileds are requred. 

orderentitys/update.jspx
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<div xmlns:field="urn:jsptagdir:/WEB-INF/tags/form/fields" xmlns:form="urn:jsptagdir:/WEB-INF/tags/form" xmlns:jsp="http://java.sun.com/JSP/Page" xmlns:sec="http://www.springframework.org/security/tags" version="2.0">
    <jsp:directive.page contentType="text/html;charset=UTF-8"/>
    <jsp:output omit-xml-declaration="yes"/>
    <form:update id="fu_com_mobeelizer_tutorial_orders_model_OrderEntity" idField="guid" modelAttribute="orderEntity" path="/orders" versionField="Version" z="user-managed">
        <field:input required="true" field="name" id="c_com_mobeelizer_tutorial_orders_model_OrderEntity_name"  z="user-managed" />
        <field:input field="description" id="c_com_mobeelizer_tutorial_orders_model_OrderEntity_description" z="q9uXezLRoYhHE+TWkWIjC3Wslg8="/>
        <field:input required="true" field="deliveryType" id="c_com_mobeelizer_tutorial_orders_model_OrderEntity_deliveryType" z="WznfkBMsm/MwFfz0ZauQEaG/MaY="/>
        <field:input required="true" field="status" id="c_com_mobeelizer_tutorial_orders_model_OrderEntity_status" z="oWHOJGjrAm0PRBS7bgFdRWQp6mU="/>
    </form:update>
</div>

We will not change show.jspx file because it won't be displayed. OrderEntity view is ready it is time for OrderItemEntity now. 

You can find required files in views/orderitementitys directory. Actually changes in this files are really similar to order views, The major changes is path in object like table, form, show etc. In most cases it will be /orders/${itemId}/orderitems. Take a look at following files:

orderitementitys/list.jspx
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<div xmlns:c="http://java.sun.com/jsp/jstl/core" xmlns:field="urn:jsptagdir:/WEB-INF/tags/form/fields" xmlns:jsp="http://java.sun.com/JSP/Page" xmlns:page="urn:jsptagdir:/WEB-INF/tags/form" xmlns:sec="http://www.springframework.org/security/tags" xmlns:table="urn:jsptagdir:/WEB-INF/tags/form/fields" version="2.0">
    <jsp:directive.page contentType="text/html;charset=UTF-8"/>
    <sec:authorize access="hasRole('consumers-web')">
        <c:set value="true" var="delete"/>
        <c:set value="true" var="create"/>
    </sec:authorize>
    <sec:authorize access="hasAnyRole('admins-web','sellers-web')">
        <c:set value="false" var="delete"/>
        <c:set value="false" var="create"/>
    </sec:authorize>
    <jsp:output omit-xml-declaration="yes"/>
    <page:show create="${create}" delete="${delete}" id="ps_com_mobeelizer_tutorial_orders_model_OrderEntity" object="${orderentity}" path="/orders" z="HsLD4hmVVpDsTAAd9a2Yde3V/lc=">
    	<field:display field="name" id="s_com_mobeelizer_tutorial_orders_model_OrderEntity_name" object="${orderentity}" z="BREPcAQERJa7QIU4gA7NGAhbgRU="/>
        <field:display field="description" id="s_com_mobeelizer_tutorial_orders_model_OrderEntity_description" object="${orderentity}" z="MrkWVmwpj8cq7EmOSgn2sfU7VFo="/>
        <field:display date="true" dateTimePattern="${orderEntity_creationdate_date_format}" field="creationDate" id="s_com_mobeelizer_tutorial_orders_model_OrderEntity_creationDate" object="${orderentity}" z="EX9LlsB4+zM0GjbMstmgNel6b9M="/>
        <field:display field="deliveryType" id="s_com_mobeelizer_tutorial_orders_model_OrderEntity_deliveryType" object="${orderentity}" z="oIKKpr8BTAy199Gh6g+DkL78PYI="/>
        <field:display field="status" id="s_com_mobeelizer_tutorial_orders_model_OrderEntity_status" object="${orderentity}" z="EoL1AcgaPO+C2AnrSNCDzXCnJwI="/>

    <sec:authorize access="hasRole('consumers-web')">
		<a href="/orders/orders/${itemId}/orderitems?form" alt="Add new item" title="Add new item">
			Add new item
			<img alt="Add new item" class="image" src="/orders/resources/images/add.png" title="Add new item" />
		</a>
    </sec:authorize>
    </page:show >
    <page:list id="pl_com_mobeelizer_tutorial_orders_model_OrderItemEntity" items="${orderitementitys}" z="s+5ciF+2fgVCXaCoR0FFHsLmins=">
        <table:table  typeIdFieldName="guid" create="${create}" data="${orderitementitys}"  delete="${delete}" id="l_com_mobeelizer_tutorial_orders_model_OrderItemEntity" path="/orders/${itemId}/orderitems" z="user-managed">
            <table:column id="c_com_mobeelizer_tutorial_orders_model_OrderItemEntity_name" property="name" z="SShWQDca/JPijHugHH3vzj0vSlQ="/>
            <table:column id="c_com_mobeelizer_tutorial_orders_model_OrderItemEntity_quantity" property="quantity" z="it4EchiY75WJSLlHr+kke0niZjo="/>
            <table:column id="c_com_mobeelizer_tutorial_orders_model_OrderItemEntity_price" property="price" z="KZqNQY+LZpUZ28QLSGKMb/Cb2vU="/>
            <table:column id="c_com_mobeelizer_tutorial_orders_model_OrderItemEntity_unit" property="unit" z="aR9RefqVZVYoXUVQ5o9zTYgPgVU="/>
        </table:table>
    </page:list>
</div>

List view contains also information about parent object - order.

orderitementitys/update.jspx
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<div xmlns:field="urn:jsptagdir:/WEB-INF/tags/form/fields" xmlns:form="urn:jsptagdir:/WEB-INF/tags/form" xmlns:jsp="http://java.sun.com/JSP/Page" version="2.0">
    <jsp:directive.page contentType="text/html;charset=UTF-8"/>
    <jsp:output omit-xml-declaration="yes"/>
    <form:update id="fu_com_mobeelizer_tutorial_orders_model_OrderItemEntity" idField="guid"  modelAttribute="orderItemEntity" path="/orders/${orderguid}/orderitems" versionField="Version" z="12zirYEgHVoIT1aeHSmewYuETG0=">
        <field:input required="true" field="name" id="c_com_mobeelizer_tutorial_orders_model_OrderItemEntity_name" z="iquY1x/zlTAcA9XtlFTdPA7aXZk="/>
        <field:input required="true" field="price" id="c_com_mobeelizer_tutorial_orders_model_OrderItemEntity_price" z="ZXr9d4u0G38DuRHBD0Lz5debzOE="/>
        <field:input required="true" field="quantity" id="c_com_mobeelizer_tutorial_orders_model_OrderItemEntity_quantity" z="xJtB2qlhJFXl0F1L9DMlh1czXWM="/>
        <field:input required="true" field="unit" id="c_com_mobeelizer_tutorial_orders_model_OrderItemEntity_unit" z="2Qko2r2ft7bDvOdG1QsSuUCzlyU="/>
    </form:update>
</div>
orderitementitys/create.jspx
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<div xmlns:c="http://java.sun.com/jsp/jstl/core" xmlns:field="urn:jsptagdir:/WEB-INF/tags/form/fields" xmlns:form="urn:jsptagdir:/WEB-INF/tags/form" xmlns:jsp="http://java.sun.com/JSP/Page" xmlns:spring="http://www.springframework.org/tags" version="2.0">
    <jsp:directive.page contentType="text/html;charset=UTF-8"/>
    <jsp:output omit-xml-declaration="yes"/>
    <form:create id="fc_com_mobeelizer_tutorial_orders_model_OrderItemEntity" modelAttribute="orderItemEntity" path="/orders/${itemId}/orderitems" render="${empty dependencies}" z="user-managed">
        <field:input required="true" field="name" id="c_com_mobeelizer_tutorial_orders_model_OrderItemEntity_name" z="iquY1x/zlTAcA9XtlFTdPA7aXZk="/>
        <field:input required="true" field="price" id="c_com_mobeelizer_tutorial_orders_model_OrderItemEntity_price" z="ZXr9d4u0G38DuRHBD0Lz5debzOE="/>
        <field:input required="true" field="quantity" id="c_com_mobeelizer_tutorial_orders_model_OrderItemEntity_quantity" z="xJtB2qlhJFXl0F1L9DMlh1czXWM="/>
        <field:input required="true" field="unit" id="c_com_mobeelizer_tutorial_orders_model_OrderItemEntity_unit" z="2Qko2r2ft7bDvOdG1QsSuUCzlyU="/>
    </form:create>
    <form:dependency dependencies="${dependencies}" id="d_com_mobeelizer_tutorial_orders_model_OrderItemEntity" render="${not empty dependencies}" z="ZV5xI6uZMgo6/idqp437ug35vpU="/>
</div>

orderitementitys/show.jspx
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<div xmlns:field="urn:jsptagdir:/WEB-INF/tags/form/fields" xmlns:jsp="http://java.sun.com/JSP/Page" xmlns:page="urn:jsptagdir:/WEB-INF/tags/form" version="2.0">
    <jsp:directive.page contentType="text/html;charset=UTF-8"/>
    <jsp:output omit-xml-declaration="yes"/>
    <page:show id="ps_com_mobeelizer_tutorial_orders_model_OrderItemEntity" object="${orderitementity}" path="/orders/${guid}/orderitems" z="6TJDYxXUk+VWZk6pquSd5NW0BLg=" create="false" update="false" delete="false" >
        <field:display field="name" id="s_com_mobeelizer_tutorial_orders_model_OrderItemEntity_name" object="${orderitementity}" z="yvr8hOIeYuatAL1jwy7EtlWv2UU="/>
        <field:display field="price" id="s_com_mobeelizer_tutorial_orders_model_OrderItemEntity_price" object="${orderitementity}" z="y3diXuvQafQkHLNYe7TbkYhzUS8="/>
        <field:display field="quantity" id="s_com_mobeelizer_tutorial_orders_model_OrderItemEntity_quantity" object="${orderitementity}" z="oAqAcqkIUoSDCNbwvClGk6VKr58="/>
        <field:display field="unit" id="s_com_mobeelizer_tutorial_orders_model_OrderItemEntity_unit" object="${orderitementity}" z="iJWMIU+5bsKLsGfpwN/sE4wxjC8="/>
    </page:show>
</div>

Last thing to modify in view is changing menu.jspx file. You can find it in views directory. On the begining of this tutorial we said that consumers can create new orders but sellers can only read them. In our menu it will look like this:

menu.jspx
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<div xmlns:jsp="http://java.sun.com/JSP/Page" xmlns:menu="urn:jsptagdir:/WEB-INF/tags/menu" xmlns:sec="http://www.springframework.org/security/tags" id="menu" version="2.0">
    <jsp:directive.page contentType="text/html;charset=UTF-8"/>
    <jsp:output omit-xml-declaration="yes"/>
    <menu:menu id="_menu" z="nZaf43BjUg1iM0v70HJVEsXDopc=">
        <menu:category id="c_orderentity" z="s7EsjfqoHnELj65Td4JMtijRbbo=">
            <sec:authorize access="hasRole('consumers-web')">
                <menu:item id="i_orderentity_new" messageCode="global_menu_new" url="/orders?form" z="user-managed"/>
            </sec:authorize>
            <menu:item id="i_orderentity_list" messageCode="global_menu_list" url="/orders" z="user-managed"/>
        </menu:category>
    </menu:menu>
</div>

View is ready and you can test it now. Some of our views returns internal error message, because our controllers methods are empty. We will fill them in next step.

Create business logic

I think it is the best time to fill our controllers methods. Open OrderItemController class and skip into createForm method - this method should display form to create new order, in view we defined that it needs OrderEntity object as a parameter. Definition of this method should look like this:

OrderEntityController.java
    @RequestMapping(params = "form", produces = "text/html")
    public String createForm(Model uiModel) {
        populateEditForm(uiModel, new OrderEntity());
        return "orderentitys/create";
    }

We are using here populateEditForm method generated by roo in .aj file.

To handle our form request we created method create in our controller. We will do all the work in OrderService so first we will define following method in it:

OrderService.java
public interface OrderService {

	void save(String owner, OrderEntity orderEntity);
}

Next we will create field with @Autowired annotation in controller class and implement create method like in code below.

OrderEntityController.java
 public class OrderEntityController {

	@Autowired
	private OrderService orderService;
 
    ....
 
    @RequestMapping(method = RequestMethod.POST, produces = "text/html")
    public String create(@Valid OrderEntity orderEntity, BindingResult bindingResult, Model uiModel, HttpServletRequest httpServletRequest) {
        User user = (org.springframework.security.core.userdetails.User) SecurityContextHolder
                .getContext().getAuthentication().getPrincipal();
        if (bindingResult.hasErrors()) {
            populateEditForm(uiModel, orderEntity);
            return "orderentitys/create";
        }
        uiModel.asMap().clear();
        orderService.save(user.getUsername(), orderEntity);
        return "redirect:/orders/" + encodeUrlPathSegment(orderEntity.getGuid(), httpServletRequest);
    }
}

Let's create save logic in service implementation. Open OrderServiceImpl class and implement save method.

OrderServiceImpl.java
@Service
public class OrderServiceImpl implements OrderService {
	@Autowired
	private OrderEntityRepository orderRepository;
 
    @Override
	public void save(String owner, OrderEntity orderEntity) {
		orderEntity.setGuid(UUID.randomUUID().toString());
		orderEntity.setOwner(owner);
		orderEntity.setModified(true);
		orderEntity.setDeleted(false);
		orderEntity.setConflicted(false);
		orderEntity.setCreationDate(new Date());
		orderRepository.save(orderEntity);
	}
}

We are inserting additional metadata here, this metadata will be needed to get from database data to synchronize. 

Consumers have to have possibility to create new orders now. We will implement listing orders in next step. To to that we have to insert this code in list method of the controller.

OrderEntityController.java
	@RequestMapping(produces = "text/html")
    public String list(Model uiModel, HttpServletRequest request) {
        User user = (org.springframework.security.core.userdetails.User) SecurityContextHolder
                .getContext().getAuthentication().getPrincipal();
        
        if (request.isUserInRole("consumers-web")) {
        	uiModel.addAttribute("orderentitys", orderService.findAllOrderEntitys(user.getUsername()));
        }
        else if(request.isUserInRole("sellers-web") || request.isUserInRole("admins-web")){
        	uiModel.addAttribute("orderentitys", orderService.findAllOrderEntitys());
        }
        return "orderentitys/list";
    }

As you can see OrderService need to have method findAllOrderEntitys(String user) which will list all of the user entities, and also have to have listAllOrderEntitys() method that will list all of the orders. We will show all orders for sellers and admins, consumers will see only theirs orders.  

OrderService.java
    ...
	List<OrderEntity> findAllOrderEntitys(String string);
	
    List<OrderEntity> findAllOrderEntitys();
    ...

Implementation of this methods looks like following:

OrderServiceImpl.java
    ...
	@Override
	public List<OrderEntity> findAllOrderEntitys(String owner) {
		return orderRepository.findByDeletedFalseAndOwner(owner);
	}
	@Override
	public List<OrderEntity> findAllOrderEntitys() {
		return orderRepository.findByDeletedFalse();
	}
    ...

Most important thing here is that we are displaying only objects which deleted field set to false. When we are deleting orders we can't do it directly, because we have to have information which object is deleted in synchronization process. Objects will be deleted directly when we will be processing synchronization data.

When adding new orders and displaying list of them works correctly we can move to udating orders. Like in creating things we have to show form first and then handle form equest. 

OrderEntityController.java
    ...
	@RequestMapping(value = "/{guid}", params = "form", produces = "text/html")
    public String updateForm(@PathVariable("guid") String guid, Model uiModel) {
        populateEditForm(uiModel, orderService.findOne(guid));
        return "orderentitys/update";
    }
    
    @RequestMapping(method = RequestMethod.PUT, produces = "text/html")
    public String update(@Valid OrderEntity orderEntity, BindingResult bindingResult, Model uiModel, HttpServletRequest httpServletRequest) {
		
        if (bindingResult.hasErrors()) {
            populateEditForm(uiModel, orderEntity);
            return "orders/update";
        }
        uiModel.asMap().clear();
        orderService.update(orderEntity);
        return "redirect:/orders/" + encodeUrlPathSegment(orderEntity.getGuid(), httpServletRequest);
    }

As you can see out service should provide two additional methods:

OrderService.java
    ...
    void update(OrderEntity orderEntity);


	OrderEntity findOne(String guid);
    ...
OrderServiceImpl.java
    ...
	@Override
	public OrderEntity findOne(String guid) {
		return orderRepository.findByDeletedFalseAndGuid(guid);
	}

	@Override
	public void update(OrderEntity orderEntity) {
		OrderEntity entity = findOne(orderEntity.getGuid());
		entity.setName(orderEntity.getName());
		entity.setDescription(orderEntity.getDescription());
		entity.setDeliveryType(orderEntity.getDeliveryType());
		entity.setStatus(orderEntity.getStatus());
		entity.setModified(true);
		orderRepository.save(entity);
	}
    ...

When we are updating order object we are ones againg setting additional metadata information needed for synchronization. 

Last of the CRUD operation to implement in our controller is deleting. 

OrderEntityController.java
    ...
	@RequestMapping(value = "/{guid}", method = RequestMethod.DELETE, produces = "text/html")
    public String delete(@PathVariable("guid") String guid, Model uiModel) {
        OrderEntity orderEntity = orderService.findOne(guid);
        orderService.delete(orderEntity);
        uiModel.asMap().clear();
        return "redirect:/orders";
    }
    ...
OrderService.java
    ...
	void delete(OrderEntity orderEntity);
    ...
OrderServiceImpl.java
    ...
	@Autowired
	private OrderItemEntityRepository orderItemRepository;
 
	@Override
	public void delete(OrderEntity orderEntity) {
		for(OrderItemEntity item : orderItemRepository.findByDeletedFalseAndOrderEntity(orderEntity.getGuid())){
			item.setModified(true);
			item.setDeleted(true);
			orderItemRepository.save(item);
		}
		orderEntity.setModified(true);
		orderEntity.setDeleted(true);
		orderRepository.save(orderEntity);
	}
    ...

Because orders are in relation with order items we have to delete all items from deleting order too. As I described before we can't delete object directly from database so we are setting deleted flag on them.

Next step is implementing CRUD operation on order item objects. Open OrderItemEntityController and skip into list method. This method list order items and shows order details. We need to add two fields - OrderService and OrderItemService to provide this method.

OrderItemEntityController.java
	...
	@Autowired
	private OrderItemService orderItemService;


	@Autowired
	private OrderService orderService;

    @RequestMapping(produces = "text/html")
    public String list(@PathVariable("orderGuid") String orderGuid, Model uiModel) {
        uiModel.addAttribute("orderentity", orderService.findOne(orderGuid));
        uiModel.addAttribute("itemId", orderGuid);
        uiModel.addAttribute("orderitementitys", orderItemService.findAll(orderGuid));
        return "orderitementitys/list";
    }
	...

As you can see we are getting list of order items in relation with specified order. Implementation of this logic should be implemented in OrderItemService. 

OrderItemService.java
public interface OrderItemService {
	List<OrderItemEntity> findAll(String orderguid);
}
OrderItemServiceImpl.java
@Service
public class OrderItemServiceImpl implements OrderItemService {
	@Autowired
	private OrderItemEntityRepository orderItemRepository;
	
	@Override
	public List<OrderItemEntity> findAll(String orderguid) {
		return orderItemRepository.findByDeletedFalseAndOrderEntity(orderguid);
	}
}

As you can see to get related objects you should only create good query into database. 

All other CRUD operations are really similar to described in order entity. You can look at this code to get info how to implement them:

OrderItemEntityController.java
@RequestMapping("orders/{orderGuid}/orderitems")
@Controller
@RooWebScaffold(path = "orderitementitys", formBackingObject = OrderItemEntity.class)
public class OrderItemEntityController {
	
    ...
	
    @RequestMapping(value = "/{itemguid}", produces = "text/html")
    public String show(@PathVariable("orderGuid") String orderguid, @PathVariable("itemguid") String itemguid, Model uiModel) {
        uiModel.addAttribute("orderitementity", orderItemService.findOne(orderguid, itemguid));
        uiModel.addAttribute("guid", orderguid);
        uiModel.addAttribute("itemId", itemguid);
        return "orderitementitys/show";
    }
    
    @RequestMapping(params = "form", produces = "text/html")
    public String createForm(@PathVariable("orderGuid") String orderguid, Model uiModel) {
    	uiModel.addAttribute("itemId", orderguid);
        populateEditForm(uiModel, new OrderItemEntity());
        return "orderitementitys/create";
    }
    
    @RequestMapping(method = RequestMethod.POST, produces = "text/html")
    public String create(@Valid OrderItemEntity orderItemEntity, @PathVariable("orderGuid") String orderguid, BindingResult bindingResult, Model uiModel, HttpServletRequest httpServletRequest) {
    	User user = (org.springframework.security.core.userdetails.User) SecurityContextHolder
                .getContext().getAuthentication().getPrincipal();
        if (bindingResult.hasErrors()) {
            populateEditForm(uiModel, orderItemEntity);
            return "orderitementitys/create";
        }
        uiModel.asMap().clear();
        orderItemService.save(user.getUsername(),orderguid,orderItemEntity);
        return "redirect:/orders/" + orderguid + "/orderitems/" + orderItemEntity.getGuid();
    }
    
    @RequestMapping(value = "/{itemguid}", params = "form", produces = "text/html")
    public String updateForm(@PathVariable("orderGuid") String orderguid, @PathVariable("itemguid") String itemguid, Model uiModel) {
    	uiModel.addAttribute("orderguid", orderguid);
        populateEditForm(uiModel, orderItemService.findOne(orderguid, itemguid));
        return "orderitementitys/update";
    }
    
    @RequestMapping(method = RequestMethod.PUT, produces = "text/html")
    public String update(@PathVariable("orderGuid") String orderguid, @Valid OrderItemEntity orderItemEntity, BindingResult bindingResult, Model uiModel, HttpServletRequest httpServletRequest) {
        if (bindingResult.hasErrors()) {
            populateEditForm(uiModel, orderItemEntity);
            return "orderitementitys/update";
        }
        uiModel.asMap().clear();
        orderItemService.update(orderItemEntity);
        return "redirect:/orders/"+orderguid + "/orderitems/"+ orderItemEntity.getGuid(); 
    }
    
    @RequestMapping(value = "/{itemguid}", method = RequestMethod.DELETE, produces = "text/html")
    public String delete(@PathVariable("orderGuid") String orderguid, @PathVariable("itemguid") String itemguid,  Model uiModel) {
        OrderItemEntity orderItemEntity = orderItemService.findOne(orderguid,itemguid);
        orderItemService.delete(orderItemEntity);
        uiModel.asMap().clear();
        return "redirect:/orders/"+orderguid+"/orderitems";
    }   
}
OrderItemService.java
public interface OrderItemService {
    ...
	OrderItemEntity findOne(String orderguid, String itemguid);
	void save(String username, String orderguid, OrderItemEntity orderItemEntity);
	void delete(OrderItemEntity orderItemEntity);
	void update(OrderItemEntity orderItemEntity);
}
OrderItemServiceImpl.java
@Service
public class OrderItemServiceImpl implements OrderItemService {
    ...

	@Override
	public OrderItemEntity findOne(String orderguid, String itemguid) {
		return orderItemRepository.findByOrderEntityAndGuid(orderguid, itemguid);
	}


	@Override
	public void save(String username, String orderguid,
			OrderItemEntity orderItemEntity) {
		orderItemEntity.setOwner(username);
		orderItemEntity.setOrderEntity(orderguid);
		orderItemEntity.setModified(true);
		orderItemEntity.setGuid(UUID.randomUUID().toString());
		orderItemRepository.save(orderItemEntity);
	}
	
	@Override
	public void delete(OrderItemEntity orderItemEntity){
		orderItemEntity.setDeleted(true); 
		orderItemEntity.setModified(true);
		orderItemRepository.save(orderItemEntity);
	}

	@Override
	public void update(OrderItemEntity orderItemEntity){
		OrderItemEntity item = orderItemRepository.findByGuid(orderItemEntity.getGuid()); 
		item.setModified(true);
		item.setName(orderItemEntity.getName());
		item.setPrice(orderItemEntity.getPrice());
		item.setQuantity(orderItemEntity.getQuantity());
		item.setUnit(orderItemEntity.getUnit());
		orderItemRepository.save(item);
	}
}

Take a look at save method in OrderItemServiceImpl class, as you can see before saving order item into database we are defining relation to specified order here. 

Synchronization

All needed operations on our models are ready, it is time for synchronization now. We will create separated controller for that called SyncController and we will define two methods there sync and syncAll. 

SynchronizationController.java
@RequestMapping("sync")
@Controller
public class SynchronizationController {
	
	@Autowired
	private MobeelizerService mobeelizerService;
	
	@RequestMapping(produces = "text/html")
    public String sync(Model uiModel, HttpServletRequest request) {
        mobeelizerService.sync(false);
        return "redirect:orders";
    }
	
	@RequestMapping(params = "all", produces = "text/html")
	public String syncAll(Model uiModel, HttpServletRequest request) {
        mobeelizerService.sync(true);
        return "redirect:orders";
    }
}

This controller is pretty easy, we are creating MobeelizerService and invoking sync method on it. Let's implement this method in MobeelizerService. 

MobeelizerService.java
public interface MobeelizerService {
    ...
	void sync(boolean all);
}

To perform synchronization we just need to call syncAndWait method from Mobeelizer Java SDK, this method gets three parametrs:

  • list of entities to synchronize
  • list of files to synchronize
  • callback which will be invoked when synchronization will finish. We implement callback interface on MobeelizerService class to handle synchronization result in the same class. 
MobeelizerServiceImpl.java
@Service
public class MobeelizerServiceImpl implements MobeelizerService , MobeelizerSyncCallback {
	...
	@Override
	public void sync(boolean all) {
		if(all){
			clearDatabase();
			mobeelizer.syncAllAndWait(this); // need to clear database before synchronization
		}
		else{
			List<Object> entities = getSyncData();
			mobeelizer.syncAndWait(entities, null, this);
		}
	}
 
	private void clearDatabase() {
		// TODO:
	}

	private List<Object> getSyncData(){
		// TODO:
	}
 
	@Override
	public void onSyncFinishedWithSuccess(Iterable<Object> entities,
			Iterable<MobeelizerFile> files, Iterable<String> deletedFiles,
			MobeelizerConfirmSyncCallback confirmCallback) {
		// TODO:
	}	

	@Override
	public void onSyncFinishedWithError(MobeelizerOperationError error) {
		// TODO:
	}
 
	@Override
	public void onSyncFinishedWithError(MobeelizerErrors databaseError) {
		// TODO:
	}
} 

As you can see we also created two additional methods, getSyncData and clearDatabase. First one gets all modified and deleted records until last synchronization from database. Second one clears database. 

MobeelizerServiceImpl.java
    ...
	private void clearDatabase() {
		for(OrderEntity entity : orderRepository.findAll()){
			orderRepository.delete(entity);
		}
		for(OrderItemEntity entity : orderItemRepository.findAll()){
			orderItemRepository.delete(entity);
		}
	}
 
    private List<Object> getSyncData(){
		List<OrderEntity> orders = orderRepository.findByModifiedTrue();
		List<OrderItemEntity> items = orderItemRepository.findByModifiedTrue();
		List<Object> entities = new ArrayList<Object>();
		
		for(OrderEntity order : orders){
			entities.add(order);
		}
		
		for(OrderItemEntity item : items ){
			entities.add(item);
		}
		
		return entities;
	}
	...

As you can see implementation of this method is really simple. We initialized synchronization now, let's handle synchronization callback. When sync will finish with success we have to add new data into database and update updated. When we do that we have to remove modification flag and remove deleted data from database.

MobeelizerServiceImpl.java
...
	@Override
	public void onSyncFinishedWithSuccess(Iterable<Object> entities,
			Iterable<MobeelizerFile> files, Iterable<String> deletedFiles,
			MobeelizerConfirmSyncCallback confirmCallback) {
		handleSyncData(entities);
		confirmCallback.confirm();
	}

	private void handleSyncData(Iterable<Object> entities) {
		for(Object entity : entities){
			if(entity instanceof OrderEntity){
				handleOrderEntity((OrderEntity) entity);
			}
			else if(entity instanceof OrderItemEntity){
				handleOrderItemEntity((OrderItemEntity) entity);
			}
		}
		removeModifiedFlag();
		removeDeletedEntities();
	}

	private void removeDeletedEntities() {
		for(OrderEntity entity : orderRepository.findByDeletedTrue()){
			orderRepository.delete(entity);
		}
		for(OrderItemEntity entity : orderItemRepository.findByDeletedTrue()){
			orderItemRepository.delete(entity);
		}
	}

	private void removeModifiedFlag() {
		for(OrderEntity entity : orderRepository.findByModifiedTrue()){
			entity.setModified(false);
			orderRepository.save(entity);
		}
		for(OrderItemEntity entity : orderItemRepository.findByModifiedTrue()){
			entity.setModified(false);
			orderItemRepository.save(entity);
		}
	}

	private void handleOrderItemEntity(OrderItemEntity entity) {
        OrderItemEntity existingOne = orderItemRepository.findByGuid(entity.getGuid());
        if(existingOne != null){
        	if(entity.isConflicted()){
	            existingOne.setConflicted(entity.isConflicted());
        	}
        	else{
	            existingOne.setName(entity.getName());
	            existingOne.setPrice(entity.getPrice());
	            existingOne.setUnit(entity.getUnit());
	            existingOne.setQuantity(entity.getQuantity());
	            existingOne.setConflicted(entity.isConflicted());
	            existingOne.setDeleted(entity.isDeleted());
        	}
            orderItemRepository.save(existingOne);
        }
        else{
            orderItemRepository.save(entity);
        }
    }
 
    private void handleOrderEntity(OrderEntity entity) {
        OrderEntity existingOne = orderRepository.findByGuid(entity.getGuid());
        if(existingOne != null){
        	if(entity.isConflicted()){
	            existingOne.setConflicted(entity.isConflicted());
        	}
        	else{
	            existingOne.setName(entity.getName());
	            existingOne.setDescription(entity.getDescription());
	            existingOne.setDeliveryType(entity.getDeliveryType());
	            existingOne.setStatus(entity.getStatus());
	            existingOne.setDeleted(entity.isDeleted());	
        	}
            orderRepository.save(existingOne);
        }
        else{
            orderRepository.save(entity);
        }
    }
...

When synchronization finish with failurewe will create message throw IllegalStateException;

MobeelizerServiceImpl.java
...
	@Override
	public void onSyncFinishedWithError(MobeelizerOperationError error) {
		throw new IllegalStateException("Synchronization failiture. Reason: "+error.getMessage());
	}
	@Override
	public void onSyncFinishedWithError(MobeelizerErrors databaseError) {
		StringBuilder builder = new StringBuilder();
		for(MobeelizerError error : databaseError.getGlobalErrors()){
			builder.append(error.getMessage()).append("\n");
		}
		throw new IllegalStateException("Synchronization failiture. Reason: "+ builder.toString());
		
	}
...

Synchronization is ready, last thing to do is displaying buttons in main menu.

Before we do that we have to add some extra values into string resources. Open WEB-INF/i18n/application.properties file and add this lines to the end. 

menu_category_synchronization_label=Synchronization
menu_item_synchronization_all_label= Full synchronization
menu_item_synchronization_diff_label=Differential synchronization

Then open views/manu.jspx file and add new menu category.

menu.jspx
...
	    <sec:authorize access="isAuthenticated()">
		    <menu:category id="c_synchronization" z="s7EsjfqoHnELj65Td4JMtijRbbo=">
		       	<sec:authorize access="hasRole('admins-web')">
			        <menu:item id="i_synchronization_all"  url="/sync?all" z="user-managed"/>        
		        </sec:authorize>
		            <menu:item id="i_synchronization_diff" url="/sync" z="user-managed"/>
		    </menu:category>   
	     </sec:authorize>
...

You can test it now, consumers and sellers can perform differential synchronization, admins can also perform full synchronization. 

Users

Our system is really functionally now. Consumers can create new orders and their items, they also can updated created objects and delete them. Sellers can list all orders created in the system, they can also modify them. Everyone can initialize synchronization process and admins can clear all database and download actual version of data from mobeelizer.
We will create one more feature - admins will have permissions to list all users and create new one. Everything we will create manually.

Create new class called MobeelizerUser with following fields in com.mobeelizer.tutorial.orders.beans package.

MobeelizerUser.java
public class MobeelizerUser {

	private String login;
	
	private String password;
	
	private String retypePassword;
	
	private String email;
	
	private String group;
 
	// TODO: Create getters and setters by your self.
}

When bean is ready create new controller class called UserController. Http interface of this controller will be following:

  • /users - displays users list
  • /users/{login} - shows user details
  • /users?form - displays form to create new user
  • /users - method POST - creates new user. 

To provide this operation we will use MobeelizerService, add this methods to it.

MobeelizerService.java
public interface MobeelizerService {
    ...

	List<MobeelizerUser> listUsers();

	MobeelizerUser findUser(String login);

	void createNewUser(MobeelizerUser user);
}

We will implement this methods on the end of this step, first we have to create view and define controller methods.

UserController.java
@Controller
@RequestMapping("/users")
public class UserController {
	@Autowired
	private MobeelizerService mobeelizerService;
	
	@RequestMapping(value="/{login}",produces = "text/html")
	public String show(@PathVariable("login") String login, Model uiModel){
		uiModel.addAttribute("user", mobeelizerService.findUser(login));
		return "users/show";
	}
	
	@RequestMapping(produces = "text/html")
	public String list(Model uiModel){
		uiModel.addAttribute("users", mobeelizerService.listUsers());
		return "users/list";
	}
	
	@RequestMapping(params = "form", produces = "text/html")
	public String createForm(Model uiModel){
		uiModel.addAttribute("user",new MobeelizerUser());
		return "users/create";
	}
	
	@RequestMapping(method = RequestMethod.POST, produces = "text/html")
    public String create(@Valid MobeelizerUser user, BindingResult bindingResult, Model uiModel, HttpServletRequest httpServletRequest) {
		mobeelizerService.createNewUser(user);
        return "redirect:/users/" + user.getLogin();
    }
}

Everything in this code should be clear and easy, we just invoking method defined in MobeelizerService. 

Before we create new views we have to add resource string into WEB-INF/i18n/application.property file.

menu_category_mobeelizeruser_label=User
menu_item_mobeelizeruser_list_label=Users
menu_item_mobeelizeruser_new_label=User
label_com_mobeelizer_tutorial_orders_model_mobeelizeruser=User
label_com_mobeelizer_tutorial_orders_model_mobeelizeruser_login=Login
label_com_mobeelizer_tutorial_orders_model_mobeelizeruser_email=E-mail
label_com_mobeelizer_tutorial_orders_model_mobeelizeruser_group=Group
label_com_mobeelizer_tutorial_orders_model_mobeelizeruser_plural=User
label_com_mobeelizer_tutorial_orders_model_mobeelizeruser_password=Password
label_com_mobeelizer_tutorial_orders_model_mobeelizeruser_retypepassword=Retype password

We can move to creating .jspx files. Create new directory called users inside view folder and create four files inside:

  • views.xml
  • create.jspx
  • show.jspx
  • list.jspx

First xml file contains information about available views. 

views.xml
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE tiles-definitions PUBLIC "-//Apache Software Foundation//DTD Tiles Configuration 2.1//EN" "http://tiles.apache.org/dtds/tiles-config_2_1.dtd">
<tiles-definitions>
    <definition extends="default" name="users/list">
        <put-attribute name="body" value="/WEB-INF/views/users/list.jspx"/>
    </definition>		
	<definition extends="default" name="users/show">
        <put-attribute name="body" value="/WEB-INF/views/users/show.jspx"/>
    </definition>	
	<definition extends="default" name="users/create">
        <put-attribute name="body" value="/WEB-INF/views/users/create.jspx"/>
    </definition>
</tiles-definitions>

Second one is definition of create form. 

create.jspx
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<div xmlns:c="http://java.sun.com/jsp/jstl/core" xmlns:field="urn:jsptagdir:/WEB-INF/tags/form/fields" xmlns:form="urn:jsptagdir:/WEB-INF/tags/form" xmlns:jsp="http://java.sun.com/JSP/Page" xmlns:spring="http://www.springframework.org/tags" version="2.0">
    <jsp:directive.page contentType="text/html;charset=UTF-8"/>
    <jsp:output omit-xml-declaration="yes"/>
    <form:create id="fc_com_mobeelizer_tutorial_orders_model_MobeelizerUser" modelAttribute="user" path="/users" render="${empty dependencies}" z="x5Tj1F6EFgrP8VQToUTU8wLzTgs=">
        <field:input required="true"  field="login" id="c_com_mobeelizer_tutorial_orders_model_MobeelizerUser_login" z="5umtRilJh55b3f57QvTsaiUa5UE="/>
        <field:input required="true"  field="group" id="c_com_mobeelizer_tutorial_orders_model_MobeelizerUser_group" z="hByOuYT7az02X5hx/9a0MIdjuIM="/>
        <field:input required="true"  type="password" field="password" id="c_com_mobeelizer_tutorial_orders_model_MobeelizerUser_password" z="sAbkGPvYxv2rR2T5mNOvpXNvwZE="/>
        <field:input required="true" type="password" field="retypePassword" id="c_com_mobeelizer_tutorial_orders_model_MobeelizerUser_retypePassword" z="oLBnQfut1YLjqgcqJQeZLXV1w38="/>
    </form:create>
    <form:dependency dependencies="${dependencies}" id="d_com_mobeelizer_tutorial_orders_model_MobeelizerUser" render="${not empty dependencies}" z="+M1xwCa8UoOx9vlyzfIq9mbtS6Y="/>
</div>

Next one, contains page to display user details.

show.jspx
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<div xmlns:field="urn:jsptagdir:/WEB-INF/tags/form/fields" xmlns:jsp="http://java.sun.com/JSP/Page" xmlns:page="urn:jsptagdir:/WEB-INF/tags/form" version="2.0">
    <jsp:directive.page contentType="text/html;charset=UTF-8"/>
    <jsp:output omit-xml-declaration="yes"/>
    <page:show id="ps_com_mobeelizer_tutorial_orders_model_MobeelizerUser" object="${user}" path="/users" z="F4tw6sniTej4XeM0SxVhSwc6l2w="  delete="false" update="false">
        <field:display field="login" id="s_com_mobeelizer_tutorial_orders_model_MobeelizerUser_login" object="${user}" z="2rOE3qmMI7qlpPXqSogO6ZG6RUg="/>
        <field:display field="email" id="s_com_mobeelizer_tutorial_orders_model_MobeelizerUser_email" object="${user}" z="YdS5n05aLrZh8ilekzoT5kgG/OA="/>
        <field:display field="group" id="s_com_mobeelizer_tutorial_orders_model_MobeelizerUser_group" object="${user}" z="NMQme+9h76C4Ft2Jz00Y932Qbw0="/>
    </page:show>
</div>

Last one is list of users.

list.jspx
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<div xmlns:jsp="http://java.sun.com/JSP/Page" xmlns:page="urn:jsptagdir:/WEB-INF/tags/form" xmlns:table="urn:jsptagdir:/WEB-INF/tags/form/fields" version="2.0">
    <jsp:directive.page contentType="text/html;charset=UTF-8"/>
    <jsp:output omit-xml-declaration="yes"/>
    <page:list id="pl_com_mobeelizer_tutorial_orders_model_MobeelizerUser" items="${users}" z="sRHyZ9hnhB/V+uS0DzE0tMydDi8=">
        <table:table data="${users}" id="l_com_mobeelizer_tutorial_orders_model_MobeelizerUser" path="/users" z="r6DZHFtlLL8awO3DeWfHpGtRJjE=" typeIdFieldName="login" update="false" delete="false">
            <table:column id="c_com_mobeelizer_tutorial_orders_model_MobeelizerUser_login" property="login" z="Z4pdMEmgUs2jGFlItfTVR48meiI="/>
            <table:column id="c_com_mobeelizer_tutorial_orders_model_MobeelizerUser_group" property="group" z="bn6lU0MMjQPEqclaXfaxxFnwmGI="/>
            <table:column id="c_com_mobeelizer_tutorial_orders_model_MobeelizerUser_email" property="email" z="Y3e5aw4poLyNh0MgROhmfgQor6g="/>
        </table:table>
    </page:list>
</div>

 Last, but not the least thing to do is adding new category into main menu. Only user from admins group can see this category. 

menu.jspx
...
		<sec:authorize access="hasRole('admins-web')">
            <menu:category id="c_mobeelizeruser" z="AVr5u+rXTamXkVuz6kw0iO3fxUc=">
                <menu:item id="i_mobeelizeruser_new" messageCode="global_menu_new" url="/users?form" z="user-managed"/>
                <menu:item id="i_mobeelizeruser_list" messageCode="global_menu_list" url="/users" z="user-managed"/>
            </menu:category>
        </sec:authorize>
...

It is time for most interesting place of this paragraph - implementing business logic. Open MobelizerServiceImpl class and take a look at below code:

MobeelizerServiceImpl.java
...
	@Override 
	public List<MobeelizerUser> listUsers() {
		List<MobeelizerUser> users = new ArrayList<MobeelizerUser>();
		List<com.mobeelizer.java.api.user.MobeelizerUser> downloadedUsers = mobeelizer.getUsers();
		for(com.mobeelizer.java.api.user.MobeelizerUser downloadedUser : downloadedUsers){
			MobeelizerUser user = new MobeelizerUser();
			user.setLogin(downloadedUser.getLogin());
			user.setEmail(downloadedUser.getMail());
			user.setGroup(downloadedUser.getGroup());
			users.add(user);
		}
		
		return users;
	}

	@Override
	public MobeelizerUser findUser(String login) {
		com.mobeelizer.java.api.user.MobeelizerUser downloadedUser = mobeelizer.getUser(login);
		if(downloadedUser != null){
			MobeelizerUser user = new MobeelizerUser();
			user.setLogin(downloadedUser.getLogin());
			user.setEmail(downloadedUser.getMail());
			user.setGroup(downloadedUser.getGroup());
			
			return user;
		}
		return null;
	}

	@Override
	public void createNewUser(MobeelizerUser user) {
		if(!user.getPassword().equals(user.getRetypePassword())){
			throw new IllegalStateException("Passwords not equals.");
		}
		com.mobeelizer.java.api.user.MobeelizerUser newUser = new com.mobeelizer.java.api.user.MobeelizerUser();
		newUser.setAdmin(false);
		newUser.setGroup(user.getGroup());
		newUser.setLogin(user.getLogin());
		newUser.setMail(user.getEmail());
		newUser.setPassword(user.getPassword());
		mobeelizer.createUser(newUser);
	}

It is pretty simple, we are just calling Mobeelizer object methods and mapping MobeelizerUser class from Java SDK to our own and that is all. 

Conclusion

To conclude, this example presents synchronization between web application and Mobeelizer cloud. We created application to handle orders and their items by consumers and sellers. We also created admin module to initialize web application database and to operate on users. 

You have learned how to design our database and how to handle CRUD operation on models, to have possibility to perform synchronization with Mobeelizer cloud, we saw how to use Mobeelizer users database in our system, and how to operate on them. 

 

 

Attachments:

orders_newApp.png (image/png)
orderEntity_newModel.png (image/png)
orderEntity_fields.png (image/png)
orderItem_newModel.png (image/png)
orderItem_fields.png (image/png)
groups.png (image/png)
devices.png (image/png)
orderEntity_credentials.png (image/png)
superUser.png (image/png)
users.png (image/png)
script.roo (application/octet-stream)
script.roo (application/octet-stream)
script.roo (application/octet-stream)
devices.png (image/png)