001/*
002 * (C) Copyright 2012 Nuxeo SA (http://nuxeo.com/) and others.
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 * Contributors:
017 *     Anahide Tchertchian
018 */
019package org.nuxeo.ecm.webapp.seam;
020
021import static org.jboss.seam.annotations.Install.FRAMEWORK;
022
023import java.io.Serializable;
024
025import org.apache.commons.logging.Log;
026import org.apache.commons.logging.LogFactory;
027import org.jboss.seam.ScopeType;
028import org.jboss.seam.annotations.In;
029import org.jboss.seam.annotations.Install;
030import org.jboss.seam.annotations.Name;
031import org.jboss.seam.annotations.Scope;
032import org.jboss.seam.core.Events;
033import org.nuxeo.ecm.platform.ui.web.rest.RestfulPhaseListener;
034import org.nuxeo.ecm.webapp.helpers.EventNames;
035
036/**
037 * Conversation component that keeps the last update timestamp to handle hot reload when this timestamp changes.
038 * <p>
039 * Triggered by {@link RestfulPhaseListener} at the beginning of render response phase so that Seam components are not
040 * left in a strange state.
041 *
042 * @since 5.6
043 * @see RestfulPhaseListener
044 * @see NuxeoSeamHotReloader#shouldResetCache(Long)
045 * @see NuxeoSeamHotReloader#triggerResetOnSeamComponents()
046 */
047@Name("seamReloadContext")
048@Scope(ScopeType.CONVERSATION)
049@Install(precedence = FRAMEWORK)
050public class NuxeoSeamHotReloadContextKeeper implements Serializable {
051
052    private static final long serialVersionUID = 1L;
053
054    private static final Log log = LogFactory.getLog(NuxeoSeamHotReloader.class);
055
056    protected Long lastCacheKey;
057
058    @In(create = true)
059    protected NuxeoSeamHotReloader seamReload;
060
061    /*
062     * Called from {@link RestfulPhaseListener}.s
063     */
064    public void triggerReloadIdNeeded() {
065        if (lastCacheKey == null) {
066            doLog("No last cache key => no hot reload triggered");
067            lastCacheKey = Long.valueOf(System.currentTimeMillis());
068        } else {
069            if (seamReload.shouldResetCache(lastCacheKey)) {
070                doLog(String.format("Before reset, cache key=%s", lastCacheKey));
071                try {
072                    // trigger reset on Seam layer by raising the flush event
073                    Events.instance().raiseEvent(EventNames.FLUSH_EVENT);
074                } finally {
075                    // update cache key even if an error is triggered, to avoid
076                    // triggering cache reset over and over
077                    Long currentTimestamp = seamReload.getCurrentCacheTimestamp();
078                    if (currentTimestamp != null) {
079                        lastCacheKey = seamReload.getCurrentCacheTimestamp();
080                    }
081                }
082                doLog(String.format("After reset, cache key=%s", lastCacheKey));
083            } else {
084                doLog(String.format("No reset needed, cache key=%s", lastCacheKey));
085            }
086        }
087    }
088
089    protected void doLog(String message) {
090        if (log.isDebugEnabled()) {
091            log.debug(message);
092        }
093    }
094
095}