1 /*
2 * Angkor Web Framework
3 *
4 * Distributable under LGPL license.
5 * See terms of license at gnu.org.
6 */
7 package com.tirsen.angkor;
8
9 import com.tirsen.angkor.jsp.PrerenderValve;
10 import com.tirsen.angkor.process.DefaultPipeline;
11 import com.tirsen.angkor.process.ErrorValve;
12 import com.tirsen.angkor.process.EventValve;
13 import com.tirsen.angkor.process.ExecuteContext;
14 import com.tirsen.angkor.process.ParseValve;
15 import com.tirsen.angkor.process.Pipeline;
16 import com.tirsen.angkor.process.RedirectValve;
17 import com.tirsen.angkor.process.SingleViewFactoryRenderValve;
18 import org.apache.commons.logging.Log;
19 import org.apache.commons.logging.LogFactory;
20
21 import javax.servlet.http.HttpSessionBindingEvent;
22 import javax.servlet.http.HttpSessionBindingListener;
23 import javax.servlet.http.HttpServletRequest;
24 import javax.servlet.http.HttpServletResponse;
25 import java.beans.PropertyEditorManager;
26 import java.io.IOException;
27 import java.io.Serializable;
28 import java.util.ArrayList;
29 import java.util.Collection;
30 import java.util.Collections;
31 import java.util.HashMap;
32 import java.util.Iterator;
33 import java.util.LinkedList;
34 import java.util.Map;
35 import java.util.WeakHashMap;
36
37 /***
38 * The main controller of an application, typically first class extended when implementing
39 * an application in Angkor.
40 *
41 * There is one instance of this for each session.
42 *
43 * Responsibilities include:
44 * <li> maintaining conversational state.
45 * <li> implementing core functionality shared by the entire application.
46 * <li> maintaining the flow of application.
47 * <li> maintaing a register of named components and their respective ViewFactory.
48 *
49 * which is to be used if using JSP in Angkor. Includes window-title.
50 *
51 * <!-- $Id: Application.java,v 1.7 2002/10/13 19:59:22 tirsen Exp $ -->
52 *
53 * @author $Author: tirsen $
54 * @version $Revision: 1.7 $
55 */
56 public class Application implements Serializable, HttpSessionBindingListener
57 {
58 private static final Log logger = LogFactory.getLog(Application.class);
59
60 private Map components = new HashMap();
61 private Throwable error;
62 private Map viewToID = new WeakHashMap();
63 private Collection parsingComponents;
64 private long currentId = 0;
65
66 public Throwable getError()
67 {
68 return error;
69 }
70
71 public void handleError(Throwable e)
72 {
73 error = e;
74 logger.error(e);
75 }
76
77 public ViewFactory getComponent(String name)
78 {
79 Object component = components.get(name);
80 if (component == null) throw new IllegalArgumentException("No such component in this application: " + name);
81 ViewFactory factory;
82 if (component instanceof ViewLink)
83 {
84 factory = ((ViewLink) component).getTarget();
85 }
86 else
87 {
88 factory = (ViewFactory) component;
89 }
90 return factory;
91 }
92
93 /***
94 * Registers a <em>new</em> component.
95 * @throws IllegalStateException if that component has already been registered.
96 */
97 public void registerComponent(String name, ViewFactory factory)
98 {
99 if (components.containsKey(name)) throw new IllegalStateException(name + " has already been registered.");
100 components.put(name, factory);
101 }
102
103 /***
104 * Registers a <em>new</em> component.
105 * @throws IllegalStateException if that component has already been registered.
106 */
107 public void registerPrerenderComponent(String name, View component)
108 {
109 if (components.containsKey(name)) throw new IllegalStateException(name + " has already been registered.");
110 components.put(name, component);
111 }
112
113 public void registerLink(String name, ViewFactory originalBinding)
114 {
115 registerComponent(name, new ViewLink(originalBinding));
116 }
117
118 /***
119 * Relinks a link.
120 */
121 public void relink(String name, ViewFactory target)
122 {
123 ViewLink link = (ViewLink) components.get(name);
124 if (link == null) throw new IllegalArgumentException("No such link " + name);
125 link.relink(target);
126 }
127
128 /***
129 * Relinks a link to a named view.
130 */
131 public void relink(String name, String target)
132 {
133 relink(name, getComponent(target));
134 }
135
136 /***
137 * Overriding method needs to call this method.
138 */
139 public void init()
140 {
141 initDefaultPropertyEditors();
142 initPrerenderedComponents();
143 }
144
145 public void destroy()
146 {
147 // do nothing by default
148 }
149
150 public void valueBound(HttpSessionBindingEvent event)
151 {
152 init();
153 }
154
155 public void valueUnbound(HttpSessionBindingEvent event)
156 {
157 destroy();
158 }
159
160 protected void initPrerenderedComponents()
161 {
162 // none by default
163 }
164
165 protected void initDefaultPropertyEditors()
166 {
167 // set up some obvious editors which should have been setup by the JDK but are not
168 PropertyEditorManager.registerEditor(Integer.class, PropertyEditorManager.findEditor(Integer.TYPE).getClass());
169 PropertyEditorManager.registerEditor(Double.class, PropertyEditorManager.findEditor(Double.TYPE).getClass());
170 }
171
172 public void beforeRequest(RenderContext context)
173 throws IOException
174 {
175 }
176
177 public void afterRequest(RenderContext context)
178 throws IOException
179 {
180 error = null;
181 }
182
183 /***
184 * Creates the default processing pipeline. This will probably be refactored a lot.
185 * Which factory to use to produce the view to render needs to be determined beforehand.
186 */
187 public Pipeline createRenderPipeline(ViewFactory factory)
188 {
189 Pipeline process = createBasicPipeline();
190 process.addValve(new SingleViewFactoryRenderValve(factory));
191 return process;
192 }
193
194 /***
195 * Creates a pipeline that prerenders all currently registered components.
196 * These prerendered components can be used in for example JSP.
197 */
198 public Pipeline createPrerenderPipeline()
199 {
200 Pipeline pipeline = createBasicPipeline();
201 pipeline.addValve(new PrerenderValve());
202 return pipeline;
203 }
204
205 /***
206 * Creates the basic pipeline with the exception of a render valve.
207 */
208 public Pipeline createBasicPipeline()
209 {
210 Pipeline process = createDefaultPipeline();
211 process.addValve(new ParseValve());
212 process.addValve(new EventValve());
213 process.addValve(new RedirectValve());
214 return process;
215 }
216
217 protected Pipeline createDefaultPipeline()
218 {
219 DefaultPipeline pipeline = new DefaultPipeline();
220 pipeline.addValve(new ErrorValve());
221 return pipeline;
222 }
223
224 public void resetParsingComponents()
225 {
226 parsingComponents = null;
227 }
228
229 /***
230 * Returns a tread-safe iterator of the parsing components. Parsing components can be registered
231 * while using the returned iterator, the components registered after this iterator has been created
232 * will not be iterated by this iterator.
233 */
234 public Iterator iterateParsingComponents()
235 {
236 return parsingComponents == null ? Collections.EMPTY_LIST.iterator() : new ArrayList(parsingComponents).iterator();
237 }
238
239 /***
240 * Public for unit tests only, maybe it's time to move to JUnitX soon.
241 * TODO move this to the ExecuteContext.
242 */
243 public Collection getParsingComponents()
244 {
245 return parsingComponents;
246 }
247
248 public void registerParsingComponent(View view)
249 {
250 if (parsingComponents == null) parsingComponents = new LinkedList();
251 parsingComponents.add(view);
252 }
253
254 public String nextUniqueID()
255 {
256 currentId++;
257 return String.valueOf(currentId);
258 }
259
260 public void allocateUniqueID(String id, View view)
261 {
262 // check uniqueness of id
263 if (viewToID.containsValue(id) && !id.equals(viewToID.get(view)))
264 {
265 throw new IllegalArgumentException("ID already allocated: " + id);
266 }
267 else
268 viewToID.put(view, id);
269 }
270
271 /***
272 * This is used by the {@link PrerenderValve} and is not part of the public API.
273 * @return a <code>String</code> -> <code>ViewFactory</code> map containing the components to prerender.
274 */
275 public Map getComponentsToPrerender() throws Exception
276 {
277 return components;
278 }
279
280 /***
281 * Returns the application of the currently executing pipeline.
282 */
283 public static Application getApplication()
284 {
285 return (Application) Pipeline.getCurrentExecuteContext().getAttribute(ExecuteContext.ApplicationAttribute);
286 }
287
288 /***
289 * Override to provide a title rendered by the angkor:header-tag.
290 */
291 public String getWindowTitle()
292 {
293 return null;
294 }
295
296 public void registerPage(String name, Page page)
297 {
298 if (components.containsKey(name) && components.get(name) != page)
299 {
300 throw new IllegalStateException(name + " has already been registered.");
301 }
302 if(page.getApplication() != null && page.getApplication() != this)
303 {
304 throw new IllegalArgumentException("Page can only be registered with one application.");
305 }
306 page.setApplication(this);
307 components.put(name, page);
308 }
309
310 public void process(HttpServletRequest request, HttpServletResponse response) throws Exception
311 {
312 RenderContext renderContext = new RenderContext(request, response);
313
314 beforeRequest(renderContext);
315
316 String pageName = extractPageName(request);
317 Page page = getPage(pageName);
318
319 Pipeline pipeline = page.createProcessPipeline();
320 pipeline.execute(renderContext);
321
322 afterRequest(renderContext);
323 }
324
325 public Page getPage(String pageName)
326 {
327 Page page = (Page) components.get(pageName);
328 if(page == null)
329 {
330 throw new IllegalArgumentException("No such page " + pageName + " exists.");
331 }
332 return page;
333 }
334
335 private String extractPageName(HttpServletRequest request)
336 {
337 String requestURI = request.getRequestURI();
338 return requestURI.substring(requestURI.lastIndexOf('/') + 1);
339 }
340 }
This page was automatically generated by Maven