gov.anl.aps.cdb.portal.controllers.CdbEntityController.java Source code

Java tutorial

Introduction

Here is the source code for gov.anl.aps.cdb.portal.controllers.CdbEntityController.java

Source

/*
 * Copyright (c) UChicago Argonne, LLC. All rights reserved.
 * See LICENSE file.
 */
package gov.anl.aps.cdb.portal.controllers;

import gov.anl.aps.cdb.common.constants.CdbProperty;
import gov.anl.aps.cdb.common.constants.CdbRole;
import gov.anl.aps.cdb.common.exceptions.AuthorizationError;
import gov.anl.aps.cdb.common.exceptions.CdbException;
import gov.anl.aps.cdb.common.exceptions.InvalidRequest;
import gov.anl.aps.cdb.portal.model.db.beans.CdbEntityFacade;
import gov.anl.aps.cdb.portal.model.db.beans.LogTopicFacade;
import gov.anl.aps.cdb.portal.model.db.entities.CdbEntity;
import gov.anl.aps.cdb.portal.model.db.entities.EntityInfo;
import gov.anl.aps.cdb.portal.model.db.entities.Log;
import gov.anl.aps.cdb.portal.model.db.entities.LogTopic;
import gov.anl.aps.cdb.portal.model.db.entities.UserInfo;
import gov.anl.aps.cdb.portal.model.db.utilities.LogUtility;
import gov.anl.aps.cdb.portal.utilities.AuthorizationUtility;
import gov.anl.aps.cdb.common.utilities.CollectionUtility;
import gov.anl.aps.cdb.portal.utilities.SearchResult;
import gov.anl.aps.cdb.portal.utilities.SessionUtility;
import gov.anl.aps.cdb.common.utilities.StringUtility;
import gov.anl.aps.cdb.portal.constants.PortalStyles;
import gov.anl.aps.cdb.portal.controllers.settings.ICdbSettings;
import gov.anl.aps.cdb.portal.model.db.beans.SettingTypeFacade;
import gov.anl.aps.cdb.portal.model.db.entities.Item;
import gov.anl.aps.cdb.portal.model.db.entities.SettingType;
import gov.anl.aps.cdb.portal.utilities.ConfigurationUtility;
import java.io.IOException;

import java.io.Serializable;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;
import javax.annotation.PostConstruct;
import javax.ejb.EJB;
import javax.faces.event.ActionEvent;
import javax.faces.model.DataModel;
import javax.faces.model.ListDataModel;
import javax.faces.model.SelectItem;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.log4j.Logger;
import org.primefaces.component.datatable.DataTable;

/**
 * Base class for all CDB entity controllers. It encapsulates common
 * functionality related to entity creation, views, updates and deletion,
 * manages user settings and session customization relevant for a given entity
 * type, entity search, etc.
 *
 * @param <EntityType> CDB entity type
 * @param <FacadeType> CDB DB facade type
 */
public abstract class CdbEntityController<EntityType extends CdbEntity, FacadeType extends CdbEntityFacade<EntityType>, SettingObject extends ICdbSettings>
        implements Serializable, ICdbEntityController<EntityType> {

    private final String CDB_ENTITY_INFO_LOG_LEVEL = "cdbEntityInfo";
    private final String CDB_ENTITY_WARNING_LOG_LEVEL = "cdbEntityWarning";

    private static final Logger logger = Logger.getLogger(CdbEntityController.class.getName());

    @EJB
    private LogTopicFacade logTopicFacade;

    @EJB
    private SettingTypeFacade settingTypeFacade;

    protected SettingObject settingObject = null;

    protected EntityType current = null;

    protected DataModel listDataModel = null;
    protected DataTable listDataTable = null;
    protected boolean listDataModelReset = true;
    protected List<EntityType> filteredObjectList = null;

    protected DataModel selectDataModel = null;
    protected DataTable selectDataTable = null;
    protected boolean selectDataModelReset = false;
    protected EntityType selectedObject = null;
    protected List<EntityType> selectedObjectList = null;

    protected String logText = null;
    protected Integer logTopicId = null;

    protected String breadcrumbViewParam = null;
    protected String breadcrumbObjectIdViewParam = null;

    private String searchString = null;
    private boolean caseInsensitive = true;
    private LinkedList<SearchResult> searchResultList;

    protected List<SettingType> settingTypeList;

    protected String contextRootPermanentUrl;

    // TODO create a base cdbentitycontrollerextension helper. 
    private Set<ItemControllerExtensionHelper> subscribedResetForCurrentControllerHelpers;
    private Set<ItemControllerExtensionHelper> subscribePrepareInsertForCurrentControllerHelpers;

    /**
     * Default constructor.
     */
    public CdbEntityController() {
        settingObject = createNewSettingObject();
        subscribedResetForCurrentControllerHelpers = new HashSet<>();
        subscribePrepareInsertForCurrentControllerHelpers = new HashSet<>();
        contextRootPermanentUrl = ConfigurationUtility
                .getPortalProperty(CdbProperty.PERMANENT_CONTEXT_ROOT_URL_PROPERTY_NAME);
    }

    /**
     * Initialize controller and update its settings.
     */
    @PostConstruct
    public void initialize() {
        settingObject.updateSettings();
    }

    public void registerSearchable() {
        SearchController searchController = SearchController.getInstance();
        searchController.registerSearchableController(this);
    }

    protected abstract SettingObject createNewSettingObject();

    /**
     * Navigate to invalid request error page.
     *
     * @param error error message
     */
    public void handleInvalidSessionRequest(String error) {
        SessionUtility.setLastSessionError(error);
        SessionUtility.navigateTo("/views/error/invalidRequest?faces-redirect=true");
    }

    /**
     * Reset log text and topic fields.
     */
    public void resetLogText() {
        logText = "";
        logTopicId = null;
    }

    /**
     * Abstract method for returning entity DB facade.
     *
     * @return entity DB facade
     */
    protected abstract FacadeType getEntityDbFacade();

    /**
     * Abstract method for creating new entity instance.
     *
     * @return created entity instance
     */
    protected abstract EntityType createEntityInstance();

    /**
     * Abstract method for retrieving entity type name.
     *
     * @return entity type name
     */
    public abstract String getEntityTypeName();

    /**
     * Get entity type group name.
     *
     * By default this method returns null, and should be overridden in derived
     * controllers when corresponding entities have groups.
     *
     * @return entity type group name
     */
    public String getEntityTypeGroupName() {
        return null;
    }

    /**
     * Get entity type category name.
     *
     * By default this method returns null, and should be overridden in derived
     * controllers when corresponding entities have categories.
     *
     * @return entity type category name
     */
    public String getEntityTypeCategoryName() {
        return null;
    }

    /**
     * Get entity type type name.
     *
     * By default this method returns null, and should be overridden in derived
     * controllers when corresponding entities have types.
     *
     * @return entity type type name
     */
    public String getEntityTypeTypeName() {
        return null;
    }

    /**
     * Get display string for entity type name.
     *
     * By default this method simply returns entity type name, and should be
     * overridden in derived controllers for entities with complex names (i.e.,
     * those that consist of two or more words).
     *
     * @return entity type name display string
     */
    public String getDisplayEntityTypeName() {
        return getEntityTypeName();
    }

    /**
     * Get current entity instance name.
     *
     * By default this method returns null, and should be overridden in derived
     * controllers.
     *
     * @return current entity instance name
     */
    public abstract String getCurrentEntityInstanceName();

    /**
     * Get current entity instance.
     *
     * @return entity
     */
    public EntityType getCurrent() {
        return current;
    }

    /**
     * Find entity instance by id.
     *
     * @param id entity instance id
     * @return entity instance
     */
    public EntityType findById(Integer id) {
        return getEntityDbFacade().find(id);
    }

    /**
     * New current is being set, reset related variables.
     */
    protected void resetVariablesForCurrent() {
        for (ItemControllerExtensionHelper helper : subscribedResetForCurrentControllerHelpers) {
            helper.resetExtensionVariablesForCurrent();
        }
    }

    /**
     * Subscription will call resetExtensionVariablesForCurrent
     *
     * @param entityController
     */
    public void subscribeResetVariablesForCurrent(ItemControllerExtensionHelper entityController) {
        subscribedResetForCurrentControllerHelpers.add(entityController);
    }

    public void subscribePrepareInsertForCurrent(ItemControllerExtensionHelper entityController) {
        subscribePrepareInsertForCurrentControllerHelpers.add(entityController);
    }

    /**
     * Set current entity instance.
     *
     * @param current entity instance
     */
    public void setCurrent(EntityType current) {
        resetVariablesForCurrent();
        this.current = current;
    }

    /**
     * Get entity info object that belongs to the given entity.
     *
     * By default this method returns null. It should be overridden in derived
     * controllers for those entities that contain entity info object.
     *
     * @param entity entity object
     * @return entity info
     */
    public EntityInfo getEntityInfo(EntityType entity) {
        return null;
    }

    /**
     * Process view request parameters.
     *
     * If request is not valid, user will be redirected to appropriate error
     * page.
     */
    public void processViewRequestParams() {
        try {
            EntityType entity = selectByViewRequestParams();
            if (entity != null) {
                prepareEntityView(entity);
            }
        } catch (CdbException ex) {
            handleInvalidSessionRequest(ex.getErrorMessage());
        }
        processPreRender();
    }

    /**
     * Set breadcrumb variables from request parameters.
     */
    protected void setBreadcrumbRequestParams() {
        if (breadcrumbViewParam == null) {
            breadcrumbViewParam = SessionUtility.getRequestParameterValue("breadcrumb");
        }
        if (breadcrumbObjectIdViewParam == null) {
            breadcrumbObjectIdViewParam = SessionUtility.getRequestParameterValue("breadcrumbObjectId");
        }
    }

    /**
     * Select current entity instance for view from request parameters.
     *
     * @return selected entity instance
     * @throws CdbException in case of invalid request parameter values
     */
    public EntityType selectByViewRequestParams() throws CdbException {
        setBreadcrumbRequestParams();
        Integer idParam = null;
        String paramValue = SessionUtility.getRequestParameterValue("id");
        try {
            if (paramValue != null) {
                idParam = Integer.parseInt(paramValue);
            }
        } catch (NumberFormatException ex) {
            throw new InvalidRequest(
                    "Invalid value supplied for " + getDisplayEntityTypeName() + " id: " + paramValue);
        }
        if (idParam != null) {
            EntityType entity = findById(idParam);
            if (entity == null) {
                throw new InvalidRequest(StringUtility.capitalize(getDisplayEntityTypeName()) + " id " + idParam
                        + " does not exist.");
            }
            setCurrent(entity);
            return entity;
        } else if (current == null || current.getId() == null) {
            throw new InvalidRequest(
                    StringUtility.capitalize(getDisplayEntityTypeName()) + " has not been selected.");
        }
        return current;
    }

    /**
     * Process edit request parameters.
     *
     * If request is not valid or not authorized, user will be redirected to
     * appropriate error page.
     */
    public void processEditRequestParams() {
        try {
            selectByEditRequestParams();
        } catch (CdbException ex) {
            handleInvalidSessionRequest(ex.getErrorMessage());
        }
        processPreRender();
    }

    /**
     * Select current entity instance for edit from request parameters.
     *
     * @return selected entity instance
     * @throws CdbException in case of invalid request parameter values
     */
    public EntityType selectByEditRequestParams() throws CdbException {
        setBreadcrumbRequestParams();
        Integer idParam = null;
        String paramValue = SessionUtility.getRequestParameterValue("id");
        try {
            if (paramValue != null) {
                idParam = Integer.parseInt(paramValue);
            }
        } catch (NumberFormatException ex) {
            throw new InvalidRequest(
                    "Invalid value supplied for " + getDisplayEntityTypeName() + " id: " + paramValue);
        }
        if (idParam != null) {
            EntityType entity = findById(idParam);
            if (entity == null) {
                throw new InvalidRequest(StringUtility.capitalize(getDisplayEntityTypeName()) + " id " + idParam
                        + " does not exist.");
            }
            setCurrent(entity);
        }

        if (current == null || current.getId() == null) {
            throw new InvalidRequest(
                    StringUtility.capitalize(getDisplayEntityTypeName()) + " has not been selected.");
        }

        // Make sure user is logged in
        UserInfo sessionUser = (UserInfo) SessionUtility.getUser();
        if (sessionUser == null) {
            SessionUtility.pushViewOnStack(
                    "/views/" + getEntityViewsDirectory() + "/edit.xhtml?id=" + idParam + "&faces-redirect=true");
            SessionUtility.navigateTo("/views/login.xhtml?faces-redirect=true");
            return null;
        } else {
            CdbRole sessionRole = (CdbRole) SessionUtility.getRole();
            if (sessionRole != CdbRole.ADMIN) {
                // Make sure user is authorized to edit entity
                // Try entity info first, then entity itself second
                boolean userAuthorized = AuthorizationUtility.isEntityWriteableByUser(getEntityInfo(current),
                        sessionUser);
                if (!userAuthorized) {
                    userAuthorized = AuthorizationUtility.isEntityWriteableByUser(current, sessionUser);
                    if (!userAuthorized) {
                        throw new AuthorizationError(
                                "User " + sessionUser.getUsername() + " is not authorized to edit "
                                        + getDisplayEntityTypeName() + " object with id " + current.getId() + ".");
                    }
                }
            }
        }
        return current;
    }

    /**
     * Process create request parameters.
     *
     * If request is not valid or not authorized, user will be redirected to
     * appropriate error page.
     */
    public void processCreateRequestParams() {
        try {
            selectByCreateRequestParams();
        } catch (CdbException ex) {
            handleInvalidSessionRequest(ex.getErrorMessage());
        }
    }

    /**
     * Get a entity views directory name.
     *
     * @return String of the directory name in views directory.
     */
    protected String getEntityViewsDirectory() {
        return getEntityTypeName();
    }

    /**
     * Create new entity instance based on request parameters.
     *
     * @throws CdbException in case of invalid request parameter values
     */
    public void selectByCreateRequestParams() throws CdbException {
        setBreadcrumbRequestParams();

        // Make sure user is logged in
        UserInfo sessionUser = (UserInfo) SessionUtility.getUser();
        if (sessionUser == null) {
            SessionUtility
                    .pushViewOnStack("/views/" + getEntityViewsDirectory() + "/create.xhtml?faces-redirect=true");
            SessionUtility.navigateTo("/views/login.xhtml?faces-redirect=true");
        } else {
            CdbRole sessionRole = (CdbRole) SessionUtility.getRole();
            if (sessionRole != CdbRole.ADMIN) {
                // Make sure user is authorized to create entity
                boolean userAuthorized = entityCanBeCreatedByUsers();
                if (!userAuthorized) {
                    throw new AuthorizationError("User " + sessionUser.getUsername()
                            + " is not authorized to create " + getDisplayEntityTypeName() + " entities.");
                }
            }
            // User authorized. 
            EntityType entity = getCurrent();
            if (entity == null || entity.getId() != null) {
                // entity is not yet set, or current entity is already in db. 
                prepareCreate();
            }

        }

    }

    /**
     * Get selected entity instance, or create new instance if none has been
     * selected previously.
     *
     * @return entity instance
     */
    public EntityType getSelected() {
        if (current == null) {
            setCurrent(createEntityInstance());
        }
        return current;
    }

    /**
     * Override this function if a derived controller needs to process when new
     * settings are present.
     */
    public void settingsAreReloaded() {
        resetListDataModel();
    }

    /**
     * Customize display for entity list.
     *
     * @return current view URL for page reload
     */
    public String customizeListDisplay() {
        String returnPage = SessionUtility.getCurrentViewId() + "?faces-redirect=true";
        logger.debug("Returning to page: " + returnPage);
        return returnPage;
    }

    /**
     * Customize display for selection dialog.
     *
     * @return current view URL for page reload
     */
    public String customizeSelectDisplay() {
        resetSelectDataModel();
        String returnPage = SessionUtility.getCurrentViewId() + "?faces-redirect=true";
        logger.debug("Returning to page: " + returnPage);
        return returnPage;
    }

    /**
     * Reset list variables and associated filter values and data model.
     *
     * @return URL to entity list view
     */
    public String resetList() {
        logger.debug("Resetting list data model for " + getDisplayEntityTypeName());
        settingObject.clearListFilters();
        resetListDataModel();
        return prepareList();
    }

    /**
     * Prepare entity list view.
     *
     * @return URL to entity list view
     */
    public String prepareList() {
        logger.debug("Preparing list data model for " + getDisplayEntityTypeName());
        current = null;
        if (listDataTable != null) {
            settingObject.updateListSettingsFromListDataTable(listDataTable);
        }
        return "list?faces-redirect=true";
    }

    /**
     * Prepare list view from a given view path.
     *
     * @param viewPath
     * @return URL to entity list view
     */
    public String prepareListFromViewPath(String viewPath) {
        return viewPath + "/" + prepareList();
    }

    /**
     * Reset list and return entity view.
     *
     * @return URL to selected entity instance view
     */
    public String resetListForView() {
        logger.debug("Resetting list for " + getDisplayEntityTypeName() + " view ");
        settingObject.clearListFilters();
        resetListDataModel();
        return view();
    }

    /**
     * Reset list and return entity instance edit page.
     *
     * @return URL to selected entity instance edit page.
     */
    public String resetListForEdit() {
        logger.debug("Resetting list for " + getDisplayEntityTypeName() + " edit");
        settingObject.clearListFilters();
        resetListDataModel();
        return edit();
    }

    /**
     * Follow breadcrumb if it is set, or prepare entity list view.
     *
     * @return previous view if breadcrumb parameters are set, or entity list
     * view otherwise
     */
    public String followBreadcrumbOrPrepareList() {
        String loadView = breadcrumbViewParam;
        if (loadView == null) {
            loadView = prepareList();
        } else if (breadcrumbObjectIdViewParam != null) {
            Integer entityId = Integer.parseInt(breadcrumbObjectIdViewParam);
            loadView = breadcrumbViewParam + "?faces-redirect=true&id=" + entityId;
        }
        breadcrumbViewParam = null;
        breadcrumbObjectIdViewParam = null;
        return loadView;
    }

    public void processPreRender() {
        settingObject.updateSettings();
    }

    public void processPreRenderList() {
        if (settingObject.updateSettings()) {
            resetListDataModel();
        }
    }

    /**
     * Get entity selection list data table.
     *
     * @return selection list data table
     */
    public DataTable getSelectDataTable() {
        return selectDataTable;
    }

    /**
     * Set entity selection list data table.
     *
     * @param selectDataTable selection list data table
     */
    public void setSelectDataTable(DataTable selectDataTable) {
        this.selectDataTable = selectDataTable;
    }

    /**
     * Clear all list filters.
     */
    public void clearAllListFilters() {
        if (listDataTable == null) {
            return;
        }
        Map<String, Object> filterMap = listDataTable.getFilters();
        for (String filterName : filterMap.keySet()) {
            filterMap.put(filterName, "");
        }
    }

    /**
     * Check if any list filter is set.
     *
     * @return true if filter is set, false otherwise
     */
    public boolean isAnyListFilterSet() {
        if (listDataTable == null) {
            return false;
        }
        Map<String, Object> filterMap = listDataTable.getFilters();
        for (Object filterValue : filterMap.values()) {
            String filter = (String) filterValue;
            if (filter != null && !filter.isEmpty()) {
                return true;
            }
        }
        return false;
    }

    /**
     * Customize view display and reload current page.
     *
     * @return URL for the current view
     */
    public String customizeViewDisplay() {
        return getUrlForCurrentView();
    }

    /**
     * Gets a redirection string for the current view.
     *
     * @return redirection string for the current view
     */
    protected String getUrlForCurrentView() {
        String returnPage = SessionUtility.getCurrentViewId() + "?faces-redirect=true";
        logger.debug("Returning to page: " + returnPage);
        return returnPage;
    }

    /**
     * Verify that view is valid.
     *
     * If not, redirect user to appropriate error page.
     *
     * @throws IOException in case of IO errors
     */
    public void verifyView() throws IOException {
        if (current != null) {
            SessionUtility.redirectTo("/views/error/invalidRequest.xhtml");
        }
    }

    /**
     * Prepare entity view.
     *
     * This method should be overridden in the derived controller.
     *
     * @param entity entity instance
     */
    protected void prepareEntityView(EntityType entity) {
    }

    /**
     * Prepare entity view and return view URL.
     *
     * @param entity entity instance
     * @return entity view URL
     */
    public String prepareView(EntityType entity) {
        logger.debug("Preparing view");
        current = entity;
        settingObject.updateSettings();
        prepareEntityView(entity);
        return view();
    }

    /**
     * Return entity view page.
     *
     * @return URL to view page in the entity folder
     */
    public String view() {
        return "view?faces-redirect=true";
    }

    /**
     * Return entity view page with query parameters of id.
     *
     * @return URL to view page in the entity folder with id query paramter.
     */
    public String viewForCurrentEntity() {
        return "view?id=" + current.getId() + "&faces-redirect=true";
    }

    /**
     * Return entity list page.
     *
     * @return URL to list page in the entity folder
     */
    public String list() {
        return "list?faces-redirect=true";
    }

    /**
     * Return entity create page.
     *
     * @return URL to create page in the entity folder
     */
    public String prepareCreate() {
        setCurrent(createEntityInstance());
        return "create?faces-redirect=true";
    }

    /**
     * Clone entity instance.
     *
     * @param entity entity instance to be cloned
     * @return cloned entity instance
     */
    public EntityType cloneEntityInstance(EntityType entity) {
        EntityType clonedEntity;
        try {
            clonedEntity = (EntityType) (entity.clone());
        } catch (CloneNotSupportedException ex) {
            logger.error("Object cannot be cloned: " + ex);
            clonedEntity = createEntityInstance();
        }
        return clonedEntity;
    }

    /**
     * Prepare entity instance clone and return view to the cloned instance.
     *
     * @param entity entity instance to be cloned
     * @return URL to cloned instance view
     */
    public String prepareClone(EntityType entity) {
        current = cloneEntityInstance(entity);
        return getEntityApplicationViewPath() + "/" + getCloneCreatePageName() + "?faces-redirect=true";
    }

    protected String getCloneCreatePageName() {
        return "create";
    }

    public String getEntityApplicationViewPath() {
        return "/views/" + getEntityViewsDirectory();
    }

    public final String getCurrentEntityPermalink() {
        if (current != null) {
            String viewPath = contextRootPermanentUrl;
            viewPath += getCurrentEntityRelativePermalink();
            return viewPath;
        }
        return null;
    }

    public String getCurrentEntityRelativePermalink() {
        return getEntityApplicationViewPath() + "/view?id=" + current.getId();
    }

    public String getEntityEditRowStyle(EntityType entity) {
        if (entity.getPersitanceErrorMessage() != null) {
            return PortalStyles.rowStyleErrorInEntity.getValue();
        }
        if (entity.getId() == null) {
            return PortalStyles.rowStyleNewEntity.getValue();
        }
        return "";
    }

    /**
     * Prepare entity insert.
     *
     * This method should be overridden in the derived controller.
     *
     * @param entity entity instance
     * @throws CdbException in case of any errors
     */
    protected void prepareEntityInsert(EntityType entity) throws CdbException {
        // TODO: This needs to be placed in item controller. 
        if (entity == current) {
            if (entity instanceof Item) {
                for (ItemControllerExtensionHelper helper : subscribedResetForCurrentControllerHelpers) {
                    helper.prepareInsertForCurrent();
                }
            }
        }
    }

    /**
     * Allows the controller to quickly add a log entry to system logs with
     * current session user stamp.
     *
     * @param logLevel
     * @param message
     */
    private void addCdbEntitySystemLog(String logLevel, String message) {
        UserInfo sessionUser = (UserInfo) SessionUtility.getUser();
        if (sessionUser != null) {
            String username = sessionUser.getUsername();
            message = "User: " + username + " | " + message;
        }
        LogUtility.addSystemLog(logLevel, message);
    }

    /**
     * Allows the controller to quickly add a warning log entry while
     * automatically appending appropriate info.
     *
     * @param warningMessage - Generic warning message.
     * @param exception - [OPTIONAL] will append the message of the exception.
     * @param entity - [OPTIONAL] will append the toString of the entity.
     */
    public void addCdbEntityWarningSystemLog(String warningMessage, Exception exception, CdbEntity entity) {
        if (entity != null) {
            warningMessage += ": " + entity.toString();
        }
        if (exception != null) {
            warningMessage += ". Exception - " + exception.getMessage();
        }

        addCdbEntitySystemLog(CDB_ENTITY_WARNING_LOG_LEVEL, warningMessage);

    }

    public String create() {
        return create(false, false);
    }

    /**
     * Create new entity instance and return view to the new instance.
     *
     * @param silent determines if a message should be shown at completion to
     * the user.
     * @param skipSystemLog do not list entry in system logs.
     * @return URL to the new entity instance view
     */
    public String create(Boolean silent, Boolean skipSystemLog) {
        try {
            performCreateOperations(current, skipSystemLog);
            if (!silent) {
                SessionUtility.addInfoMessage("Success",
                        "Created " + getDisplayEntityTypeName() + " " + getCurrentEntityInstanceName() + ".");
            }
            return view();
        } catch (CdbException ex) {
            logger.error("Could not create " + getDisplayEntityTypeName() + ": " + ex.getMessage());
            if (!silent) {
                SessionUtility.addErrorMessage("Error",
                        "Could not create " + getDisplayEntityTypeName() + ": " + ex.getMessage());
            }
            if (!skipSystemLog) {
                addCdbEntityWarningSystemLog("Failed to create", ex, current);
            }
            return null;
        } catch (RuntimeException ex) {
            Throwable t = ExceptionUtils.getRootCause(ex);
            logger.error("Could not create " + getDisplayEntityTypeName() + ": " + t.getMessage());
            if (!silent) {
                SessionUtility.addErrorMessage("Error",
                        "Could not create " + getDisplayEntityTypeName() + ": " + t.getMessage());
            }
            if (!skipSystemLog) {
                addCdbEntityWarningSystemLog("Failed to create", ex, current);
            }
            return null;
        }
    }

    public void performCreateOperations(EntityType entity) throws CdbException, RuntimeException {
        performCreateOperations(entity, false);
    }

    public void performCreateOperations(EntityType entity, boolean skipSystemLog)
            throws CdbException, RuntimeException {
        performCreateOperations(entity, skipSystemLog, false);
    }

    public void performCreateOperations(EntityType entity, boolean skipSystemLog, boolean skipUpdateCurrent)
            throws CdbException, RuntimeException {
        try {
            if (!skipUpdateCurrent) {
                setCurrent(entity);
            }
            EntityType newEntity = entity;
            prepareEntityInsert(entity);
            getEntityDbFacade().create(entity);
            resetListDataModel();
            resetSelectDataModel();
            // Best to reload the entity after creation to ensure all connections are updated and initalized. 
            Object newEntityId = newEntity.getId();

            if (!skipSystemLog) {
                addCdbEntitySystemLog(CDB_ENTITY_INFO_LOG_LEVEL, "Created: " + entity.toString());
            }
            if (!skipUpdateCurrent) {
                setCurrent(findById((Integer) newEntityId));
            }

            entity.setPersitanceErrorMessage(null);
        } catch (CdbException ex) {
            entity.setPersitanceErrorMessage(ex.getMessage());
            throw ex;
        } catch (RuntimeException ex) {
            entity.setPersitanceErrorMessage(ex.getMessage());
            throw ex;
        }
    }

    /**
     * Prepare entity instance edit.
     *
     * @param entity entity instance to be updated
     * @return URL to edit page
     */
    public String prepareEdit(EntityType entity) {
        resetLogText();
        current = entity;
        return edit();
    }

    /**
     * Return entity edit page.
     *
     * @return URL to edit page in the entity folder
     */
    public String edit() {
        clearSelectFiltersAndResetSelectDataModel();
        return "edit?faces-redirect=true";
    }

    /**
     * Prepare entity update.
     *
     * This method should be overridden in the derived controller.
     *
     * @param entity entity instance
     * @throws CdbException in case of any errors
     */
    protected void prepareEntityUpdate(EntityType entity) throws CdbException {
    }

    /**
     * Perform any addition actions after an entity has been updated.
     *
     * @param entity
     */
    protected void completeEntityUpdate(EntityType entity) {
    }

    public void updateWithoutRedirect() {
        update();
    }

    /**
     * Update current entity and save changes in the database.
     *
     * @return URL to current entity instance view page
     */
    public String update() {
        try {
            performUpdateOperations(current);
            SessionUtility.addInfoMessage("Success",
                    "Updated " + getDisplayEntityTypeName() + " " + getCurrentEntityInstanceName() + ".");
            return viewForCurrentEntity();
        } catch (CdbException ex) {
            SessionUtility.addErrorMessage("Error",
                    "Could not update " + getDisplayEntityTypeName() + ": " + ex.getMessage());
            addCdbEntityWarningSystemLog("Failed to update", ex, current);
            return null;
        } catch (RuntimeException ex) {
            Throwable t = ExceptionUtils.getRootCause(ex);
            logger.error("Could not update " + getDisplayEntityTypeName() + " " + getCurrentEntityInstanceName()
                    + ": " + t.getMessage());
            SessionUtility.addErrorMessage("Error",
                    "Could not update " + getDisplayEntityTypeName() + ": " + t.getMessage());
            addCdbEntityWarningSystemLog("Failed to update", ex, current);
            return null;
        }
    }

    public void performUpdateOperations(EntityType entity) throws CdbException, RuntimeException {
        try {
            setCurrent(entity);
            logger.debug("Updating " + getDisplayEntityTypeName() + " " + getCurrentEntityInstanceName());
            prepareEntityUpdate(entity);
            EntityType updatedEntity = getEntityDbFacade().edit(entity);
            addCdbEntitySystemLog(CDB_ENTITY_INFO_LOG_LEVEL, "Updated: " + entity.toString());
            resetListDataModel();
            resetSelectDataModel();
            resetLogText();
            setCurrent(updatedEntity);
            completeEntityUpdate(entity);
            entity.setPersitanceErrorMessage(null);
        } catch (CdbException ex) {
            entity.setPersitanceErrorMessage(ex.getMessage());
            throw ex;
        } catch (RuntimeException ex) {
            Throwable t = ExceptionUtils.getRootCause(ex);
            entity.setPersitanceErrorMessage(t.getMessage());
            throw ex;
        }
    }

    public void reloadCurrent() {
        current = getEntityDbFacade().find(current.getId());
    }

    public String inlineUpdate() {
        String updateResult = update();

        // An error occured, reload the page with correct information. 
        if (updateResult == null) {
            reloadCurrent();
            return view();
        }

        return null;
    }

    /**
     * Prepare entity update when changes involve removing associated objects
     * from the database.
     *
     * This method should be overridden in the derived controller.
     *
     * @param entity entity instance
     * @throws CdbException in case of any errors
     */
    protected void prepareEntityUpdateOnRemoval(EntityType entity) throws CdbException {
    }

    /**
     * Update current entity and save changes in the database.
     *
     * This method is used when changes involve only removing associated objects
     * from the database.
     *
     * @return URL to current entity instance view page
     */
    public String updateOnRemoval() {
        try {
            logger.debug("Updating " + getDisplayEntityTypeName() + " " + getCurrentEntityInstanceName());
            prepareEntityUpdateOnRemoval(current);
            EntityType updatedEntity = getEntityDbFacade().edit(current);
            SessionUtility.addInfoMessage("Success",
                    "Updated " + getDisplayEntityTypeName() + " " + getCurrentEntityInstanceName() + ".");
            resetListDataModel();
            resetSelectDataModel();
            resetLogText();
            current = updatedEntity;
            return view();
        } catch (CdbException ex) {
            SessionUtility.addErrorMessage("Error",
                    "Could not update " + getDisplayEntityTypeName() + ": " + ex.getMessage());
            return null;
        } catch (RuntimeException ex) {
            Throwable t = ExceptionUtils.getRootCause(ex);
            logger.error("Could not update " + getDisplayEntityTypeName() + " " + getCurrentEntityInstanceName()
                    + ": " + t.getMessage());
            SessionUtility.addErrorMessage("Error",
                    "Could not update " + getDisplayEntityTypeName() + ": " + t.getMessage());
            return null;
        }
    }

    /**
     * Prepare entity removal from the database.
     *
     * This method should be overridden in the derived controller.
     *
     * @param entity entity instance
     * @throws CdbException in case of any errors
     */
    protected void prepareEntityDestroy(EntityType entity) throws CdbException {
    }

    /**
     * Perform any additional actions upon successful removal of an entity.
     */
    protected void completeEntityDestroy(EntityType entity) {
    }

    /**
     * Remove entity instance from the database.
     *
     * @param entity entity instance to be deleted
     */
    public void destroy(EntityType entity) {
        current = entity;
        destroy();
    }

    /**
     * Executes destroy but does not return redirection string.
     *
     * @return redirection to current view when successful.
     */
    public String destroyInCurrentView() {
        String result = destroy();

        if (result != null) {
            return getUrlForCurrentView();
        }

        return null;
    }

    public void performDestroyOperations(EntityType entity) throws CdbException, RuntimeException {
        try {
            if (entity == null) {
                logger.warn("entity item is not set");
                // Do nothing if entity item is not set.
                return;
            } else if (entity.getId() == null) {
                logger.warn("entity item id is null");
                completeEntityDestroy(entity);
                // Do nothing if there is no id.
                return;
            }

            prepareEntityDestroy(entity);
            getEntityDbFacade().remove(entity);
            completeEntityDestroy(entity);
            addCdbEntitySystemLog(CDB_ENTITY_INFO_LOG_LEVEL, "Deleted: " + entity.toString());
            resetListDataModel();
            resetSelectDataModel();
            settingObject.clearListFilters();
        } catch (CdbException ex) {
            entity.setPersitanceErrorMessage(ex.getMessage());
            throw ex;
        } catch (RuntimeException ex) {
            Throwable t = ExceptionUtils.getRootCause(ex);
            entity.setPersitanceErrorMessage(t.getMessage());
            throw ex;
        }
    }

    /**
     * Remove current (selected) entity instance from the database and reset
     * list variables and data model.
     *
     * @return URL to entity list page
     */
    public String destroy() {
        logger.debug("Destroying " + getDisplayEntityTypeName() + " " + getCurrentEntityInstanceName());
        try {
            performDestroyOperations(current);
            SessionUtility.addInfoMessage("Success",
                    "Deleted " + getDisplayEntityTypeName() + " " + getCurrentEntityInstanceName() + ".");
            return prepareList();
        } catch (CdbException ex) {
            SessionUtility.addErrorMessage("Error",
                    "Could not delete " + getDisplayEntityTypeName() + ": " + ex.getMessage());
            addCdbEntityWarningSystemLog("Failed to delete", ex, current);
            return null;
        } catch (RuntimeException ex) {
            Throwable t = ExceptionUtils.getRootCause(ex);
            logger.error("Could not delete " + getDisplayEntityTypeName() + " " + getCurrentEntityInstanceName()
                    + ": " + t.getMessage());
            SessionUtility.addErrorMessage("Error",
                    "Could not delete " + getDisplayEntityTypeName() + ": " + t.getMessage());
            addCdbEntityWarningSystemLog("Failed to delete", ex, current);
            return null;
        }
    }

    /**
     * Create data model from list of all available entity instances.
     */
    public void createListDataModel() {
        listDataModel = new ListDataModel(getEntityDbFacade().findAll());
    }

    public void setListDataModel(ListDataModel listDataModel) {
        this.listDataModel = listDataModel;
    }

    /**
     * Get list data model.
     *
     * If model is not set, this method will create it.
     *
     * @return list data model
     */
    public DataModel getListDataModel() {
        if (listDataModel == null) {
            createListDataModel();
        }
        return listDataModel;
    }

    /**
     * Prepare entity list for selection.
     *
     * This method should be overridden in the derived controller.
     *
     * @param selectEntityList entity list to be used for selection
     */
    public void prepareEntityListForSelection(List<EntityType> selectEntityList) {
    }

    /**
     * Create data model for entity selection.
     *
     * @return selection data model
     */
    public DataModel createSelectDataModel() {
        List<EntityType> selectEntityList = getEntityDbFacade().findAll();
        prepareEntityListForSelection(selectEntityList);
        selectDataModel = new ListDataModel(selectEntityList);
        return selectDataModel;
    }

    /**
     * Create data model for entity selection using provided list.
     *
     * @param selectEntityList entity list to be used for selection
     * @return selection data model
     */
    public DataModel createSelectDataModel(List<EntityType> selectEntityList) {
        selectDataModel = new ListDataModel(selectEntityList);
        return selectDataModel;
    }

    /**
     * Get data model for entity selection.
     *
     * If selection data model is null, it will be created.
     *
     * @return selection data model
     */
    public DataModel getSelectDataModel() {
        if (selectDataModel == null) {
            createSelectDataModel();
        }
        return selectDataModel;
    }

    /**
     * Create data model for entity selection, but without current (selected)
     * entity.
     *
     * @return selection data model
     */
    public DataModel createSelectDataModelWithoutCurrent() {
        List<EntityType> selectEntityList = getAvailableItemsWithoutCurrent();

        prepareEntityListForSelection(selectEntityList);
        selectDataModel = new ListDataModel(selectEntityList);
        return selectDataModel;
    }

    /**
     * Get data model for entity selection, but without current (selected)
     * entity.
     *
     * If selection data model is null, it will be created.
     *
     * @return selection data model
     */
    public DataModel getSelectDataModelWithoutCurrent() {
        if (selectDataModel == null) {
            createSelectDataModelWithoutCurrent();
        }
        return selectDataModel;
    }

    /**
     * Get data model using list of all entity instances.
     *
     * @return data model
     */
    public DataModel getItems() {
        return getListDataModel();
    }

    /**
     * Get list of selected objects and reset selection data model.
     *
     * @return list of selected objects
     */
    public List<EntityType> getSelectedObjectListAndResetSelectDataModel() {
        List<EntityType> returnList = selectedObjectList;
        resetSelectDataModel();
        return returnList;
    }

    public List<EntityType> getSelectedObjectList() {
        return selectedObjectList;
    }

    public List<EntityType> getFilteredObjectList() {
        return filteredObjectList;
    }

    public List<EntityType> getFilteredItems() {
        return filteredObjectList;
    }

    /**
     * Reset selected object list to null.
     */
    public void resetSelectedObjectList() {
        selectedObjectList = null;
    }

    public void setSelectedObjectList(List<EntityType> selectedObjectList) {
        this.selectedObjectList = selectedObjectList;
    }

    public void setFilteredObjectList(List<EntityType> filteredObjectList) {
        this.filteredObjectList = filteredObjectList;
    }

    public void setFilteredItems(List<EntityType> filteredItems) {
        this.filteredObjectList = filteredItems;
    }

    public EntityType getSelectedObjectAndResetDataModel() {
        EntityType entity = getSelectedObject();
        selectedObject = null;
        resetSelectDataModel();
        return entity;

    }

    public EntityType getSelectedObject() {
        return selectedObject;
    }

    public void setSelectedObject(EntityType selectedObject) {
        this.selectedObject = selectedObject;
    }

    /**
     * Reset list data model and set current entity.
     *
     * @param currentEntity current entity
     */
    public void resetListDataModelAndSetCurrent(EntityType currentEntity) {
        resetListDataModel();
        current = currentEntity;
    }

    /**
     * Reset various list variables to null so that they can be recreated for
     * next request.
     */
    public void resetListDataModel() {
        listDataModel = null;
        listDataTable = null;
        listDataModelReset = true;
        filteredObjectList = null;
        // Flush cache 
        //getFacade().flush();
    }

    /**
     * Reset various selection variables so that they can be recreated for next
     * request.
     */
    public void resetSelectDataModel() {
        selectDataModel = null;
        selectDataTable = null;
        selectedObjectList = null;
        selectDataModelReset = true;
        // Flush cache 
        //getFacade().flush();
    }

    public void clearListFiltersAndResetListDataModel() {
        settingObject.clearListFilters();
        resetListDataModel();
    }

    public void clearSelectFiltersAndResetSelectDataModelActionListener(ActionEvent actionEvent) {
        clearSelectFiltersAndResetSelectDataModel();
    }

    public void clearSelectFiltersAndResetSelectDataModel() {
        if (selectDataTable != null) {
            selectDataTable.getFilters().clear();
        }
        settingObject.clearSelectFilters();
        resetSelectDataModel();
    }

    /**
     * Create log object if log text is not null.
     *
     * @return new log entry
     */
    public Log prepareLogEntry() {
        Log logEntry = null;
        if (logText != null && !logText.isEmpty()) {
            logEntry = LogUtility.createLogEntry(logText);
            if (logTopicId != null) {
                LogTopic logTopic = logTopicFacade.find(logTopicId);
                logEntry.setLogTopic(logTopic);
            }
            resetLogText();
        }
        return logEntry;
    }

    public List<EntityType> getAvailableItems() {
        return getEntityDbFacade().findAll();
    }

    public List<EntityType> getAvailableItemsWithoutCurrent() {
        List<EntityType> entityList = getEntityDbFacade().findAll();
        if (current.getId() != null) {
            entityList.remove(current);
        }
        return entityList;
    }

    public EntityType getEntity(Integer id) {
        return getEntityDbFacade().find(id);
    }

    public SelectItem[] getAvailableItemsForSelectMany() {
        return CollectionUtility.getSelectItems(getEntityDbFacade().findAll(), false);
    }

    public SelectItem[] getAvailableItemsForSelectOne() {
        return CollectionUtility.getSelectItems(getEntityDbFacade().findAll(), true);
    }

    public String getCurrentViewId() {
        return SessionUtility.getCurrentViewId();
    }

    public static String displayEntityList(List<?> entityList) {
        String itemDelimiter = ", ";
        return CollectionUtility.displayItemListWithoutOutsideDelimiters(entityList, itemDelimiter);
    }

    /**
     * Search all entities for a given string.
     *
     * @param searchString search string
     * @param caseInsensitive use case insensitive search
     */
    public void performEntitySearch(String searchString, boolean caseInsensitive) {
        if (searchString == null || searchString.isEmpty()) {
            searchResultList = new LinkedList<>();
            return;
        }
        if (searchString.equals(this.searchString) && caseInsensitive == this.caseInsensitive) {
            // Return old results
            return;
        }

        // Start new search
        this.searchString = searchString;
        this.caseInsensitive = caseInsensitive;
        searchResultList = new LinkedList<>();

        Pattern searchPattern;
        if (caseInsensitive) {
            searchPattern = Pattern.compile(Pattern.quote(searchString), Pattern.CASE_INSENSITIVE);
        } else {
            searchPattern = Pattern.compile(Pattern.quote(searchString));
        }
        DataModel<EntityType> dataModel = getListDataModel();
        Iterator<EntityType> iterator = dataModel.iterator();
        while (iterator.hasNext()) {
            EntityType entity = iterator.next();
            try {
                SearchResult searchResult = entity.search(searchPattern);
                if (!searchResult.isEmpty()) {
                    searchResultList.add(searchResult);
                }
            } catch (RuntimeException ex) {
                logger.warn("Could not search entity " + entity.toString() + " (Error: " + ex.toString() + ")");
            }

        }
    }

    public LinkedList<SearchResult> getSearchResultList() {
        return searchResultList;
    }

    public boolean getDisplaySearchResultList() {
        return searchResultList != null && !searchResultList.isEmpty();
    }

    public boolean searchHasResults() {
        return !searchResultList.isEmpty();
    }

    public boolean entityHasCategories() {
        return getEntityTypeCategoryName() != null;
    }

    public boolean entityHasTypes() {
        return getEntityTypeTypeName() != null;
    }

    public String getEntityEntityCategoryName() {
        return "Category";
    }

    public String getEntityEntityTypeName() {
        return "Type";
    }

    public boolean entityHasGroups() {
        return getEntityTypeGroupName() != null;
    }

    public boolean entityCanBeCreatedByUsers() {
        return false;
    }

    public String getLogText() {
        return logText;
    }

    public void setLogText(String logText) {
        this.logText = logText;
    }

    public Integer getLogTopicId() {
        return logTopicId;
    }

    public void setLogTopicId(Integer logTopicId) {
        this.logTopicId = logTopicId;
    }

    public SettingObject getSettingObject() {
        return settingObject;
    }

    /**
     * Get list of setting types.
     *
     * If not set, this list is retrieved from the database.
     *
     * @return setting type list
     */
    public List<SettingType> getSettingTypeList() {
        if (settingTypeList == null) {
            settingTypeList = settingTypeFacade.findAll();
        }
        return settingTypeList;
    }

    public Boolean getDisplayLoadPropertyValuesButton() {
        return false;
    }

    public Boolean getDisplayUpdateSortOrderButton() {
        return false;
    }

    /**
     * If list data model needs to be reset this method will return true, and
     * modify reset flag.
     *
     * @return true if list data model needs to be reset, false otherwise
     */
    public boolean shouldResetListDataModel() {
        if (listDataModelReset) {
            listDataModelReset = false;
            return true;
        }
        return false;
    }

    /**
     * If select data model needs to be reset this method will return true, and
     * modify reset flag.
     *
     * @return true if select data model needs to be reset, false otherwise
     */
    public boolean shouldResetSelectDataModel() {
        if (selectDataModelReset) {
            selectDataModelReset = false;
            return true;
        }
        return false;
    }

    public String getBreadcrumbViewParam() {
        return breadcrumbViewParam;
    }

    public void setBreadcrumbViewParam(String breadcrumbViewParam) {
        this.breadcrumbViewParam = breadcrumbViewParam;
    }
}