/** * Copyright 2005-2016 hdiv.org * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.hdiv.session; import java.util.List; import javax.servlet.http.HttpSession; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.hdiv.context.RequestContextHolder; import org.hdiv.state.IPage; import org.hdiv.util.Constants; import org.springframework.beans.factory.BeanFactory; /** * Session cache handler * @author Ander Ruiz * */ public class HTTPSessionCache { /** * Commons Logging instance. */ private static final Log log = LogFactory.getLog(SessionHDIV.class); private BeanFactory beanFactory; /** * Prefix for the key of the pages stored in session. */ private static final String PAGE_ID_KEY_PREFIX = "hdiv-page-"; public void insertPage(final SimpleCacheKey key, final IPage newPage) { RequestContextHolder ctx = key.getRequestContext(); SessionModel session = ctx.getSession(); int pageId = newPage.getId(); IStateCache cache = getStateCache(session); // Get current request page identifier. Null if no state Integer currentPage = key.getRequestContext().getCurrentPageId(); Integer lastPageId = cache.getLastPageId(); IPage lastPage = lastPageId == null ? null : findPage(new SimpleCacheKey(key.getRequestContext(), lastPageId)); boolean isRefreshRequest = lastPage != null && newPage.getParentStateId() != null && lastPage.getParentStateId() != null && newPage.getParentStateId().equals(lastPage.getParentStateId()); // Check if is an Ajax request. boolean isAjaxRequest = ctx.isAjax(); Integer removedPageId = cache.addPage(pageId, currentPage, isRefreshRequest, isAjaxRequest); // if it returns a page identifier it is because the cache has reached // the maximum size and therefore we must delete the page which has been // stored for the longest time if (removedPageId != null) { deletePage(session, removedPageId); } // we update page identifier cache in session saveStateCache(session, cache); session.setAttribute(PAGE_ID_KEY_PREFIX + newPage.getId(), newPage); if (log.isDebugEnabled()) { log.debug("Added new page with id:" + newPage.getId()); } // log cache content logCacheContent(key.getRequestContext(), cache); } public IPage findPage(final SimpleCacheKey key) { if (log.isDebugEnabled()) { log.debug("Getting page with id:" + key.getPageId()); } SessionModel session = key.getRequestContext().getSession(); return (IPage) session.getAttribute(PAGE_ID_KEY_PREFIX + key.getPageId()); } public boolean removePage(final SimpleCacheKey key) { SessionModel session = key.getRequestContext().getSession(); Object attr = session.getAttribute(PAGE_ID_KEY_PREFIX + key.getPageId()); if (attr == null) { return false; } session.removeAttribute(PAGE_ID_KEY_PREFIX + key.getPageId()); IStateCache cache = getStateCache(session); return cache.getPageIds().remove(new Integer(key.getPageId())); } private void deletePage(final SessionModel session, final int pageId) { session.removeAttribute(PAGE_ID_KEY_PREFIX + pageId); if (log.isDebugEnabled()) { log.debug("Deleted page with id:" + pageId); } } void removeEndedPages(final RequestContextHolder context, final String conversationId) { SessionModel session = context.getSession(); IStateCache cache = getStateCache(session); if (log.isDebugEnabled()) { log.debug("Cache pages before finished pages are deleted:" + cache.toString()); } List<Integer> pageIds = cache.getPageIds(); for (int i = 0; i < pageIds.size(); i++) { Integer pageId = pageIds.get(i); IPage currentPage = findPage(new SimpleCacheKey(context, pageId)); if (currentPage != null && conversationId.equalsIgnoreCase(currentPage.getFlowId())) { deletePage(context.getSession(), pageId); pageIds.remove(i); i--; } } if (log.isDebugEnabled()) { log.debug("Cache pages after finished pages are deleted:" + cache); } } /** * Log cache content in the logger. * * @param context Context holder for request-specific state. * @param cache cache object */ protected void logCacheContent(final RequestContextHolder context, final IStateCache cache) { if (log.isTraceEnabled()) { synchronized (cache) { List<Integer> ids = cache.getPageIds(); StringBuilder sb = new StringBuilder(); for (final Integer id : ids) { IPage page = findPage(new SimpleCacheKey(context, id)); String parentPage = null; if (page != null) { parentPage = page.getParentStateId(); } if (parentPage != null) { parentPage = parentPage.substring(0, parentPage.indexOf('-')); } sb.append('[').append(id).append(" (").append(parentPage).append(")] "); } log.trace("Cache content [" + sb.toString() + "]"); } } } /** * Create new or obtain existing state Cache instance. * * @param session {@link HttpSession} instance * @return IStateCache instance */ public IStateCache getStateCache(final SessionModel session) { IStateCache cache = (IStateCache) session.getAttribute(Constants.STATE_CACHE_NAME); if (cache == null) { cache = createStateCacheInstance(); session.setAttribute(Constants.STATE_CACHE_NAME, cache); } return cache; } /** * Save state Cache instance. * * @param session {@link HttpSession} instance * @param stateCache {@link IStateCache} instance * @since HDIV 2.1.6 */ protected void saveStateCache(final SessionModel session, final IStateCache stateCache) { session.setAttribute(Constants.STATE_CACHE_NAME, stateCache); } /** * Create new {@link IStateCache} instance. * * @return {@link IStateCache} instance * @since HDIV 2.1.6 */ protected IStateCache createStateCacheInstance() { return beanFactory.getBean(IStateCache.class); } public void setBeanFactory(final BeanFactory beanFactory) { this.beanFactory = beanFactory; } }