SEBSERV-162 audit logs and deletion

This commit is contained in:
anhefti 2021-09-14 11:52:56 +02:00
parent f794ab5e7d
commit a589fd8ad4
16 changed files with 248 additions and 16 deletions

View file

@ -18,6 +18,7 @@ import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import ch.ethz.seb.sebserver.gbl.api.API.BulkActionType;
import ch.ethz.seb.sebserver.gbl.api.APIMessage;
import ch.ethz.seb.sebserver.gbl.util.Utils;
@ -31,6 +32,7 @@ public class EntityProcessingReport {
public static final String ATTR_SOURCE = "source";
public static final String ATTR_RESULTS = "results";
public static final String ATTR_ERRORS = "errors";
public static final String ATTR_TYPE = "bulkActionType";
/** A set of entity-keys that are or were processed by a bulk action- or other process with a EntityProcessingReport
* result. */
@ -43,16 +45,20 @@ public class EntityProcessingReport {
/** A set of error entries that defines an error if happened. */
@JsonProperty(value = ATTR_ERRORS, required = true)
public final Set<ErrorEntry> errors;
@JsonProperty(value = ATTR_TYPE, required = true)
public final BulkActionType bulkActionType;
@JsonCreator
public EntityProcessingReport(
@JsonProperty(value = ATTR_SOURCE, required = true) final Collection<EntityKey> source,
@JsonProperty(value = ATTR_RESULTS, required = true) final Collection<EntityKey> results,
@JsonProperty(value = ATTR_ERRORS, required = true) final Collection<ErrorEntry> errors) {
@JsonProperty(value = ATTR_ERRORS, required = true) final Collection<ErrorEntry> errors,
@JsonProperty(value = ATTR_TYPE, required = true) final BulkActionType bulkActionType) {
this.source = Utils.immutableSetOf(source);
this.results = Utils.immutableSetOf(results);
this.errors = Utils.immutableSetOf(errors);
this.bulkActionType = bulkActionType;
}
@JsonIgnore
@ -119,7 +125,8 @@ public class EntityProcessingReport {
return new EntityProcessingReport(
Collections.emptyList(),
Collections.emptyList(),
Arrays.asList(new ErrorEntry(null, APIMessage.ErrorMessage.RESOURCE_NOT_FOUND.of())));
Arrays.asList(new ErrorEntry(null, APIMessage.ErrorMessage.RESOURCE_NOT_FOUND.of())),
null);
}
}

View file

@ -138,6 +138,31 @@ public class IndicatorTemplate implements Entity {
return this.thresholds;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((this.id == null) ? 0 : this.id.hashCode());
return result;
}
@Override
public boolean equals(final Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
final IndicatorTemplate other = (IndicatorTemplate) obj;
if (this.id == null) {
if (other.id != null)
return false;
} else if (!this.id.equals(other.id))
return false;
return true;
}
@Override
public String toString() {
final StringBuilder builder = new StringBuilder();

View file

@ -412,6 +412,11 @@ public enum ActionDefinition {
ImageIcon.CANCEL,
PageStateDefinitionImpl.EXAM_TEMPLATE_VIEW,
ActionCategory.FORM),
EXAM_TEMPLATE_DELETE(
new LocTextKey("sebserver.examtemplate.form.action.delete"),
ImageIcon.DELETE,
PageStateDefinitionImpl.EXAM_TEMPLATE_LIST,
ActionCategory.FORM),
INDICATOR_TEMPLATE_NEW(
new LocTextKey("sebserver.examtemplate.indicator.action.list.new"),

View file

@ -12,6 +12,8 @@ import java.util.List;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.swt.widgets.Composite;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
@ -36,6 +38,7 @@ import ch.ethz.seb.sebserver.gui.service.page.PageService.PageActionBuilder;
import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer;
import ch.ethz.seb.sebserver.gui.service.page.impl.PageAction;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.DeleteExamTemplate;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.DeleteIndicatorTemplate;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetExamTemplate;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetIndicatorTemplatePage;
@ -52,6 +55,8 @@ import ch.ethz.seb.sebserver.gui.widget.WidgetFactory;
@GuiProfile
public class ExamTemplateForm implements TemplateComposer {
private static final Logger log = LoggerFactory.getLogger(ExamTemplateForm.class);
public static final LocTextKey TITLE_TEXT_KEY =
new LocTextKey("sebserver.examtemplate.form.title");
public static final LocTextKey TITLE_NEW_TEXT_KEY =
@ -85,6 +90,9 @@ public class ExamTemplateForm implements TemplateComposer {
private static final LocTextKey INDICATOR_EMPTY_LIST_MESSAGE =
new LocTextKey("sebserver.examtemplate.indicator.list.empty");
private static final LocTextKey EXAM_TEMPLATE_DELETE_CONFIRM =
new LocTextKey("sebserver.examtemplate.form.action.delete.confirm");
private final PageService pageService;
private final ResourceService resourceService;
private final WidgetFactory widgetFactory;
@ -200,6 +208,12 @@ public class ExamTemplateForm implements TemplateComposer {
.withExec(this.pageService.backToCurrentFunction())
.publishIf(() -> !readonly)
.newAction(ActionDefinition.EXAM_TEMPLATE_DELETE)
.withEntityKey(entityKey)
.withConfirm(() -> EXAM_TEMPLATE_DELETE_CONFIRM)
.withExec(this::deleteExamTemplate)
.publishIf(() -> userGrant.iw() && readonly)
;
if (readonly) {
@ -277,6 +291,14 @@ public class ExamTemplateForm implements TemplateComposer {
}
}
private PageAction deleteExamTemplate(final PageAction action) {
this.pageService.getRestService().getBuilder(DeleteExamTemplate.class)
.withURIVariable(API.PARAM_MODEL_ID, action.getEntityKey().modelId)
.call()
.onError(error -> action.pageContext().notifyUnexpectedError(error));
return action;
}
private PageAction deleteSelectedIndicator(final PageAction action) {
final EntityKey entityKey = action.getEntityKey();
final EntityKey indicatorKey = action.getSingleSelection();

View file

@ -0,0 +1,40 @@
/*
* Copyright (c) 2021 ETH Zürich, Educational Development and Technology (LET)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam;
import org.springframework.context.annotation.Lazy;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import com.fasterxml.jackson.core.type.TypeReference;
import ch.ethz.seb.sebserver.gbl.api.API;
import ch.ethz.seb.sebserver.gbl.api.EntityType;
import ch.ethz.seb.sebserver.gbl.model.EntityProcessingReport;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall;
@Lazy
@Component
@GuiProfile
public class DeleteExamTemplate extends RestCall<EntityProcessingReport> {
public DeleteExamTemplate() {
super(new TypeKey<>(
CallType.DELETE,
EntityType.EXAM_TEMPLATE,
new TypeReference<EntityProcessingReport>() {
}),
HttpMethod.DELETE,
MediaType.APPLICATION_FORM_URLENCODED,
API.EXAM_TEMPLATE_ENDPOINT + API.MODEL_ID_VAR_PATH_SEGMENT);
}
}

View file

@ -153,7 +153,8 @@ public class BulkActionServiceImpl implements BulkActionService {
action.result.stream()
.filter(Result::hasError)
.map(this::createErrorEntry)
.collect(Collectors.toList()));
.collect(Collectors.toList()),
action.type);
});
}

View file

@ -10,9 +10,10 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.dao;
import ch.ethz.seb.sebserver.gbl.model.exam.ExamTemplate;
import ch.ethz.seb.sebserver.gbl.util.Result;
import ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.BulkActionSupportDAO;
/** Concrete EntityDAO interface of ExamTemplate entities */
public interface ExamTemplateDAO extends EntityDAO<ExamTemplate, ExamTemplate> {
public interface ExamTemplateDAO extends EntityDAO<ExamTemplate, ExamTemplate>, BulkActionSupportDAO<ExamTemplate> {
/** Used to get the ExamTemplate that is set as default for a given institution.
*

View file

@ -10,6 +10,7 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.dao;
import ch.ethz.seb.sebserver.gbl.api.EntityType;
import ch.ethz.seb.sebserver.gbl.model.Entity;
import ch.ethz.seb.sebserver.gbl.model.EntityProcessingReport;
import ch.ethz.seb.sebserver.gbl.model.user.UserAccount;
import ch.ethz.seb.sebserver.gbl.model.user.UserActivityLog;
import ch.ethz.seb.sebserver.gbl.model.user.UserInfo;
@ -76,6 +77,19 @@ public interface UserActivityLogDAO extends
* @return Result of the Entity or referring to an Error if happened */
<E extends Entity> Result<E> logModify(E entity);
/** Create a user activity log entry for the current user of activity type DELETE
*
* @param entity the Entity
* @return Result of the Entity or referring to an Error if happened */
<E extends Entity> Result<E> logDelete(E entity);
/** Used to log a successful bulk action and uses the EntityProcessingReport from the
* bulk action to log all details.
*
* @param bulkActionReport the bulk action report containing all processed entity keys for logging
* @return Result refer to the given EntityProcessingReport or to an error when happened */
Result<EntityProcessingReport> logBulkAction(EntityProcessingReport bulkActionReport);
/** Creates a user activity log entry for the current user.
*
* @param activityType the activity type

View file

@ -32,6 +32,7 @@ import ch.ethz.seb.sebserver.gbl.Constants;
import ch.ethz.seb.sebserver.gbl.api.APIMessage.FieldValidationException;
import ch.ethz.seb.sebserver.gbl.api.EntityType;
import ch.ethz.seb.sebserver.gbl.api.JSONMapper;
import ch.ethz.seb.sebserver.gbl.model.EntityDependency;
import ch.ethz.seb.sebserver.gbl.model.EntityKey;
import ch.ethz.seb.sebserver.gbl.model.exam.Exam.ExamType;
import ch.ethz.seb.sebserver.gbl.model.exam.ExamTemplate;
@ -42,6 +43,7 @@ import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ExamTemplateRecor
import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ExamTemplateRecordMapper;
import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.IndicatorRecordDynamicSqlSupport;
import ch.ethz.seb.sebserver.webservice.datalayer.batis.model.ExamTemplateRecord;
import ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.impl.BulkAction;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.AdditionalAttributesDAO;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.DAOLoggingSupport;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ExamDAO;
@ -232,6 +234,11 @@ public class ExamTemplateDAOImpl implements ExamTemplateDAO {
.onError(TransactionHandler::rollback);
}
@Override
public Set<EntityDependency> getDependencies(final BulkAction bulkAction) {
return Collections.emptySet();
}
@Override
@Transactional
public Result<Collection<EntityKey>> delete(final Set<EntityKey> all) {

View file

@ -38,6 +38,7 @@ import ch.ethz.seb.sebserver.gbl.api.EntityType;
import ch.ethz.seb.sebserver.gbl.api.JSONMapper;
import ch.ethz.seb.sebserver.gbl.model.Entity;
import ch.ethz.seb.sebserver.gbl.model.EntityKey;
import ch.ethz.seb.sebserver.gbl.model.EntityProcessingReport;
import ch.ethz.seb.sebserver.gbl.model.user.UserAccount;
import ch.ethz.seb.sebserver.gbl.model.user.UserActivityLog;
import ch.ethz.seb.sebserver.gbl.model.user.UserInfo;
@ -87,11 +88,13 @@ public class UserActivityLogDAOImpl implements UserActivityLogDAO {
}
@Override
@Transactional
public Result<UserInfo> logLogin(final UserInfo user) {
return log(UserLogActivityType.LOGIN, user);
}
@Override
@Transactional
public Result<UserInfo> logLogout(final UserInfo user) {
return log(UserLogActivityType.LOGOUT, user);
}
@ -156,6 +159,50 @@ public class UserActivityLogDAOImpl implements UserActivityLogDAO {
return log(UserLogActivityType.MODIFY, entity);
}
@Override
@Transactional
public <E extends Entity> Result<E> logDelete(final E entity) {
return log(UserLogActivityType.DELETE, entity);
}
@Override
@Transactional
public Result<EntityProcessingReport> logBulkAction(final EntityProcessingReport bulkActionReport) {
try {
UserLogActivityType activityType = null;
switch (bulkActionReport.bulkActionType) {
case ACTIVATE:
activityType = UserLogActivityType.ACTIVATE;
break;
case DEACTIVATE:
activityType = UserLogActivityType.DEACTIVATE;
break;
case HARD_DELETE:
activityType = UserLogActivityType.DELETE;
break;
}
final String message = bulkActionReport.results
.stream()
.map(EntityKey::toString)
.collect(Collectors.joining(Constants.LIST_SEPARATOR));
final UserLogActivityType _activityType = activityType;
bulkActionReport.source.stream()
.forEach(entityKey -> log(
_activityType,
entityKey.entityType,
entityKey.modelId,
message));
return Result.of(bulkActionReport);
} catch (final Exception e) {
log.error("Failed to do audit log for bulk action: ", e);
return Result.of(bulkActionReport);
}
}
@Override
@Transactional
public <E extends Entity> Result<E> log(
@ -200,6 +247,7 @@ public class UserActivityLogDAOImpl implements UserActivityLogDAO {
}
@Override
@Transactional
public <T> Result<T> log(
final UserLogActivityType activityType,
final EntityType entityType,

View file

@ -150,12 +150,14 @@ public class ClientEventController extends ReadonlyEntityController<ClientEvent,
return new EntityProcessingReport(
Collections.emptyList(),
Collections.emptyList(),
Arrays.asList(new ErrorEntry(null, APIMessage.ErrorMessage.UNEXPECTED.of(delete.getError()))));
Arrays.asList(new ErrorEntry(null, APIMessage.ErrorMessage.UNEXPECTED.of(delete.getError()))),
BulkActionType.HARD_DELETE);
} else {
return new EntityProcessingReport(
sources,
delete.get(),
Collections.emptyList());
Collections.emptyList(),
BulkActionType.HARD_DELETE);
}
}

View file

@ -20,6 +20,8 @@ import javax.validation.Valid;
import org.apache.commons.lang3.StringUtils;
import org.mybatis.dynamic.sql.SqlTable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.MediaType;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.WebDataBinder;
@ -62,6 +64,8 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.validation.BeanValidationSe
* @param <M> The concrete Entity domain-model type used for POST methods (new) */
public abstract class EntityController<T extends Entity, M extends Entity> {
private static final Logger log = LoggerFactory.getLogger(EntityController.class);
protected final AuthorizationService authorization;
protected final BulkActionService bulkActionService;
protected final EntityDAO<T, M> entityDAO;
@ -341,6 +345,7 @@ public abstract class EntityController<T extends Entity, M extends Entity> {
return this.entityDAO.byModelId(modelId)
.flatMap(this::checkWriteAccess)
.flatMap(this::validForDelete)
.flatMap(this::logDelete)
.flatMap(entity -> bulkDelete(entity, convertToEntityType(addIncludes, includes)))
.flatMap(this::notifyDeleted)
.flatMap(pair -> this.logBulkAction(pair.b))
@ -371,7 +376,9 @@ public abstract class EntityController<T extends Entity, M extends Entity> {
}
final EntityType entityType = this.entityDAO.entityType();
final Collection<EntityKey> sources = ids.stream()
final Collection<EntityKey> sources = ids
.stream()
.map(this::logDelete)
.map(id -> new EntityKey(id, entityType))
.collect(Collectors.toList());
@ -383,6 +390,7 @@ public abstract class EntityController<T extends Entity, M extends Entity> {
return this.bulkActionService
.createReport(bulkAction)
.flatMap(this::logBulkAction)
.flatMap(this::notifyAllDeleted)
.getOrThrow();
}
@ -589,12 +597,42 @@ public abstract class EntityController<T extends Entity, M extends Entity> {
return this.userActivityLogDAO.logModify(entity);
}
/** Makes a DELETE user activity log for the specified entity.
* This may be overwritten if the create user activity log should be skipped.
*
* @param entity the Entity instance
* @return Result refer to the logged Entity instance or to an error if happened */
protected String logDelete(final String modelId) {
try {
return this.entityDAO
.byModelId(modelId)
.flatMap(this::logDelete)
.map(Entity::getModelId)
.getOrThrow();
} catch (final Exception e) {
log.warn("Failed to log delete for entity id: {}", modelId, e);
return modelId;
}
}
/** Makes a DELETE user activity log for the specified entity.
* This may be overwritten if the create user activity log should be skipped.
*
* @param entity the Entity instance
* @return Result refer to the logged Entity instance or to an error if happened */
protected Result<T> logDelete(final T entity) {
return this.userActivityLogDAO.logDelete(entity);
}
/** Makes user activity log for a bulk action.
*
* @param bulkActionReport the EntityProcessingReport
* @return Result of entity */
protected Result<EntityProcessingReport> logBulkAction(final EntityProcessingReport bulkActionReport) {
// TODO
this.userActivityLogDAO.logBulkAction(bulkActionReport)
.onError(error -> log.warn("Failed to log audit for bulk action: {}", bulkActionReport, error));
return Result.of(bulkActionReport);
}

View file

@ -190,6 +190,9 @@ public class ExamTemplateController extends EntityController<ExamTemplate, ExamT
.save(newExamTemplate)
.getOrThrow();
this.userActivityLogDAO.logCreate(newIndicator)
.onError(error -> log.error("Failed to log indicator template creation: {}", newIndicator, error));
return newIndicator;
}
@ -235,6 +238,9 @@ public class ExamTemplateController extends EntityController<ExamTemplate, ExamT
.save(newExamTemplate)
.getOrThrow();
this.userActivityLogDAO.logModify(modifyData)
.onError(error -> log.error("Failed to log indicator template modification: {}", modifyData, error));
return modifyData;
}
@ -259,10 +265,14 @@ public class ExamTemplateController extends EntityController<ExamTemplate, ExamT
.byModelId(parentModelId)
.getOrThrow();
final List<IndicatorTemplate> newIndicators = examTemplate.indicatorTemplates
final IndicatorTemplate toDelete = examTemplate.indicatorTemplates
.stream()
.filter(i -> !modelId.equals(i.getModelId()))
.collect(Collectors.toList());
.filter(i -> modelId.equals(i.getModelId()))
.findFirst()
.orElse(null);
final List<IndicatorTemplate> newIndicators = new ArrayList<>(examTemplate.indicatorTemplates);
newIndicators.remove(toDelete);
final ExamTemplate newExamTemplate = new ExamTemplate(
examTemplate.id,
@ -275,6 +285,9 @@ public class ExamTemplateController extends EntityController<ExamTemplate, ExamT
.save(newExamTemplate)
.getOrThrow();
this.userActivityLogDAO.logDelete(toDelete)
.onError(error -> log.error("Failed to log indicator template modification: {}", toDelete, error));
return new EntityKey(modelId, EntityType.INDICATOR);
}

View file

@ -23,6 +23,7 @@ import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import ch.ethz.seb.sebserver.gbl.api.API;
import ch.ethz.seb.sebserver.gbl.api.API.BulkActionType;
import ch.ethz.seb.sebserver.gbl.api.APIMessage;
import ch.ethz.seb.sebserver.gbl.api.EntityType;
import ch.ethz.seb.sebserver.gbl.api.authorization.PrivilegeType;
@ -104,12 +105,14 @@ public class UserActivityLogController extends ReadonlyEntityController<UserActi
return new EntityProcessingReport(
Collections.emptyList(),
Collections.emptyList(),
Arrays.asList(new ErrorEntry(null, APIMessage.ErrorMessage.UNEXPECTED.of(delete.getError()))));
Arrays.asList(new ErrorEntry(null, APIMessage.ErrorMessage.UNEXPECTED.of(delete.getError()))),
BulkActionType.HARD_DELETE);
} else {
return new EntityProcessingReport(
sources,
delete.get(),
Collections.emptyList());
Collections.emptyList(),
BulkActionType.HARD_DELETE);
}
}

View file

@ -64,6 +64,8 @@ sebserver.overall.types.entityType.LMS_SETUP=LMS Setup
sebserver.overall.types.entityType.USER=User Account
sebserver.overall.types.entityType.CLIENT_INSTRUCTION=SEB Client Instruction
sebserver.overall.types.entityType.EXAM_SEB_RESTRICTION=SEB Exam Restriction
sebserver.overall.types.entityType.CERTIFICATE=Certificate
sebserver.overall.types.entityType.EXAM_TEMPLATE=Exam Template
sebserver.overall.activity.title.serveradmin=SEB Server Administration
sebserver.overall.activity.title.sebconfig=Configurations
@ -1636,8 +1638,10 @@ sebserver.examtemplate.form.examConfigTemplate.tooltip=The linked ecam configura
sebserver.examtemplate.form.supporter=Exam Supporter
sebserver.examtemplate.form.supporter.tooltip=List of all exam supporter user that will automatically be applied to an exam imported with this template.
sebserver.examtemplate.form.action.save=Save
sebserver.examtemplate.form.action.edit=Edit Template Settings
sebserver.examtemplate.form.action.save=Save Exam Template
sebserver.examtemplate.form.action.edit=Edit Exam Template
sebserver.examtemplate.form.action.delete=Delete Exam Template
sebserver.examtemplate.form.action.delete.confirm=Are you sure to delete this exam template?<br/><br/>Please note that a reference from a exam that uses this template will also be deleted but the exam itself is not affected.
sebserver.examtemplate.indicator.list.actions=
sebserver.examtemplate.indicator.list.title=Indicator Templates

View file

@ -20,6 +20,7 @@ import org.junit.Test;
import com.fasterxml.jackson.databind.ObjectWriter;
import ch.ethz.seb.sebserver.gbl.api.API.BulkActionType;
import ch.ethz.seb.sebserver.gbl.api.APIMessage;
import ch.ethz.seb.sebserver.gbl.api.EntityType;
import ch.ethz.seb.sebserver.gbl.api.JSONMapper;
@ -298,7 +299,8 @@ public class ModelObjectJSONGenerator {
Arrays.asList(new EntityKey(1L, EntityType.EXAM)),
Arrays.asList(new EntityKey(1L, EntityType.INDICATOR), new EntityKey(2L, EntityType.INDICATOR)),
Arrays.asList(new EntityProcessingReport.ErrorEntry(new EntityKey(2L, EntityType.INDICATOR),
APIMessage.ErrorMessage.UNEXPECTED.of())));
APIMessage.ErrorMessage.UNEXPECTED.of())),
BulkActionType.HARD_DELETE);
System.out.println(domainObject.getClass().getSimpleName() + ":");
System.out.println(writerWithDefaultPrettyPrinter.writeValueAsString(domainObject));