/* * Copyright 2017 OmniFaces * * 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.omnifaces.cdi; import static java.lang.annotation.ElementType.TYPE; import static java.lang.annotation.RetentionPolicy.RUNTIME; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.Target; import javax.enterprise.context.ApplicationScoped; import javax.enterprise.context.RequestScoped; import javax.enterprise.context.SessionScoped; import javax.enterprise.util.Nonbinding; import javax.faces.context.FacesContext; import javax.faces.event.PhaseId; import javax.faces.webapp.FacesServlet; import javax.servlet.ServletContext; import javax.servlet.ServletRequestListener; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; import javax.servlet.http.HttpSessionListener; import org.omnifaces.cdi.eager.EagerBeansFilter; import org.omnifaces.cdi.eager.EagerBeansPhaseListener; import org.omnifaces.cdi.eager.EagerBeansRepository; import org.omnifaces.cdi.eager.EagerBeansWebListener; import org.omnifaces.cdi.eager.EagerExtension; /** * <p> * The CDI annotation <code>@</code>{@link Eager} specifies that a scoped bean is to be eagerly instantiated. * <p> * JSF's own native managed beans are being deprecated in favor of CDI managed beans. One feature that those native JSF * managed beans had that's not directly available for CDI managed beans is the ability to eagerly instantiate * application scoped beans. * <p> * OmniFaces fills this void and even goes one step further by introducing the <code>@Eager</code> annotation * that can be applied to <code>@RequestScoped</code>, <code>@ViewScoped</code>, * <code>@SessionScoped</code> and <code>@ApplicationScoped</code> beans. This causes these beans to be instantiated * automatically at the start of each such scope instead of on demand when a bean is first referenced. * <p> * In case of <code>@RequestScoped</code> and <code>@ViewScoped</code> beans instantiation happens per request URI / view * and an extra attribute is required for specifying this. * * <h3>Supported scopes</h3> * * <p> * Currently supported scopes: * <ol> * <li> CDI {@link RequestScoped} * <li> CDI {@link javax.faces.view.ViewScoped} * <li> OmniFaces {@link ViewScoped} * <li> CDI {@link SessionScoped} * <li> CDI {@link ApplicationScoped} * </ol> * * <h3>Usage</h3> * * <p> * E.g. * The following bean will be instantiated during application's startup: * <pre> * @Eager * @ApplicationScoped * public class MyEagerApplicationScopedBean { * * @PostConstruct * public void init() { * System.out.println("Application scoped init!"); * } * } * </pre> * <p> * <em>Note: you can also use the stereotype <code>@</code>{@link Startup} for this instead.</em> * <p> * The following bean will be instantiated whenever a session is created: * <pre> * @Eager * @SessionScoped * public class MyEagerSessionScopedBean implements Serializable { * * private static final long serialVersionUID = 1L; * * @PostConstruct * public void init() { * System.out.println("Session scoped init!"); * } * } * </pre> * <p> * The following bean will be instantiated whenever the URI <code>/components/cache</code> (relatively to the * application root) is requested, i.e. when an app is deployed to <code>/myapp</code> at localhost this will correspond to * a URL like <code>http://localhost:8080/myapp/components/cache</code>: * <pre> * @Eager(requestURI = "/components/cache") * @RequestScoped * public class MyEagerRequestScopedBean { * * @PostConstruct * public void init() { * System.out.println("/components/cache requested"); * } * } * </pre> * * <h3><code>FacesContext</code> in <code>@PostConstruct</code></h3> * * <p> * When using <code>@Eager</code> or <code>@Eager(requestURI)</code>, be aware that the {@link FacesContext} is * <strong>not</strong> available in the <code>@PostConstruct</code>. Reason is, the {@link FacesServlet} isn't * invoked yet at the moment <code>@Eager</code> bean is constructed. Only in <code>@Eager(viewId)</code>, the * {@link FacesContext} is available in the <code>@PostConstruct</code>. * <p> * In case you need information from {@link HttpServletRequest}, {@link HttpSession} and/or {@link ServletContext}, then * you can just <code>@Inject</code> it right away. Also, all other CDI managed beans are available the usual way * via <code>@Inject</code>, as long as they do also not depend on {@link FacesContext} in their * <code>@PostConstruct</code>. * * <h3>Compatibility</h3> * * <p> * In some (older) containers, most notably GlassFish 3, the CDI request scope is not available in a {@link ServletRequestListener} * (this is actually not spec complicant, as CDI demands this scope to be active then, but it is what it is). * <p> * Additionally in some containers, most notably GlassFish 3 again, instantiating session scoped beans from a {@link HttpSessionListener} * will corrupt "something" in the container. The instantiating of the beans will succeed, but if the session is later accessed an * exception like the following will be thrown: * * <pre> * java.lang.IllegalArgumentException: Should never reach here * at org.apache.catalina.connector.SessionTracker.track(SessionTracker.java:168) * at org.apache.catalina.connector.Request.doGetSession(Request.java:2939) * at org.apache.catalina.connector.Request.getSession(Request.java:2583) * at org.apache.catalina.connector.RequestFacade.getSession(RequestFacade.java:920) * at javax.servlet.http.HttpServletRequestWrapper.getSession(HttpServletRequestWrapper.java:259) * at com.sun.faces.context.ExternalContextImpl.getSession(ExternalContextImpl.java:155) * at javax.faces.context.ExternalContextWrapper.getSession(ExternalContextWrapper.java:396) * at javax.faces.context.ExternalContextWrapper.getSession(ExternalContextWrapper.java:396) * ... * </pre> * * If any or both of those problems occur, a filter needs to be installed instead in <code>web.xml</code> as follows: * * <pre> * <filter> * <filter-name>eagerBeansFilter</filter-name> * <filter-class>org.omnifaces.cdi.eager.EagerBeansFilter</filter-class> * </filter> * <filter-mapping> * <filter-name>eagerBeansFilter</filter-name> * <url-pattern>/*</url-pattern> * </filter-mapping> *</pre> * * <p> * Note that the {@link EagerBeansFilter} will automatically disable the {@link EagerBeansWebListener}. * * * @since 1.8 * @author Arjan Tijms * @see EagerExtension * @see EagerBeansRepository * @see EagerBeansPhaseListener * @see EagerBeansWebListener * @see EagerBeansFilter * */ @Documented @Retention(RUNTIME) @Target(TYPE) public @interface Eager { /** * (Required when combined with {@link RequestScoped}) The URI (relative to the root of the web app) for which a * request scoped bean should be instantiated. When this attribute is specified the bean will be instantiated very * early during request processing, namely just before the first Servlet Filter is invoked, but after a SAM. * <p> * JSF services will not be available (yet) when the bean is instantiated this way. * <p> * If both this attribute and {@link Eager#viewId()} is specified, this attribute takes precedence for {@link RequestScoped}. * This attribute <b>can not</b> be used for <code>ViewScoped</code> beans. * * @return The request URI relative to the context root. */ @Nonbinding String requestURI() default ""; /** * (Required when combined with {@link RequestScoped} or <code>ViewScoped</code>) The id of the view for which a request or view scoped bean * should be instantiated. When this attribute is specified the bean will be instantiated during invocation of the * {@link FacesServlet}, namely right after the RESTORE_VIEW phase (see {@link PhaseId#RESTORE_VIEW}). * * <p> * JSF services are available when the bean is instantiated this way. * * <p> * If both this attribute and {@link Eager#requestURI()} is specified and the scope is {@link RequestScoped}, the * <code>requestURI</code> attribute takes precedence. If the scope is <code>ViewScoped</code> <code>requestURI</code> is ignored and only * this attribute is considered. * * @return The view ID. */ @Nonbinding String viewId() default ""; }