Skip to content

Commit

Permalink
Merge pull request #332 from devgateway/325-optlock-dgtkit2
Browse files Browse the repository at this point in the history
Add optimistic locking to hibernate entities dgtkit v2
  • Loading branch information
mpostelnicu authored Oct 27, 2020
2 parents b15bd17 + f826f3d commit 0f3953b
Show file tree
Hide file tree
Showing 6 changed files with 126 additions and 32 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
</div>
<div wicket:id="deleteModal"></div>
<div wicket:id="deleteFailedModal"></div>
<div wicket:id="saveFailedModal"></div>
</form>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@
package org.devgateway.toolkit.forms.wicket.page.edit;

import de.agilecoders.wicket.core.markup.html.bootstrap.button.Buttons;
import de.agilecoders.wicket.core.markup.html.bootstrap.common.NotificationMessage;
import de.agilecoders.wicket.core.markup.html.bootstrap.dialog.TextContentModal;
import de.agilecoders.wicket.core.markup.html.bootstrap.form.BootstrapForm;
import de.agilecoders.wicket.core.util.Attributes;
import de.agilecoders.wicket.extensions.markup.html.bootstrap.ladda.LaddaAjaxButton;
import nl.dries.wicket.hibernate.dozer.DozerModel;
import org.apache.wicket.Page;
import org.apache.wicket.ajax.AjaxEventBehavior;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.attributes.AjaxRequestAttributes;
Expand All @@ -30,7 +30,6 @@
import org.apache.wicket.model.StringResourceModel;
import org.apache.wicket.request.mapper.parameter.PageParameters;
import org.apache.wicket.spring.injection.annot.SpringBean;
import org.apache.wicket.util.time.Duration;
import org.apache.wicket.util.visit.IVisit;
import org.apache.wicket.util.visit.IVisitor;
import org.apache.wicket.validation.ValidationError;
Expand All @@ -51,6 +50,7 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.orm.ObjectOptimisticLockingFailureException;

import javax.persistence.EntityManager;
import java.io.Serializable;
Expand Down Expand Up @@ -116,6 +116,8 @@ private T newInstance() {

protected TextContentModal deleteFailedModal;

protected TextContentModal saveFailedModal;

@SpringBean
private EntityManager entityManager;

Expand Down Expand Up @@ -178,6 +180,7 @@ protected void onSubmit(final AjaxRequestTarget target) {
protected TextContentModal createDeleteFailedModal() {
final TextContentModal modal = new TextContentModal("deleteFailedModal",
new ResourceModel("delete_error_message"));
modal.header(new ResourceModel("error"));
final LaddaAjaxButton deleteButton = new LaddaAjaxButton("button", Buttons.Type.Info) {
@Override
protected void onSubmit(final AjaxRequestTarget target) {
Expand All @@ -198,6 +201,30 @@ protected void onEvent(final AjaxRequestTarget target) {
return modal;
}

protected TextContentModal createSaveFailedModal() {
final TextContentModal modal = new TextContentModal("saveFailedModal",
new ResourceModel("optimistic_lock_error_message"));
modal.header(new ResourceModel("error"));
final LaddaAjaxButton okButton = new LaddaAjaxButton("button", Buttons.Type.Info) {
@Override
protected void onSubmit(final AjaxRequestTarget target) {
setResponsePage(listPageClass);
}
};
okButton.setDefaultFormProcessing(false);
okButton.setLabel(Model.of("OK"));
modal.addButton(okButton);

modal.add(new AjaxEventBehavior("hidden.bs.modal") {
@Override
protected void onEvent(final AjaxRequestTarget target) {
setResponsePage(listPageClass);
}
});

return modal;
}

/**
* Traverses all fields and refreshes the ones that are not valid, so that
* we can see the errors
Expand Down Expand Up @@ -277,6 +304,9 @@ public EditForm(final String id) {
deleteFailedModal = createDeleteFailedModal();
add(deleteFailedModal);

saveFailedModal = createSaveFailedModal();
add(saveFailedModal);

// don't display the delete button if we just create a new entity
if (entityId == null) {
deleteButton.setVisibilityAllowed(false);
Expand All @@ -298,7 +328,7 @@ protected void onSubmit(final AjaxRequestTarget target) {
}

/**
* Generic funcionality for the save page button, this can be extended
* Generic functionality for the save page button, this can be extended
* further by subclasses
*
* @author mpostelnicu
Expand All @@ -316,40 +346,44 @@ public SaveEditPageButton(final String id, final IModel<String> model) {

@Override
protected void onSubmit(final AjaxRequestTarget target) {
// save the object and go back to the list page
T saveable = editForm.getModelObject();

// saves the entity and flushes the changes
jpaService.saveAndFlush(saveable);

// clears session and detaches all entities that are currently
// attached
entityManager.clear();

// we flush the mondrian/wicket/reports cache to ensure it gets rebuilt
flushReportingCaches();
try {
// save the object and go back to the list page
T saveable = editForm.getModelObject();

// saves the entity and flushes the changes
jpaService.saveAndFlush(saveable);

// clears session and detaches all entities that are currently
// attached
entityManager.clear();

// we flush the mondrian/wicket/reports cache to ensure it gets rebuilt
flushReportingCaches();

// only redirect if redirect is true
if (redirectToSelf) {
// we need to close the blockUI if it's opened and enable all
// the buttons
target.appendJavaScript("$.unblockUI();");
target.appendJavaScript("$('#" + editForm.getMarkupId() + " button').prop('disabled', false);");
} else if (redirect) {
setResponsePage(getResponsePage(), getParameterPage());
}

// only redirect if redirect is true
if (redirectToSelf) {
// we need to close the blockUI if it's opened and enable all
// the buttons
target.appendJavaScript("$.unblockUI();");
target.appendJavaScript("$('#" + editForm.getMarkupId() + " button').prop('disabled', false);");
} else if (redirect) {
setResponsePage(getResponsePage(), getParameterPage());
// redirect is set back to true, which is the default behavior
redirect = true;
redirectToSelf = false;
} catch (ObjectOptimisticLockingFailureException e) {
saveFailedModal.show(target);
}

// redirect is set back to true, which is the default behavior
redirect = true;
redirectToSelf = false;
}

/**
* by default, submit button returns back to listPage
*
* @return
*/
protected Class<? extends BasePage> getResponsePage() {
protected Class<? extends Page> getResponsePage() {
return listPageClass;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,6 @@ deleteButton=Delete
saveButton=Save
saveDraft=Save Draft
formHasErrors=The form has errors or is missing information and cannot be submitted. Please scroll up, fix all the errors (highlighted in red) and re-submit!
error=Error
delete_error_message=Cannot delete entity, it is in use by the system
optimistic_lock_error_message=Changes were not saved. This object was modified by someone else.
Original file line number Diff line number Diff line change
Expand Up @@ -205,8 +205,6 @@ public SaveEditPageButton getSaveEditPageButton() {

@Override
protected void onSubmit(final AjaxRequestTarget target) {
super.onSubmit(target);

final Person person = editForm.getModelObject();
// encode the password
if (person.getChangeProfilePassword()) {
Expand All @@ -218,8 +216,12 @@ protected void onSubmit(final AjaxRequestTarget target) {
person.setChangePasswordNextSignIn(false);
}

jpaService.save(person);
setResponsePage(EditUserPage.this.getResponsePage());
super.onSubmit(target);
}

@Override
protected Class<? extends Page> getResponsePage() {
return EditUserPage.this.getResponsePage();
}
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,21 @@
import nl.dries.wicket.hibernate.dozer.proxy.Proxied;
import org.springframework.data.jpa.domain.AbstractPersistable;

import javax.persistence.Column;
import javax.persistence.MappedSuperclass;
import javax.persistence.Version;

/**
* @author mpostelnicu
*
*/
@MappedSuperclass
public class GenericPersistable extends AbstractPersistable<Long> implements Serializable {

@Version
@Column(name = "optlock")
private Integer version;

/**
* Custom serialization for id is needed since Spring Data JPA 2.x AbstractPersistable no longer implements
* Serializable.
Expand All @@ -48,4 +57,8 @@ private void readObject(final ObjectInputStream in) throws IOException, ClassNot

in.defaultReadObject();
}

public Integer getVersion() {
return version;
}
}
42 changes: 42 additions & 0 deletions persistence/src/main/resources/liquibase-changelog.xml
Original file line number Diff line number Diff line change
Expand Up @@ -60,4 +60,46 @@
<sqlFile path="Person_roles.sql" />
</changeSet>

<changeSet id="#325-optlock" author="mpostelnicu">
<preConditions onFail="CONTINUE">
<columnExists tableName="ADMIN_SETTINGS" columnName="optlock"/>
<columnExists tableName="CATEGORY" columnName="optlock"/>
<columnExists tableName="FILE_CONTENT" columnName="optlock"/>
<columnExists tableName="FILE_METADATA" columnName="optlock"/>
<columnExists tableName="PERSON" columnName="optlock"/>
<columnExists tableName="ROLE" columnName="optlock"/>
<columnExists tableName="STATUS_CHANGED_COMMENT" columnName="optlock"/>
<columnExists tableName="TEST_FORM" columnName="optlock"/>
<columnExists tableName="TEST_FORM_CHILD" columnName="optlock"/>
</preConditions>

<update tableName="ADMIN_SETTINGS">
<column name="optlock" valueNumeric="0"/>
</update>
<update tableName="CATEGORY">
<column name="optlock" valueNumeric="0"/>
</update>
<update tableName="FILE_CONTENT">
<column name="optlock" valueNumeric="0"/>
</update>
<update tableName="FILE_METADATA">
<column name="optlock" valueNumeric="0"/>
</update>
<update tableName="PERSON">
<column name="optlock" valueNumeric="0"/>
</update>
<update tableName="ROLE">
<column name="optlock" valueNumeric="0"/>
</update>
<update tableName="STATUS_CHANGED_COMMENT">
<column name="optlock" valueNumeric="0"/>
</update>
<update tableName="TEST_FORM">
<column name="optlock" valueNumeric="0"/>
</update>
<update tableName="TEST_FORM_CHILD">
<column name="optlock" valueNumeric="0"/>
</update>
</changeSet>

</databaseChangeLog>

0 comments on commit 0f3953b

Please sign in to comment.