1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 package org.roscopeco.janno.core;
23
24 import javax.servlet.ServletContext;
25 import javax.servlet.ServletRequest;
26 import javax.servlet.http.HttpServletRequest;
27 import javax.servlet.http.HttpSession;
28
29 import java.io.File;
30
31 import org.apache.commons.logging.Log;
32 import org.apache.commons.logging.LogFactory;
33 import org.codehaus.classworlds.ClassRealm;
34 import org.codehaus.classworlds.ClassWorld;
35 import org.codehaus.classworlds.DuplicateRealmException;
36 import org.nanocontainer.integrationkit.ContainerBuilder;
37 import org.picocontainer.PicoContainer;
38 import org.picocontainer.defaults.DefaultPicoContainer;
39
40 import org.roscopeco.moxy.builders.MoxyContainerBuilderFactory;
41 import org.roscopeco.moxy.core.MoxyContext;
42 import org.roscopeco.moxy.defaults.DefaultMoxyContext;
43 import org.roscopeco.moxy.util.BuilderUtils;
44 import org.roscopeco.moxy.util.ContextWalker;
45 import org.roscopeco.moxy.util.PicoContainerStack;
46 import org.roscopeco.moxy.util.PicoPathWalker;
47
48 import static org.roscopeco.janno.core.CoreConstants.*;
49 import static org.roscopeco.janno.core.JannoLifecycleListener.warnOnce;
50
51 /***
52 * Internal stateless static class with lifecycle management methods.
53 * This class has no instances and no non-final or dynamic fields.
54 * It does hold a shared <code>static final</code> commons logger,
55 * obtained during <code><clinit></code>. It uses the logger
56 * defined for the {@link JannoLifecycleListener} class.
57 *
58 * @author Ross Bamford (rosco<at>roscopeco.co.uk)
59 * @version $Revision: 1.10 $ $Date: 2005/08/23 14:20:13 $
60 */
61
62 final class LifecycleManager
63 {
64 static Log log = LogFactory.getLog(JannoLifecycleListener.class);
65 static final boolean logRequests = (System.getProperty(DEBUG_LOG_REQUESTS) != null) &&
66 (System.getProperty(DEBUG_LOG_REQUESTS).toLowerCase().equals("true"));
67
68
69
70
71 static void applicationSetup(ServletContext ctx) throws DuplicateRealmException {
72 if (log.isDebugEnabled())
73 log.debug("Starting application setup with ServletContext "+ctx.getClass().getName()+"@"+System.identityHashCode(ctx)+" '"+ctx.getServletContextName()+"'");
74
75 PicoContainer p = buildAndStartApplicationContext(ctx,buildAndStartServerTree(ctx));
76 ctx.setAttribute(CONTEXT_KEY,p);
77
78 if (log.isDebugEnabled()) {
79 log.debug("Application container is "+p.getClass().getName()+"@"+System.identityHashCode(p));
80 log.debug("Application setup complete (ServletContext "+ctx.getClass().getName()+"@"+System.identityHashCode(ctx)+" '"+ctx.getServletContextName()+"')");
81 }
82 }
83
84 static void applicationCleanup(ServletContext ctx) {
85
86 if (log.isDebugEnabled())
87 log.debug("Starting application cleanup with ServletContext "+ctx.getClass().getName()+"@"+System.identityHashCode(ctx)+" '"+ctx.getServletContextName()+"'");
88 PicoContainer p = (PicoContainer)ctx.getAttribute(CONTEXT_KEY);
89 if (p != null) {
90 p.stop();
91 p.dispose();
92 ctx.removeAttribute(CONTEXT_KEY);
93 } else {
94 log.warn("Application context has no container?");
95 }
96 if (log.isDebugEnabled())
97 log.debug("Application cleanup complete (ServletContext "+ctx.getClass().getName()+"@"+System.identityHashCode(ctx)+" '"+ctx.getServletContextName()+"')");
98 }
99
100 static void sessionSetup(HttpSession sess) {
101 if (log.isDebugEnabled())
102 log.debug("Starting session setup with HttpSession "+sess.getClass().getName()+"@"+System.identityHashCode(sess)+" '"+sess.getId()+"'");
103
104 PicoContainer parent =
105 (PicoContainer)sess.getServletContext().getAttribute(CONTEXT_KEY);
106 if (parent == null) {
107 Exception e = new MissingContextException(CONTEXT_KEY);
108 log.error("Missing application context",e);
109 throw(new RuntimeException(e));
110 }
111
112
113 ContainerBuilder b = (ContainerBuilder)parent.getComponentInstance(SESSION_BUILDER);
114 if (b == null) warnOnce("No Session-scoped builder; using default container");
115
116 if (log.isDebugEnabled())
117 log.debug("Will use parent "+parent.getClass().getName()+"@"+System.identityHashCode(parent));
118 PicoContainer p = null;
119 if (b != null) {
120 if (log.isDebugEnabled())
121 log.debug("Building session container "+b.getClass().getName()+"@"+System.identityHashCode(b));
122 p = BuilderUtils.buildContainer(b,parent,sess);
123 } else {
124 log.debug("Building DEFAULT session container");
125 p = new DefaultPicoContainer(parent);
126 }
127
128 log.debug("STARTING SESSION CONTAINER");
129 p.start();
130 sess.setAttribute(SESSION_KEY,p);
131 if (log.isDebugEnabled()) {
132 log.debug("Session container is "+p.getClass().getName()+"@"+System.identityHashCode(p));
133 log.debug("Session setup complete (HttpSession "+sess.getClass().getName()+"@"+System.identityHashCode(sess)+" '"+sess.getId()+"')");
134 }
135 }
136
137 static void sessionCleanup(HttpSession sess) {
138 if (log.isDebugEnabled())
139 log.debug("Starting session cleanup with HttpSession "+sess.getClass().getName()+"@"+System.identityHashCode(sess)+" '"+sess.getId()+"'");
140
141 PicoContainer p = (PicoContainer)sess.getAttribute(SESSION_KEY);
142 if (p != null) {
143 if (log.isDebugEnabled())
144 log.debug("Found session container "+p.getClass().getName()+"@"+System.identityHashCode(p));
145 log.debug("STOPPING SESSION CONTAINER");
146 p.stop();
147 p.dispose();
148 sess.removeAttribute(SESSION_KEY);
149 log.debug("Container removed from session");
150 } else {
151 log.warn("Session (ID "+sess.getId()+") has no container?");
152 }
153
154 if (log.isDebugEnabled()) {
155 log.debug("Session cleanup complete (HttpSession "+sess.getClass().getName()+"@"+System.identityHashCode(sess)+" '"+sess.getId()+"')");
156 }
157 }
158
159 static final void requestSetup(ServletRequest req, ServletContext app) {
160 if (log.isDebugEnabled())
161 log.debug("Starting request setup with ServletRequest "+req.getClass().getName()+"@"+System.identityHashCode(req)+" '"+req.getRemoteAddr()+"'");
162
163
164 PicoContainer parent = (PicoContainer)app.getAttribute(CONTEXT_KEY);
165 if (parent == null) {
166 Exception e = new MissingContextException(CONTEXT_KEY);
167 log.error("Missing application context",e);
168 throw(new RuntimeException(e));
169 }
170
171 if (req instanceof HttpServletRequest) {
172 HttpSession sess = ((HttpServletRequest)req).getSession(false);
173 if (sess != null) {
174 PicoContainer t = (PicoContainer)sess.getAttribute(SESSION_KEY);
175 if (t != null) {
176 parent = t;
177 if (log.isDebugEnabled())
178 log.debug("+ Found session container "+parent.getClass().getName()+"@"+System.identityHashCode(parent));
179 }
180 }
181 }
182
183 if (log.isDebugEnabled()) {
184 log.debug("Will use parent "+parent.getClass().getName()+"@"+System.identityHashCode(parent));
185 log.debug("Making new request stack...");
186 }
187
188
189 PicoContainerStack rs = new PicoContainerStack(parent);
190
191
192 ContainerBuilder b = (ContainerBuilder)parent.getComponentInstance(REQUEST_BUILDER);
193 if (b == null) warnOnce("No Request-scoped builder; using default container");
194
195
196 if (b == null) {
197 log.debug("Pushing new empty container");
198 rs.pushNew();
199 } else {
200 log.debug("Building request container "+b.getClass().getName()+"@"+System.identityHashCode(b));
201 rs.pushNew(b,req);
202 }
203
204 if (log.isDebugEnabled())
205 log.debug("Stack initialized ("+rs.getClass().getName()+"@"+System.identityHashCode(rs)+"; with "+rs.size()+" container(s)");
206
207 log.debug("STARTING REQUEST STACK");
208 rs.start();
209 req.setAttribute(REQUEST_KEY,rs);
210
211 if (log.isDebugEnabled()) {
212 log.debug("Started and registered");
213 log.debug("Request setup complete (ServletRequest "+req.getClass().getName()+"@"+System.identityHashCode(req)+" '"+req.getRemoteAddr()+"')");
214 }
215 }
216
217 static final void requestCleanup(ServletRequest req) {
218 if (log.isDebugEnabled())
219 log.debug("Starting request cleanup (ServletRequest "+req.getClass().getName()+"@"+System.identityHashCode(req)+" '"+req.getRemoteAddr()+"')");
220
221 PicoContainerStack p = (PicoContainerStack)req.getAttribute(REQUEST_KEY);
222 if (p != null) {
223 if (log.isDebugEnabled())
224 log.debug("Found request container "+p.getClass().getName()+"@"+System.identityHashCode(p));
225 log.debug("STOPPING REQUEST STACK");
226 p.stop();
227 p.dispose();
228 req.removeAttribute(REQUEST_KEY);
229 } else {
230 log.warn("Request (ID "+req.toString()+") has no container?");
231 }
232
233 if (log.isDebugEnabled()) {
234 log.debug("Request cleanup complete (ServletRequest "+req.getClass().getName()+"@"+System.identityHashCode(req)+" '"+req.getRemoteAddr()+"')");
235 }
236 }
237
238
239
240
241
242 private static final MoxyContext buildAndStartServerTree(ServletContext ctx) {
243 ClassRealm sysRealm;
244 MoxyContext server;
245
246 log.info("Initialising Server context");
247
248
249 try {
250
251
252 sysRealm = new ClassWorld().newRealm("server",LifecycleManager.class.getClassLoader());
253 server = new DefaultMoxyContext(ROOT_NAME,sysRealm);
254
255
256 server.registerComponentInstance(ContextWalker.class,new PicoPathWalker(server));
257 server.registerComponentInstance(CoreConfig.class,
258 new CoreConfigImpl(new File(ctx.getRealPath("/")),(File)null,(File)null,ctx,server,
259 LogFactory.getLog("Janno: Runtime core")));
260
261 MoxyContext mod = doModuleContextBuildNoStart(ctx,server);
262 if (mod == null) {}
263
264 log.debug("Starting context tree...");
265 server.start();
266 log.debug("Started");
267 return server;
268
269 } catch (DuplicateRealmException e) {
270 throw(new InternalError(e+" with new world"));
271 }
272 }
273
274
275
276
277 private static final MoxyContext buildAndStartApplicationContext(ServletContext ctx, MoxyContext parent)
278 throws DuplicateRealmException, MissingParameterException {
279 log.info("Building Application context");
280 String script = ctx.getInitParameter(APP_SCRIPT_PARAM);
281 if (script == null) throw(new MissingParameterException(APP_SCRIPT_PARAM));
282 log.debug("Using script: "+script);
283 MoxyContext ctxt = doCompositionFromScript(ctx,parent,script);
284 ctxt.start();
285 return ctxt;
286 }
287
288
289
290
291
292 private static final MoxyContext doModuleContextBuildNoStart(
293 ServletContext ctx, MoxyContext parent)
294 throws DuplicateRealmException, MissingParameterException {
295 log.info("Building Module context");
296 String script = ctx.getInitParameter(MODULE_SCRIPT_PARAM);
297 if (script == null) throw(new MissingParameterException(MODULE_SCRIPT_PARAM));
298 log.debug("Using script: "+script);
299 return doCompositionFromScript(ctx,parent,script);
300 }
301
302
303 private static final MoxyContext doCompositionFromScript(
304 ServletContext ctx, MoxyContext parent, String script) {
305 MoxyContext c;
306 try {
307 c = (MoxyContext)MoxyContainerBuilderFactory.
308 buildContainer(ctx.getResource(script),parent,ctx);
309 } catch (ClassCastException e) {
310 throw(new FatalStartupException("Composition \""+script+"\" returns non-MoxyContext"));
311 } catch (Exception e) {
312 throw(new FatalStartupException("Exception in composition \""+script+"\"",e));
313 }
314 return c;
315 }
316 }