001 /*******************************************************************************
002 * Copyright (C) PicoContainer Organization. All rights reserved.
003 * ---------------------------------------------------------------------------
004 * The software in this package is published under the terms of the BSD style
005 * license a copy of which has been included with this distribution in the
006 * LICENSE.txt file.
007 ******************************************************************************/
008 package org.picocontainer.web.jsf;
009
010 import java.util.Map;
011
012 import javax.faces.context.FacesContext;
013 import javax.faces.el.EvaluationException;
014 import javax.faces.el.VariableResolver;
015
016 import org.picocontainer.PicoContainer;
017 import org.picocontainer.MutablePicoContainer;
018 import org.picocontainer.web.PicoServletContainerFilter;
019
020 /**
021 * This is a variable resolver implementation for Java ServerFaces.
022 * <h2>Installation</h2>
023 * <p>
024 * Add install this variable resolver by adding setting the application's
025 * variable resolver to
026 * <em>org.picocontainer.web.jsf.NanoWarDelegatingVariableResolver</em>. An
027 * example follows:
028 * </p>
029 * <hr/>
030 *
031 * <pre>
032 * <faces-config>
033 * <application>
034 * <strong>
035 * <variable-resolver>
036 * org.picocontainer.web.jsf.NanoWarDelegatingVariableResolver
037 * </variable-resolver>
038 * </strong>
039 * </application>
040 * ....
041 * </faces-config>
042 * </pre>
043 *
044 * <hr/>
045 * <h2>Usage</h2>
046 * <h4>Part 1 - Write your Constructor Dependency Injection (CDI) - based
047 * backing bean:</h4>
048 * <p>
049 * Even though you are writing a backing bean, you can utilize PicoContainers
050 * CDI capabilities to the fullest. Example:
051 * </p>
052 *
053 * <pre>
054 * //Imports and variables...
055 *
056 * public ListCheeseController(<strong>CheeseService service</strong>) {
057 * this.service = service;
058 * }
059 *
060 * //The rest of the class.
061 * </pre>
062 *
063 * <h4>Part 2 - Set up your NanoWAR services.</h4>
064 * <p>
065 * (This assumes you have installed NanoWAR properly. Please see the NanoWAR
066 * documentation for specific instructions)
067 * </p>
068 * <p>
069 * You need to name your services with the name you will be giving your
070 * <tt>Backing Bean</tt>. Example:
071 *
072 * <pre>
073 * pico = builder.container(parent: parent) {
074 * if(assemblyScope instanceof javax.servlet.ServletContext) {
075 * // Application Services would go here.
076 * } else if (assemblyScope instanceof javax.servlet.ServletRequest) {
077 * <strong>addComponent(key: 'cheeseBean', class: 'org.picocontainer.web.samples.jsf.ListCheeseController')</strong>
078 * }
079 * }
080 * </pre>
081 *
082 * <h4>Part 3 - Set up your managed beans for JSF</h4>
083 * <p>
084 * Set the managed bean names in your <tt>faces-config</tt> to equal the names
085 * given to the backing beans in the nanowar composition script. Example:
086 * </p>
087 *
088 * <pre>
089 * <managed-bean>
090 * <description>CDI Injected Bean</description>
091 * <strong><managed-bean-name>cheeseBean</managed-bean-name></strong>
092 * <managed-bean-class>
093 * org.picocontainer.web.samples.jsf.CheeseController
094 * </managed-bean-class>
095 * <managed-bean-scope>request</managed-bean-scope>
096 * </managed-bean>
097 * </pre>
098 *
099 * <p>
100 * Notice how the same names were used in the <tt>faces-config</tt> as in the
101 * nanowar configuration. When the JSF page asks for the bean named
102 * 'addCheeseBean', the Nano variable resolver will take that name and check
103 * nanocontainer for an object of that instance. If it finds one, it will send
104 * it back to the page.
105 * </p>
106 * <em>Note:</em>
107 * <p>
108 * This class currently has only been tested using MyFaces. There are reports
109 * that this technique doesn't work on all reference implementation versions. We
110 * welcome success or failure feedback!
111 * </p>
112 *
113 * @author Michael Rimov
114 */
115 public class PicoVariableResolver extends VariableResolver {
116
117 public static class ServletFilter extends PicoServletContainerFilter {
118 private static ThreadLocal<MutablePicoContainer> currentRequestContainer = new ThreadLocal<MutablePicoContainer>();
119 private static ThreadLocal<MutablePicoContainer> currentSessionContainer = new ThreadLocal<MutablePicoContainer>();
120 private static ThreadLocal<MutablePicoContainer> currentAppContainer = new ThreadLocal<MutablePicoContainer>();
121
122 protected void setAppContainer(MutablePicoContainer container) {
123 currentAppContainer.set(container);
124 }
125 protected void setRequestContainer(MutablePicoContainer container) {
126 currentRequestContainer.set(container);
127 }
128 protected void setSessionContainer(MutablePicoContainer container) {
129 currentSessionContainer.set(container);
130 }
131
132 protected static MutablePicoContainer getRequestContainerForThread() {
133 return currentRequestContainer.get();
134 }
135 protected static MutablePicoContainer getSessionContainerForThread() {
136 return currentSessionContainer.get();
137 }
138 protected static MutablePicoContainer getApplicationContainerForThread() {
139 return currentAppContainer.get();
140 }
141
142 }
143 /**
144 * The nested variable resolver.
145 */
146 private VariableResolver nested;
147
148 /**
149 * Decorated Variable resolver.
150 *
151 * @param decorated
152 */
153 public PicoVariableResolver(VariableResolver decorated) {
154 super();
155 if (decorated == null) {
156 throw new NullPointerException("decorated");
157 }
158 nested = decorated;
159 }
160
161 /**
162 * Retrieve the delegated value.
163 *
164 * @return the wrapped variable resolver.
165 */
166 protected VariableResolver getNested() {
167 return nested;
168 }
169
170 /**
171 * {@inheritDoc}
172 *
173 * @param facesContext
174 * @param name
175 * @return the resulting object, either resolved through NanoWAR, or passed
176 * onto the delegate resolver.
177 * @throws EvaluationException
178 * @see javax.faces.el.VariableResolver#resolveVariable(javax.faces.context.FacesContext,
179 * java.lang.String)
180 */
181 public Object resolveVariable(FacesContext facesContext, String name) {
182
183 PicoContainer nano = getPicoContainer(facesContext);
184
185 Object result = nano.getComponent(name);
186 if (result == null) {
187 return nested.resolveVariable(facesContext, name);
188 }
189
190 return result;
191 }
192
193 /**
194 * Tries to locate the nanocontainer first at request level, and then if it
195 * doesn't find it there. (Filter might not be installed), it tries
196 * Application level. If that fails it throws an exception since you
197 * wouldn't expect the NanoWarDelegatingVariableResolver
198 *
199 * @param facesContext
200 * @return NanoContainer instance.
201 * @throws EvaluationException if it cannot find a NanoWAR instance.
202 */
203 protected PicoContainer getPicoContainer(FacesContext facesContext) {
204 Map requestAttributeMap = facesContext.getExternalContext().getRequestMap();
205
206 PicoContainer container = null;
207
208 // First check request map.
209 if (requestAttributeMap != null) {
210 container = ServletFilter.getRequestContainerForThread();
211 }
212
213 if (requestAttributeMap == null || container == null) {
214
215 // If that fails, check session for container.
216 Map sessionMap = facesContext.getExternalContext().getSessionMap();
217 if (sessionMap != null) {
218 // If there is a session.
219 container = ServletFilter.getSessionContainerForThread();
220 }
221
222 if (sessionMap == null || container == null) {
223
224 // If that fails, check for App level container.
225 container = ServletFilter.getApplicationContainerForThread();
226 if (container == null) {
227 // If that fails... Fail.
228 throw new EvaluationException(
229 "The NanoWar delegating variable resolver is installed, however no NanoWar "
230 + "container was found in the request or application scope.");
231 }
232 }
233 }
234
235 return container;
236 }
237
238 }