/* * Copyright (C) 2005-2012 BetaCONCEPT Limited * * This file is part of Astroboa. * * Astroboa is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Astroboa is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with Astroboa. If not, see <http://www.gnu.org/licenses/>. */ package org.betaconceptframework.astroboa.console.jsf.edit.draft; import java.util.ArrayList; import java.util.Calendar; import java.util.List; import java.util.concurrent.ConcurrentHashMap; import javax.faces.application.FacesMessage; import org.apache.commons.lang.StringUtils; import org.betaconceptframework.astroboa.api.model.CmsRepositoryEntity; import org.betaconceptframework.astroboa.console.seam.SeamEventNames; import org.betaconceptframework.ui.jsf.utility.JSFUtilities; import org.jboss.seam.ScopeType; import org.jboss.seam.annotations.Name; import org.jboss.seam.annotations.Observer; import org.jboss.seam.annotations.Scope; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Draft is used to store unsaved property values, mainly * rich texts for each logged in user. * * Astroboa provides a mechanism to auto save these values in this * draft n order for these to be available when ever property * is being edited again. * * This bean is not in SESSION scope as there is the case where * user might accidentally logs out. Thus we need APPLICATION scope * and appropriate structure to organize unsaved values per logged in * user * * * @author Gregory Chomatas (gchomatas@betaconcept.com) * @author Savvas Triantafyllou (striantafyllou@betaconcept.com) * */ @Name("draft") @Scope(ScopeType.APPLICATION) public class Draft { private final Logger logger = LoggerFactory.getLogger(getClass()); //Key is LoggedIn RepositoryUser id private ConcurrentHashMap<String, LoggedInUserDraftItemContainer> draftItemContainerPerUser = new ConcurrentHashMap<String, LoggedInUserDraftItemContainer>(); public void add(String repositoryUserId, CmsRepositoryEntity cmsRepositoryEntity) { logger.debug("Adding entity {} for user {}",cmsRepositoryEntity, repositoryUserId); if (StringUtils.isBlank(repositoryUserId)) { return; } boolean success = false; //Try to success for at most 30 secs as there might be the case of an endless loop. //Do not forget that if edit form is enabled auto save will run every 2 minutes Calendar thirtySecondsAfterStart = Calendar.getInstance(); thirtySecondsAfterStart.add(Calendar.SECOND, 30); try{ do { LoggedInUserDraftItemContainer draftItemContainer = draftItemContainerPerUser.get(repositoryUserId); LoggedInUserDraftItemContainer newDraftItemContainer = (draftItemContainer == null) ? new LoggedInUserDraftItemContainer(repositoryUserId) : draftItemContainer; newDraftItemContainer.add(cmsRepositoryEntity); if (draftItemContainer == null) { success = (draftItemContainerPerUser.putIfAbsent(repositoryUserId, newDraftItemContainer) == null)? true : false; } else { success = draftItemContainerPerUser.replace(repositoryUserId, draftItemContainer, newDraftItemContainer); } if (! success && Calendar.getInstance().after(thirtySecondsAfterStart)) { success = true; JSFUtilities.addMessage(null, "object.edit.save.draft.error",null, FacesMessage.SEVERITY_WARN); } } while (!success); } catch(Exception e) { logger.error("",e); JSFUtilities.addMessage(null, "object.edit.save.draft.error",null, FacesMessage.SEVERITY_WARN); } } public void deleteDraftItem_UIAction(String loggedInRepositoryUserId, String draftItemId) { if (StringUtils.isBlank(loggedInRepositoryUserId)|| StringUtils.isBlank(draftItemId)) { return; } if (draftItemContainerPerUser.containsKey(loggedInRepositoryUserId)) { draftItemContainerPerUser.get(loggedInRepositoryUserId).removeDraftItem(draftItemId); if (! draftItemContainerPerUser.get(loggedInRepositoryUserId).hasDraftItems()) { draftItemContainerPerUser.remove(loggedInRepositoryUserId); } } } public List<DraftItem> getDraftItemsForLoggedInRepositoryUser(String loggedInRepositoryUserId) { if (StringUtils.isBlank(loggedInRepositoryUserId) || !draftItemContainerPerUser.containsKey(loggedInRepositoryUserId)) { return new ArrayList<DraftItem>(); } return draftItemContainerPerUser.get(loggedInRepositoryUserId).getACopyOfDraftItems(); } public boolean hasLoggedInRepositoryUserItemsInDraft(String loggedInRepositoryUserId) { return StringUtils.isNotBlank(loggedInRepositoryUserId) && draftItemContainerPerUser.containsKey(loggedInRepositoryUserId) && draftItemContainerPerUser.get(loggedInRepositoryUserId).hasDraftItems(); } public void permanentlyRemoveDraftItemsForLoggedInRepositoryUser_UIAction(String loggedInRepositoryUserId) { if (StringUtils.isNotBlank(loggedInRepositoryUserId) && draftItemContainerPerUser.containsKey(loggedInRepositoryUserId)) { draftItemContainerPerUser.remove(loggedInRepositoryUserId); } } public void removeDraftItemsOlderThan(int numberOfHours) { if (draftItemContainerPerUser.isEmpty()) { return; } for (LoggedInUserDraftItemContainer loggedInUserDraftItemContainer : draftItemContainerPerUser.values()) { int numberOfDraftItemsRemoved = loggedInUserDraftItemContainer.removeDraftItemsOlderThan(numberOfHours); if (numberOfDraftItemsRemoved >0) { logger.info("Removed {} expired items from user's {}", numberOfDraftItemsRemoved, loggedInUserDraftItemContainer.getRepositoryUserId()); } } } @Observer({SeamEventNames.CONTENT_OBJECT_MODIFIED}) public void removeCmsRepositoryEntityFromDraft(String loggedInRepositoryUserId, String cmsRepositoryEntityId) { deleteDraftItem_UIAction(loggedInRepositoryUserId, cmsRepositoryEntityId); } }