001// -------------------------------------------------------------------------------- 002// Copyright 2002-2024 Echo Three, LLC 003// 004// Licensed under the Apache License, Version 2.0 (the "License"); 005// you may not use this file except in compliance with the License. 006// You may obtain a copy of the License at 007// 008// http://www.apache.org/licenses/LICENSE-2.0 009// 010// Unless required by applicable law or agreed to in writing, software 011// distributed under the License is distributed on an "AS IS" BASIS, 012// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013// See the License for the specific language governing permissions and 014// limitations under the License. 015// -------------------------------------------------------------------------------- 016 017package com.echothree.util.server.control; 018 019import com.echothree.model.data.user.common.pk.UserVisitPK; 020import com.echothree.util.common.command.BaseEditResult; 021import com.echothree.util.common.command.BaseResult; 022import com.echothree.util.common.command.EditMode; 023import com.echothree.util.common.form.BaseEdit; 024import com.echothree.util.common.form.BaseEditForm; 025import com.echothree.util.common.form.BaseSpec; 026import com.echothree.util.common.message.ExecutionErrors; 027import com.echothree.util.common.validation.FieldDefinition; 028import com.echothree.util.server.persistence.BaseEntity; 029import com.echothree.util.server.persistence.EntityPermission; 030import java.util.List; 031 032public abstract class BaseAbstractEditCommand<S extends BaseSpec, E extends BaseEdit, R extends BaseEditResult<E>, BE extends BaseEntity, LE extends BaseEntity> 033 extends BaseEditCommand<S, E> { 034 035 protected BaseAbstractEditCommand(UserVisitPK userVisitPK, BaseEditForm<S, E> editForm, CommandSecurityDefinition commandSecurityDefinition, List<FieldDefinition> specFieldDefinitions, 036 List<FieldDefinition> editFieldDefinitions) { 037 super(userVisitPK, editForm, commandSecurityDefinition, specFieldDefinitions, editFieldDefinitions); 038 } 039 040 protected abstract R getResult(); 041 042 protected abstract E getEdit(); 043 044 protected EntityPermission editModeToEntityPermission(EditMode editMode) { 045 EntityPermission entityPermission; 046 047 if(editMode == EditMode.UPDATE) { 048 entityPermission = EntityPermission.READ_WRITE; 049 } else { 050 // EditMode.LOCK & EditMode.ABANDON 051 entityPermission = EntityPermission.READ_ONLY; 052 } 053 054 return entityPermission; 055 } 056 057 protected abstract BE getEntity(R result); 058 059 protected abstract LE getLockEntity(BE baseEntity); 060 061 protected abstract void fillInResult(R result, BE baseEntity); 062 063 private void fillInResult(R result, BE baseEntity, LE lockEntity) { 064 fillInResult(result, baseEntity); 065 066 if(editMode.equals(EditMode.LOCK) || editMode.equals(EditMode.UPDATE)) { 067 result.setEntityLock(getEntityLockTransfer(lockEntity)); 068 } 069 } 070 071 protected abstract void doLock(E edit, BE baseEntity); 072 073 // This check is called for both EditMode.LOCK and EditMode.UPDATE. 074 protected void canEdit(BE baseEntity) { 075 // Nothing. 076 } 077 078 // This check is called for EditMode.UPDATE, after canEdit(...) is called. 079 protected void canUpdate(BE baseEntity) { 080 // Nothing. 081 } 082 083 protected abstract void doUpdate(BE baseEntity); 084 085 @Override 086 protected final BaseResult execute() { 087 R result = getResult(); 088 BE baseEntity = getEntity(result); 089 090 // getEntity(...) may set both SecurityMessages and ExecutionErrors. 091 if(!hasSecurityMessages() && !hasExecutionErrors()) { 092 LE lockEntity = getLockEntity(baseEntity); 093 094 switch(editMode) { 095 case LOCK -> { 096 canEdit(baseEntity); 097 098 // canEdit(...) may set both SecurityMessages and ExecutionErrors. 099 if(!hasSecurityMessages()) { 100 if(!hasExecutionErrors()) { 101 if(lockEntity(lockEntity)) { 102 edit = getEdit(); 103 result.setEdit(edit); 104 doLock(edit, baseEntity); 105 } else { 106 addExecutionError(ExecutionErrors.EntityLockFailed.name()); 107 } 108 } 109 110 fillInResult(result, baseEntity, lockEntity); 111 } 112 } 113 case ABANDON -> unlockEntity(lockEntity); 114 case UPDATE -> { 115 canEdit(baseEntity); 116 117 // canEdit(...) may set both SecurityMessages and ExecutionErrors. 118 if(!hasSecurityMessages()) { 119 if(!hasExecutionErrors()) { 120 canUpdate(baseEntity); 121 122 if(!hasExecutionErrors()) { 123 if(lockEntityForUpdate(lockEntity)) { 124 try { 125 doUpdate(baseEntity); 126 } finally { 127 unlockEntity(lockEntity); 128 } 129 } else { 130 addExecutionError(ExecutionErrors.EntityLockStale.name()); 131 } 132 } 133 } 134 135 if(hasExecutionErrors()) { 136 fillInResult(result, baseEntity, lockEntity); 137 } 138 } 139 } 140 } 141 } 142 143 return result; 144 } 145 146 R errorResult = null; 147 148 @Override 149 protected void saveResultAfterEditValidatorErrors() { 150 errorResult = getResult(); 151 152 BE baseEntity = getEntity(errorResult); 153 LE lockEntity = getLockEntity(baseEntity); 154 155 fillInResult(errorResult, baseEntity, lockEntity); 156 } 157 158 @Override 159 protected BaseResult getBaseResultAfterErrors() { 160 return errorResult; 161 } 162 163}