Documentation : Java Conflicts Resolving Tutorial

This tutorial shows how to resolve conflict manualy. We will create separate tool, which will display conflict history to the user and give him the oportunity to select correct version of the entity. 

This tutorial is continuation of Java Advanced Tutorial. Before you start it, be sure that previous one is completed and running.

In this document we assume that you are familiar with:

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

As you remember in our example we have three groups of users: consumers, sellers and admins. In this tutorail consumers will not be allowed to display and resolve conflicts, only seller and admins will be allowed to do that. We will mark conflicted entities with red color on the list and show additional button which will move user to conflict resolving tool.

Add additional resources to the project

Open project from previous tutorial and add:

  • conflict.png  to src/main/webapp/images/
  • additional strings to the application.properties file.

    application.properties
    label_com_mobeelizer_tutorial_orders_model_orderconflictversion_plural=Order Entity Versions
    label_com_mobeelizer_tutorial_orders_model_orderitemconflictversion_plural=Order Item Entity Versions
    label_com_mobeelizer_tutorial_orders_model_orderconflictversion_user=User
    label_com_mobeelizer_tutorial_orders_model_orderconflictversion_device=Device Category
    label_com_mobeelizer_tutorial_orders_model_orderconflictversion_date=Date
    label_com_mobeelizer_tutorial_orders_model_orderitemconflictversion_user=User
    label_com_mobeelizer_tutorial_orders_model_orderitemconflictversion_device=Device Category
    label_com_mobeelizer_tutorial_orders_model_orderitemconflictversion_date=Date

Create Controller

We will start with creating new controller responsible for listing entity versions and resolving conflict. We will call it ConflictResolverController. 

Create new class in com.mobeelizer.tutotiral.orders.controllers package called  ConflictResolverController. Implement it like on the following listing. 

ConflictResolverController.java
@Controller
@RequestMapping("")
public class ConflictResolverController {
	
	@RequestMapping(value="orders/conflict/{guid}", produces = "text/html")
    public String showOrderConflict(@PathVariable("guid") String guid, Model uiModel){


        return "conflicts/showOrders";
    }
    
	@RequestMapping(value="orders/conflict/{guid}/resolve", produces = "text/html", method= RequestMethod.POST)
    public String resolveOrderConflict(@Valid OrderEntity order, @PathVariable("guid") String guid, Model uiModel){


        return "conflicts/resolveResult";
    }
	
	@RequestMapping(value="orders/{orderGuid}/orderitems/conflict/{itemGuid}", produces = "text/html")
    public String showOrderItemConflict(@PathVariable("itemGuid") String guid, Model uiModel){

        return "conflicts/showItems";
    }
    
	@RequestMapping(value="orders/{orderGuid}/orderitems/conflict/{itemGuid}/resolve", produces = "text/html", method= RequestMethod.POST)
    public String resolveOrderItemConflict(@Valid OrderItemEntity orderItem, @PathVariable("itemGuid") String guid, Model uiModel){

        return "conflicts/resolveResult";
    }
}

As you can see there are four methods, two of them to list conflict entity versions and two to resolve conflicts. These methods are empty now (we will implement them later), and they are returning reference to view objects. We have to create them to make this code running. 

Create conflicts directory in src/main/webapp/WEB-INF/views path and four files inside. 

  • resolveResult.jspx
  • showItems.jspx
  • showOrders.jspx
  • views.xml

Jspx files will contain view implementation, for now you can leave them empty. Open views.xml file and create definition of our views inside:

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="conflicts/showOrders">
        <put-attribute name="body" value="/WEB-INF/views/conflicts/showOrders.jspx"/>
    </definition> 
    <definition extends="default" name="conflicts/showItems">
        <put-attribute name="body" value="/WEB-INF/views/conflicts/showItems.jspx"/>
    </definition> 
    <definition extends="default" name="conflicts/resolveResult">
        <put-attribute name="body" value="/WEB-INF/views/conflicts/resolveResult.jspx"/>
    </definition> 
</tiles-definitions>

Mark entity as conflicted 

In this step we will display on our list pages that entity is in conflict state and we will show the button to display conflict history. To do that we will make a little modification in tagx files. Open table.tagx file placed in src/main/webapp/WEB-INF/tags/form/fields. 

In this file we will add another feature that table will support. When conflictResolving flag will be up we will check if entity (row) is in conflict and if it is, we will show button that will move as to conflict history page. At the begining add new attribute to table object. 

table.tagx
...
<jsp:directive.attribute name="conflictResolving" type="java.lang.Boolean" required="false" rtexprvalue="true" description="Indicates whether conflict resolving is enabled." />
...

Our attribute is optional and if it is not set we want to set it to false. 

table.tagx
...
    <c:if test="${empty conflictResolving}">
      <c:set var="conflictResolving" value="false" />
    </c:if>
...

Flag is ready, lets create some logic when it is up.  Find forEach loop in the file - this loop gets all items from given list and is dispaying them in rows. It is good place to check if item is in conflict. 

table.tagx
...
	<c:forEach items="${data}" var="item">   
      	<c:set var="conflictedClass" value=" "/>
       	<c:set var="conflicted" value="false"/>
      	<c:if test="${conflictResolving}">
      		<c:if test="${item['conflicted']}">
      			<c:set var="conflicted" value="true"/>
      			<c:set var="conflictedClass" value="conflicted"/>
      		</c:if>
      	</c:if>
        <tr class="${conflictedClass}">
...

We have one additional variable here that have not be described before - conflictedClass. When entity is in conflict we want to mark it on the list by red backgroud. From now row will be marked with conflicted class attribute, to complete this feature we have to edit css file. 

standard.css
.conflicted{
	background:#DFBDBD ! important;
}

Last thing here is adding resolve conflict button. We will show it instead of update button. 

table.tagx
...
      <c:forEach>
		...
          <c:if test="${conflicted}">
          	<td class="utilbox">
	 	        <spring:url value="${path}/conflict/${itemId}" var="conflict_form_url" />
			    <spring:url value="/resources/images/conflict.png" var="conflict_image_url" />
			    <c:set value="Resolve conflict" var="conflict_label" />
			    <a href="${conflict_form_url}" alt="${fn:escapeXml(conflict_label)}" title="${fn:escapeXml(conflict_label)}">
			    	<img alt="${fn:escapeXml(conflict_label)}" class="image" src="${conflict_image_url}" title="${fn:escapeXml(conflict_label)}" />
			 	</a>
		     </td>
          </c:if>
          <c:if test="${update and not(conflicted)}">
			...
      </c:forEach>
...

When table.tagx is ready last thing to do is modifing list.jspx files from views/orderentitys and views/orderitementitys directories. As I wrote at the beginning of this tutorial only sellers and admins have permission to resolve conflicts, let's implement it:

orderentitys/list.jspx
...
    <sec:authorize access="hasRole('consumers-web')">
		...
        <c:set value="false" var="conflictResolving"/>
    </sec:authorize>
    <sec:authorize access="hasAnyRole('admins-web','selers-web')">
        ...
        <c:set value="true" var="conflictResolving"/>
    </sec:authorize>
    <jsp:output omit-xml-declaration="yes"/>
    <page:list id="pl_com_mobeelizer_tutorial_orders_model_OrderEntity" items="${orderentitys}">
        <table:table create="${create}" data="${orderentitys}" delete="${delete}" conflictResolving="${conflictResolving}"...> 
...
orderitementitys/list.jspx
...
    <sec:authorize access="hasRole('consumers-web')">
		...
        <c:set value="false" var="conflictResolving"/>
    </sec:authorize>
    <sec:authorize access="hasAnyRole('admins-web','selers-web')">
        ...
        <c:set value="true" var="conflictResolving"/>
    </sec:authorize>
...
<page:list id="pl_com_mobeelizer_tutorial_orders_model_OrderItemEntity" items="${orderitementitys}">
        <table:table  typeIdFieldName="guid" create="${create}" conflictResolving="${conflictResolving}" data="${orderitementitys}"...>
...

 

Get conflict history from Mobeelizer

Next thing to do is implementing getting conflict history. In Mobeelizer SDK there is method dedicated to get entity conflict history called getConflictHistory, we will use it. At the begining we have to create new methods in MobeelizerService interface. 

MobeelizerService.java
 public interface MobeelizerService {

	...

	List<MobeelizerEntityVersion> getOrderConflict(String guid);

	List<MobeelizerEntityVersion> getOrderItemConflict(String guid);
}

... and implement them in MobeelizerServiceImpl class. 

MobeelizerServiceImpl.java
@Service
public class MobeelizerServiceImpl implements MobeelizerService , MobeelizerSyncCallback {


	... 
 
	@Override
	public List<MobeelizerEntityVersion> getOrderConflict(String guid) {
		return getConflict("OrderEntity", guid);
	}

	@Override
	public List<MobeelizerEntityVersion> getOrderItemConflict(String guid) {
		return getConflict("OrderItemEntity", guid);
	}

	private List<MobeelizerEntityVersion> getConflict(String model, String guid){
		final List<MobeelizerEntityVersion> versionsResult = new ArrayList<MobeelizerEntityVersion>();
		mobeelizer.getConflictHistory(model, guid, new MobeelizerGetConflictHistoryCallback(){
			@Override
			public void onFinishedWithSuccess(Iterable<MobeelizerEntityVersion> versions) {
				for(MobeelizerEntityVersion version : versions){
					versionsResult.add(version);
				}
			}
			@Override
			public void onFinishedWithError(MobeelizerOperationError error) {
				throw new IllegalStateException(error.getMessage());
			}
		
		});
		return versionsResult;	
	}
}

As you can se in implemented method we are invoking getConflictHistory method from Mobeelizer object and that is all. 

We can come back to our controller class now and call methods from just implemented service. 

ConflictResolverController.java
@Controller
@RequestMapping("")
public class ConflictResolverController {

	@Autowired
	private MobeelizerService mobeelizerService;
	
	@RequestMapping(value="orders/conflict/{guid}", produces = "text/html")
    public String showOrderConflict(@PathVariable("guid") String guid, Model uiModel){
		uiModel.addAttribute("orderversions", mobeelizerService.getOrderConflict(guid));
        return "conflicts/show";
    }

	@RequestMapping(value="orders/{orderGuid}/orderitems/conflict/{itemGuid}", produces = "text/html")
    public String showOrderItemConflict(@PathVariable("itemGuid") String guid, Model uiModel){
		uiModel.addAttribute("orderversions", mobeelizerService.getOrderItemConflict(guid));
        return "conflicts/showItems";
    }
	
	...
}

Display history to users

Next step is creating views for our controller. Open showOrders.jspx file, created before. Our view will be injected into html page sceleton so we will start with creating div object. 

showOrders.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="http://www.springframework.org/tags/form"  
	xmlns:util="urn:jsptagdir:/WEB-INF/tags/util"   	
	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" 
	xmlns:spring="http://www.springframework.org/tags" 
	xmlns:fn="http://java.sun.com/jsp/jstl/functions"  version="2.0" >

  	...

</div>

In our div object there is a lot of namespace declaration we will need them to create objects like page, form, fields and so on. We will show list of entity versions so next step is creating page:list object inside the div.

showOrders.jspx
<div ...>
    <jsp:directive.page contentType="text/html;charset=UTF-8"/>
    <jsp:output omit-xml-declaration="yes"/>
	<page:list id="ps_com_mobeelizer_tutorial_orders_model_OrderConflictVersion" items="${orderversions}" >
		...
	<page:list/>
</div>

Page is ready so we can list entities now, create foreach loop inside the page, and show entities in spearated panel's. Title of the panel will be the version date. 

showOrders.jspx
...	
	<page:list ...>
		<c:forEach items="${orderversions}" var="item">
			<util:panel id="${item['date']}" title="${item['date']}" openPane="false">
			...
			</util:panel>
		</c:forEach>
	</page:list>
...

Each version contains following fields:

  • date - date of version creation (actually of its synchronization with mobeelizer)
  • user - user who made the modification
  • device - device category where modification was made. 
  • entity 

We will display this information inside the panel. Entity object will be shown in spearated panel inside. 

showOrders.jspx
...	
			<util:panel id="${item['date']}" title="${item['date']}" openPane="false">
				<field:display field="user" 
							   id="s_com_mobeelizer_tutorial_orders_model_OrderConflictVersion_user" 
							   object="${item}" />
				<field:display field="date" 
							   id="s_com_mobeelizer_tutorial_orders_model_OrderConflictVersion_date" 
							   object="${item}" />
				<field:display field="device" 
							   id="s_com_mobeelizer_tutorial_orders_model_OrderConflictVersion_device" 
							   object="${item}" />
				<br/>
				<util:panel id="${item['date']}_${item['user']}" title="Entity" openPane="false">
					...
				</util:panel>
			</util:panel>
...

Inside entity panel we have to create two things, display entity fields and show button that help as select version as correct one. To create such a button we will use form object with hidden fields, we will use it becouse we have to send order entity using POST method to the controller. There is one think more, when entity deleted flag is up we will show message "Entity deleted".

showOrders.jspx
...	
				<util:panel id="${item['date']}_${item['user']}" title="Entity" openPane="false">
					<c:choose>
						<c:when test="${item['entity']['deleted']}">
							Entity deleted
							<br /><br />
						</c:when>
						<c:otherwise>							
							<field:display field="name" 
										   id="s_com_mobeelizer_tutorial_orders_model_OrderEntity_name" 
										   object="${item['entity']}" />						
							<field:display field="description" 
                                           id="s_com_mobeelizer_tutorial_orders_model_OrderEntity_description" 
                                           object="${item['entity']}" />						
							<field:display date="true" 
                                           dateTimePattern="${orderEntity_creationdate_date_format}" 
                                           field="creationDate" 
                                           id="s_com_mobeelizer_tutorial_orders_model_OrderEntity_creationDate" 
                                           object="${item['entity']}" />
							<field:display field="deliveryType"  
										   id="s_com_mobeelizer_tutorial_orders_model_OrderEntity_deliveryType" 
										   object="${item['entity']}" />
							<field:display field="status" 
										   id="s_com_mobeelizer_tutorial_orders_model_OrderEntity_status" 
										   object="${item['entity']}" />	
						</c:otherwise>
					</c:choose>				
					<form:form action="${item['entity']['guid']}/resolve" method="POST" modelAttribute="${modelAttribute}" >	
						<input name="name" value="${item['entity']['name']}" type="hidden"/> 
						<input name="description" value="${item['entity']['description']}" type="hidden"/> 
						<input name="deliveryType" value="${item['entity']['deliveryType']}" type="hidden"/> 
						<input name="status" value="${item['entity']['status']}" type="hidden"/> 
						<input name="deleted" value="${item['entity']['deleted']}" type="hidden"/> 		
						<div class="submit" id="${fn:escapeXml(id)}_submit">
						  <c:set var="save_button" value="Select" />
						  <script type="text/javascript">
								Spring.addDecoration(new Spring.ValidateAllDecoration({elementId:'proceed', event:'onclick'}));
						  </script>
						  <input id="proceed" type="submit" value="${fn:escapeXml(save_button)}"/>
						</div>
					</form:form>
				</util:panel>
...

List of Order entity versions is ready, we can create list of Order Item versions now. It is analogous to just created code and it should look like this:

showItems.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="http://www.springframework.org/tags/form"  
	 xmlns:util="urn:jsptagdir:/WEB-INF/tags/util"  
	 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:spring="http://www.springframework.org/tags"
	 xmlns:fn="http://java.sun.com/jsp/jstl/functions"  version="2.0" >
    <jsp:directive.page contentType="text/html;charset=UTF-8"/>
    <jsp:output omit-xml-declaration="yes"/>
    <page:list id="ps_com_mobeelizer_tutorial_orders_model_OrderItemConflictVersion" label="Order Versions" items="${orderversions}" >
		<c:forEach items="${orderversions}" var="item">
			<util:panel id="${item['date']}" title="${item['date']}" openPane="false">
				<field:display field="user" 
							   id="s_com_mobeelizer_tutorial_orders_model_OrderItemConflictVersion_user" 
							   object="${item}" />
				<field:display field="date" 
  							   id="s_com_mobeelizer_tutorial_orders_model_OrderItemConflictVersion_date" 
			   				   object="${item}" />
				<field:display field="device" 
							   id="s_com_mobeelizer_tutorial_orders_model_OrderItemConflictVersion_device" 
							   object="${item}"/>
				<br/>
				<util:panel id="${item['date']}_${item['user']}" title="Entity" openPane="false">
					<c:choose>
						<c:when test="${item['entity']['deleted']}">
							Entity deleted
							<br /><br />
						</c:when>
						<c:otherwise>	
						<field:display field="name" 
									   id="s_com_mobeelizer_tutorial_orders_model_OrderItemEntity_name"
									   object="${item['entity']}" />
						<field:display field="price"
									   id="s_com_mobeelizer_tutorial_orders_model_OrderItemEntity_price"
									   object="${item['entity']}" />
						<field:display field="quantity"
									   id="s_com_mobeelizer_tutorial_orders_model_OrderItemEntity_quantity"
									   object="${item['entity']}" />
						<field:display field="unit"
									   id="s_com_mobeelizer_tutorial_orders_model_OrderItemEntity_unit"
									   object="${item['entity']}" />						
						</c:otherwise>
					</c:choose>				
				<form:form action="${item['entity']['guid']}/resolve" method="POST" modelAttribute="${modelAttribute}" >	
					<input name="name" value="${item['entity']['name']}" type="hidden"/> 
					<input name="price" value="${item['entity']['price']}" type="hidden"/> 
					<input name="quantity" value="${item['entity']['quantity']}" type="hidden"/> 
					<input name="unit" value="${item['entity']['unit']}" type="hidden"/> 
					<input name="deleted" value="${item['entity']['deleted']}" type="hidden"/> 		
					<div class="submit" id="${fn:escapeXml(id)}_submit">
					  <c:set var="save_button" value="Select" />
					  <script type="text/javascript">
							Spring.addDecoration(new Spring.ValidateAllDecoration({elementId:'proceed', event:'onclick'}));
					  </script>
					  <input id="proceed" type="submit" value="${fn:escapeXml(save_button)}"/>
					</div>
					</form:form>
				</util:panel>
			</util:panel>
			<br />
		</c:forEach >
    </page:list>
</div>

Resolve conflict

Resolving conflict with mobeelizer is really easy, because it is just synchronization. One additional thing that we have to do is setting correct values in conflicted entity fields and mark it with resolveConflict flag. We didn't create this flag before so we will do it now. Open OrderEntity.java from com.mobeelizer.tutorial.orders.model package and add new field.

OrderEntity.java
@RooJavaBean
@RooToString
@RooJpaActiveRecord
public class OrderEntity {
    private String guid;
    private String owner;
    private boolean modified;
    private boolean deleted;
    private boolean conflicted;
    private String description;
    private String name;
    
    private boolean resolveConflict;


    @Temporal(TemporalType.TIMESTAMP)
    @DateTimeFormat(style = "M-")
    private Date creationDate;
    private int deliveryType;
    private int status;
 
	public boolean isResolveConflict(){
		return this.resolveConflict;
	}
 
	public void setResolveConflict(boolean resolveConflict){
		this.resolveConflict = resolveConflict;
	}
}

Because we are not using roo console in this tutorial we have to create getters and setters by our self.

Do the same with OrderItemEntity class. 

OrderItemEntity.java
@RooJavaBean
@RooToString
@RooJpaActiveRecord
public class OrderItemEntity {
    private String guid;
    private String owner;
    private boolean conflicted;
    private boolean deleted;
    private boolean modified;
    private String name;
    private double price;
    private double quantity;
    private String units;
    private String orderEntity;
    private String unit;


	private boolean resolveConflict;

	public boolean isResolveConflict(){
		return this.resolveConflict;
	}
	public void setResolveConflict(boolean resolveConflict){
		this.resolveConflict = resolveConflict;
	}
}

Our models are ready. Create new methods in MobeelizerService responsible for conflict resolving. 

MobeelizerService.java
public interface MobeelizerService {

	...
 
	void resolveOrderConflict(String guid, OrderEntity orderEntity);


	void resolveOrderItemConflict(String guid, OrderItemEntity orderItemEntity);
}

.. and implement them in MobeelizerServiceImpl class.

MobeelizerService.Impl
@Service
public class MobeelizerServiceImpl implements MobeelizerService , MobeelizerSyncCallback {
	...
	@Override
	public void resolveOrderItemConflict(String guid,
			OrderItemEntity orderItemEntity) {
		OrderItemEntity orderItem = orderItemRepository.findByGuid(guid);
		orderItem.setName(orderItemEntity.getName());
		orderItem.setPrice(orderItemEntity.getPrice());
		orderItem.setQuantity(orderItemEntity.getQuantity());
		orderItem.setUnit(orderItemEntity.getUnit());
		orderItem.setModified(true);
		orderItem.setConflicted(false);
		orderItem.setDeleted(orderItemEntity.isDeleted());
		orderItem.setResolveConflict(true);
		orderItemRepository.save(orderItem);
		try{
			sync(false);
		}catch(IllegalStateException e){
			orderItem.setConflicted(true);
			orderItemRepository.save(orderItem);
			throw e;
		}
	}
	
	@Override
	public void resolveOrderConflict(String guid, OrderEntity orderEntity) {
		OrderEntity order = orderRepository.findByGuid(guid);
		order.setName(orderEntity.getName());
		order.setDescription(orderEntity.getDescription());
		order.setConflicted(false);
		order.setStatus(orderEntity.getStatus());
		order.setDeliveryType(orderEntity.getDeliveryType());
		order.setModified(true);
		order.setDeleted(orderEntity.isDeleted());
		order.setResolveConflict(true);
		orderRepository.save(order);
		try{
			sync(false);
		}catch(IllegalStateException e){
			order.setConflicted(true);
			orderRepository.save(order);
			throw e;
		}
	}
}

It is easy, right? We have to create one more thing, when synchronization finished with success we have to clear resolveConflict flag for all entities. Find removeModifiedFlag() method and add two additional lines. 

MobeelizerServiceImpl.java
...
	private void removeModifiedFlag() {
        for(OrderEntity entity : orderRepository.findByModifiedTrue()){
            entity.setModified(false);
            entity.setResolveConflict(false);  // just added
            orderRepository.save(entity);
        }
        for(OrderItemEntity entity : orderItemRepository.findByModifiedTrue()){
            entity.setModified(false);
            entity.setResolveConflict(false); // just added
            orderItemRepository.save(entity);
        }
    }
...

When it is done we can invoke our methods in ConflictResolverController now. 

ConflictResolverController.java
@Controller
@RequestMapping("")
public class ConflictResolverController {

	...

	@RequestMapping(value="orders/conflict/{guid}/resolve", produces = "text/html", method= RequestMethod.POST)
    public String resolveOrderConflict(@Valid OrderEntity order, @PathVariable("guid") String guid, Model uiModel){
		try{
			mobeelizerService.resolveOrderConflict(guid, order);
			uiModel.addAttribute("success", true);
		}
		catch (Exception e){
			uiModel.addAttribute("success", false);
		}
        return "conflicts/resolveResult";
    }
    
	@RequestMapping(value="orders/{orderGuid}/orderitems/conflict/{itemGuid}/resolve", produces = "text/html", method= RequestMethod.POST)
    public String resolveOrderItemConflict(@Valid OrderItemEntity orderItem, @PathVariable("itemGuid") String guid, Model uiModel){
		try{
			mobeelizerService.resolveOrderItemConflict(guid, orderItem);
			uiModel.addAttribute("success", true);
		}
		catch (Exception e){
			uiModel.addAttribute("success", false);
		}
        return "conflicts/resolveResult";
    }
}

Last thing to do is creating resolve result view in resolveResult.jspx file. Open it and implement like follows.

resolveResul.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"  version="2.0">
    <jsp:directive.page contentType="text/html;charset=UTF-8"/>
    <jsp:output omit-xml-declaration="yes"/>
    <c:choose>
		<c:when test="${success}">
			<h2> Conflict resolved </h2>
		</c:when>
		<c:otherwise>
			<h2> Conflict resolving finished with error.</h2>
		</c:otherwise>
	</c:choose>
</div>

Conclusion

To conclude, this example presents how to create own tool to resolve conflict manualy. We added few features to application from previous tutorial. When entity is in conflict state, it is mark on the list with red color and button to resolving conflict is enabled. In resolving tool we have shown list of entity versions since conflict has occurred and give user oportunity to select correct version. 

You learned how to get conflict history. You also have seen that conflict resolving is normal synchronization of entities with one additional field - resolveConflict - set to true.

Attachments:

conflict.png (image/png)