org.nuxeo.ecm.webapp.seam.NuxeoSeamHotReloader.java Source code

Java tutorial

Introduction

Here is the source code for org.nuxeo.ecm.webapp.seam.NuxeoSeamHotReloader.java

Source

/*
 * (C) Copyright 2006-2008 Nuxeo SA (http://nuxeo.com/) and others.
 *
 * 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.
 *
 * Contributors:
 *     Thierry Delprat
 */
package org.nuxeo.ecm.webapp.seam;

import static org.jboss.seam.ScopeType.EVENT;
import static org.jboss.seam.annotations.Install.FRAMEWORK;

import java.io.IOException;
import java.io.Serializable;
import java.security.Principal;

import javax.faces.context.FacesContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jboss.seam.ScopeType;
import org.jboss.seam.annotations.Factory;
import org.jboss.seam.annotations.In;
import org.jboss.seam.annotations.Install;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Observer;
import org.jboss.seam.annotations.Scope;
import org.jboss.seam.annotations.intercept.BypassInterceptors;
import org.jboss.seam.core.Events;
import org.nuxeo.ecm.core.api.NuxeoPrincipal;
import org.nuxeo.ecm.platform.ui.web.auth.NXAuthConstants;
import org.nuxeo.ecm.platform.ui.web.rest.api.URLPolicyService;
import org.nuxeo.ecm.platform.ui.web.util.BaseURL;
import org.nuxeo.ecm.webapp.helpers.EventNames;
import org.nuxeo.runtime.api.Framework;
import org.nuxeo.runtime.reload.ReloadService;
import org.nuxeo.runtime.service.TimestampedService;

/**
 * Simple Seam bean to control the Reload Action
 *
 * @author tiry
 */
@Name("seamReload")
@Scope(EVENT)
@Install(precedence = FRAMEWORK)
public class NuxeoSeamHotReloader implements Serializable {

    private static final long serialVersionUID = 1L;

    private static final Log log = LogFactory.getLog(NuxeoSeamHotReloader.class);

    @In(required = false, create = true)
    private transient Principal currentUser;

    /**
     * Returns true if dev mode is set
     *
     * @since 5.6
     * @see Framework#isDevModeSet()
     */
    @Factory(value = "nxDevModeSet", scope = ScopeType.EVENT)
    public boolean isDevModeSet() {
        return Framework.isDevModeSet();
    }

    @Factory(value = "seamHotReloadIsEnabled", scope = ScopeType.APPLICATION)
    public boolean isHotReloadEnabled() {
        return SeamHotReloadHelper.isHotReloadEnabled();
    }

    /**
     * Returns true if dev mode is set and current user is an administrator.
     *
     * @since 5.6
     * @return
     */
    public boolean getCanTriggerFlush() {
        NuxeoPrincipal pal = null;
        if (currentUser instanceof NuxeoPrincipal) {
            pal = (NuxeoPrincipal) currentUser;
        }
        return isDevModeSet() && pal != null && pal.isAdministrator();
    }

    /**
     * Calls the {@link ReloadService#flush()} method, that should trigger the reset of a bunch of caches shared by all
     * users, and sends a Seam event to propagate this to other Seam components.
     * <p>
     * Does nothing if not in dev mode.
     * <p>
     * The reload service flush method should already be triggerd by install/uninstall of modules. This method makes it
     * possible to force it again, and to propagate it to the Seam layer for current user.
     *
     * @see #resetSeamComponentsCaches()
     * @see #shouldResetCache(Long)
     * @see #shouldResetCache(TimestampedService, Long)
     * @since 5.6
     */
    public String doFlush() {
        if (Framework.isDevModeSet()) {
            FacesContext faces = FacesContext.getCurrentInstance();
            String viewId = faces.getViewRoot().getViewId();
            URLPolicyService service = Framework.getLocalService(URLPolicyService.class);
            String outcome = service.getOutcomeFromViewId(viewId, null);
            ReloadService srv = Framework.getLocalService(ReloadService.class);
            srv.flush();
            Events.instance().raiseEvent(EventNames.FLUSH_EVENT);
            // return the current view id otherwise an error appears in logs
            // because navigation cache needs to be rebuilt after execution
            return outcome;
        }
        return null;
    }

    /**
     * Returns true if reload service has sent a runtime flush event since given timestamp.
     *
     * @since 5.6
     * @param cacheTimestamp
     * @see ReloadService#lastFlushed()
     */
    public boolean shouldResetCache(Long cacheTimestamp) {
        if (cacheTimestamp == null) {
            return true;
        }
        Long serviceTimestamp = getCurrentCacheTimestamp();
        if (serviceTimestamp == null) {
            return false;
        }
        if (cacheTimestamp.compareTo(serviceTimestamp) < 0) {
            return true;
        }
        return false;
    }

    /**
     * Returns the last flush timestamp held by the {@link ReloadService}.
     *
     * @since 5.6
     * @see ReloadService
     * @see TimestampedService
     */
    public Long getCurrentCacheTimestamp() {
        ReloadService service = Framework.getService(ReloadService.class);
        return service.lastFlushed();
    }

    /**
     * Returns true if given service has changed since given timestamp.
     *
     * @since 5.6
     * @param service
     * @param cacheTimestamp
     * @see TimestampedService
     */
    public boolean shouldResetCache(TimestampedService service, Long cacheTimestamp) {
        if (cacheTimestamp == null || service == null) {
            return true;
        }
        Long serviceTimestamp = service.getLastModified();
        if (serviceTimestamp == null) {
            return false;
        }
        if (cacheTimestamp.compareTo(serviceTimestamp) < 0) {
            return true;
        }
        return false;
    }

    /**
     * Resets most caches of the Seam application.
     * <p>
     * This is useful when a change is detected on the reload service.
     * <p>
     * For compatibility and easier upgrade, this method listens to the {@link EventNames#FLUSH_EVENT}, and sends other
     * events to the Seam layer for other components to reset their own cache without needing to change their code.
     * <p>
     * In the future, this behaviour could be removed, so Seam component should reset their cache listening to the
     * {@link EventNames#FLUSH_EVENT} directly.
     *
     * @since 5.6
     */
    @Observer(value = { EventNames.FLUSH_EVENT }, create = false)
    @BypassInterceptors
    public void triggerResetOnSeamComponents() {
        String[] events = { EventNames.USER_ALL_DOCUMENT_TYPES_SELECTION_CHANGED,
                EventNames.LOCATION_SELECTION_CHANGED, EventNames.CONTENT_ROOT_SELECTION_CHANGED,
                EventNames.DOMAIN_SELECTION_CHANGED, EventNames.LOCAL_CONFIGURATION_CHANGED, };
        Events seamEvents = Events.instance();
        for (String event : events) {
            seamEvents.raiseEvent(event);
        }
    }

    /**
     * Triggers a full reload of Seam context and components.
     * <p>
     * Needs the Seam debug jar to be present and Seam debug mode to be enabled.
     */
    public String doReload() {
        final FacesContext facesContext = FacesContext.getCurrentInstance();
        if (facesContext == null) {
            return null;
        }

        HttpServletResponse response = (HttpServletResponse) facesContext.getExternalContext().getResponse();
        HttpServletRequest request = (HttpServletRequest) facesContext.getExternalContext().getRequest();

        String url = BaseURL.getBaseURL(request);
        url += "restAPI/seamReload";

        try {
            response.resetBuffer();
            response.sendRedirect(url);
            response.flushBuffer();
            request.setAttribute(NXAuthConstants.DISABLE_REDIRECT_REQUEST_KEY, Boolean.TRUE);
            facesContext.responseComplete();
        } catch (IOException e) {
            log.error("Error during redirect", e);
        }
        return null;
    }

}