001// -------------------------------------------------------------------------------- 002// Copyright 2002-2025 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.util.common.command.BaseEditResult; 020import com.echothree.util.common.command.BaseResult; 021import com.echothree.util.common.command.EditMode; 022import com.echothree.util.common.form.BaseEdit; 023import com.echothree.util.common.form.BaseSpec; 024import com.echothree.util.common.message.ExecutionErrors; 025import com.echothree.util.common.validation.FieldDefinition; 026import com.echothree.util.server.persistence.BaseEntity; 027import com.echothree.util.server.persistence.EntityPermission; 028import java.util.List; 029 030public abstract class BaseAbstractEditCommand<S extends BaseSpec, E extends BaseEdit, R extends BaseEditResult<E>, BE extends BaseEntity, LE extends BaseEntity> 031 extends BaseEditCommand<S, E> { 032 033 protected BaseAbstractEditCommand(CommandSecurityDefinition commandSecurityDefinition, List<FieldDefinition> specFieldDefinitions, 034 List<FieldDefinition> editFieldDefinitions) { 035 super(commandSecurityDefinition, specFieldDefinitions, editFieldDefinitions); 036 } 037 038 protected abstract R getResult(); 039 040 protected abstract E getEdit(); 041 042 protected EntityPermission editModeToEntityPermission(EditMode editMode) { 043 EntityPermission entityPermission; 044 045 if(editMode == EditMode.UPDATE) { 046 entityPermission = EntityPermission.READ_WRITE; 047 } else { 048 // EditMode.LOCK & EditMode.ABANDON 049 entityPermission = EntityPermission.READ_ONLY; 050 } 051 052 return entityPermission; 053 } 054 055 protected abstract BE getEntity(R result); 056 057 protected abstract LE getLockEntity(BE baseEntity); 058 059 protected abstract void fillInResult(R result, BE baseEntity); 060 061 private void fillInResult(R result, BE baseEntity, LE lockEntity) { 062 fillInResult(result, baseEntity); 063 064 if(editMode.equals(EditMode.LOCK) || editMode.equals(EditMode.UPDATE)) { 065 result.setEntityLock(getEntityLockTransfer(lockEntity)); 066 } 067 } 068 069 protected abstract void doLock(E edit, BE baseEntity); 070 071 // This check is called for both EditMode.LOCK and EditMode.UPDATE. 072 protected void canEdit(BE baseEntity) { 073 // Nothing. 074 } 075 076 // This check is called for EditMode.UPDATE, after canEdit(...) is called. 077 protected void canUpdate(BE baseEntity) { 078 // Nothing. 079 } 080 081 protected abstract void doUpdate(BE baseEntity); 082 083 @Override 084 protected BaseResult execute() { 085 var result = getResult(); 086 var baseEntity = getEntity(result); 087 088 // getEntity(...) may set both SecurityMessages and ExecutionErrors. 089 if(!hasSecurityMessages() && !hasExecutionErrors()) { 090 var lockEntity = getLockEntity(baseEntity); 091 092 switch(editMode) { 093 case LOCK -> { 094 canEdit(baseEntity); 095 096 // canEdit(...) may set both SecurityMessages and ExecutionErrors. 097 if(!hasSecurityMessages()) { 098 if(!hasExecutionErrors()) { 099 if(lockEntity(lockEntity)) { 100 edit = getEdit(); 101 result.setEdit(edit); 102 doLock(edit, baseEntity); 103 } else { 104 addExecutionError(ExecutionErrors.EntityLockFailed.name()); 105 } 106 } 107 108 fillInResult(result, baseEntity, lockEntity); 109 } 110 } 111 case ABANDON -> unlockEntity(lockEntity); 112 case UPDATE -> { 113 canEdit(baseEntity); 114 115 // canEdit(...) may set both SecurityMessages and ExecutionErrors. 116 if(!hasSecurityMessages()) { 117 if(!hasExecutionErrors()) { 118 canUpdate(baseEntity); 119 120 if(!hasExecutionErrors()) { 121 if(lockEntityForUpdate(lockEntity)) { 122 try { 123 doUpdate(baseEntity); 124 } finally { 125 unlockEntity(lockEntity); 126 } 127 } else { 128 addExecutionError(ExecutionErrors.EntityLockStale.name()); 129 } 130 } 131 } 132 133 if(hasExecutionErrors()) { 134 fillInResult(result, baseEntity, lockEntity); 135 } 136 } 137 } 138 } 139 } 140 141 return result; 142 } 143 144 R errorResult = null; 145 146 @Override 147 protected void saveResultAfterEditValidatorErrors() { 148 errorResult = getResult(); 149 150 var baseEntity = getEntity(errorResult); 151 var lockEntity = getLockEntity(baseEntity); 152 153 fillInResult(errorResult, baseEntity, lockEntity); 154 } 155 156 @Override 157 protected BaseResult getBaseResultAfterErrors() { 158 return errorResult; 159 } 160 161}