Source code: nextapp/echoservlet/Initializer.java
1 /*
2 * This file is part of the Echo Web Application Framework (hereinafter "Echo").
3 * Copyright (C) 2002-2004 NextApp, Inc.
4 *
5 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 *
7 * The contents of this file are subject to the Mozilla Public License Version
8 * 1.1 (the "License"); you may not use this file except in compliance with
9 * the License. You may obtain a copy of the License at
10 * http://www.mozilla.org/MPL/
11 *
12 * Software distributed under the License is distributed on an "AS IS" basis,
13 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 * for the specific language governing rights and limitations under the
15 * License.
16 *
17 * Alternatively, the contents of this file may be used under the terms of
18 * either the GNU General Public License Version 2 or later (the "GPL"), or
19 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
20 * in which case the provisions of the GPL or the LGPL are applicable instead
21 * of those above. If you wish to allow use of your version of this file only
22 * under the terms of either the GPL or the LGPL, and not to allow others to
23 * use your version of this file under the terms of the MPL, indicate your
24 * decision by deleting the provisions above and replace them with the notice
25 * and other provisions required by the GPL or the LGPL. If you do not delete
26 * the provisions above, a recipient may use your version of this file under
27 * the terms of any one of the MPL, the GPL or the LGPL.
28 */
29
30 package nextapp.echoservlet;
31
32 import java.io.IOException;
33 import java.io.Serializable;
34 import java.util.Enumeration;
35 import java.util.HashMap;
36 import java.util.Map;
37
38 import javax.servlet.http.HttpSession;
39
40 import nextapp.echo.EchoInstance;
41
42 import nextapp.echoservlet.html.Html;
43 import nextapp.echoservlet.resource.ResourceNames;
44
45 /**
46 * The service is responsible for initializing applications. This is
47 * the first service that will be invoked when an application starts up. It
48 * provides a client detection page that will use JavaScript to gather
49 * information about public properties of the client browser, storing them in
50 * a form. The form is submitted immediately thereafter, invoking this
51 * service a second time. The form data will be parsed by a new
52 * <code>ClientProperties</code> object that is created
53 * from this service. An InstancePeer and related objects are then created
54 * for the application, stored in the user's session, and initialized.
55 * The service() method of the application's default window is then invoked,
56 * allowing the application to take control.
57 */
58 final class Initializer
59 implements Serializable, Service {
60
61 /**
62 * A prefix for the session key used to temporarily store initial parameters.
63 */
64 public static final String PARAMETERS_KEY_PREFIX = "InitialParameters.";
65
66 /** The Id of this service */
67 private static final Id ID = new Id(ClientObjects.STD_PREFIX + "start");
68
69 /**
70 * The HTTP form parameter name used by the client detection script
71 * to indicate client detection information has been retrieved.
72 */
73 public static final String PARAMETER_DETECT_COMPLETE = ClientObjects.STD_PREFIX + "detectComplete";
74
75 /**
76 * The client detection service. It is not necessary that this service
77 * be registered with the EchoServer.
78 */
79 private static final Service SERVICE_CLIENT_DETECT
80 = Template.createFromResource(ClientObjects.STD_PREFIX + "clidet", ResourceNames.CLIENT_DETECT);
81
82 /**
83 * The client detection service. It is not necessary that this service
84 * be registered with the EchoServer.
85 */
86 private static final Service SERVICE_STARTUP_CONTAINER
87 = Template.createFromResource(ClientObjects.STD_PREFIX + "startcon", ResourceNames.STARTUP_CONTAINER);
88
89 static {
90 EchoServer.addGlobalService(SERVICE_CLIENT_DETECT);
91 }
92
93 /**
94 * Creates one-time-use variable data used to start the application.
95 *
96 * @param conn The connection for which the variable data will be used.
97 * @return The generated <code>VariableData</code> object.
98 */
99 public static VariableData createInitialVariableData(Connection conn) {
100 VariableData initialVariableData = new VariableData();
101 EchoServer server = conn.getServer();
102
103 initialVariableData.set(VariableData.APPLICATION_URI, conn.getApplicationUri());
104 initialVariableData.set(VariableData.STARTUP_WINDOW_TITLE,
105 Html.encode(server.getApplicationProperties().get(ApplicationProperties.STARTUP_WINDOW_TITLE)));
106 initialVariableData.set(VariableData.STARTUP_WINDOW_TEXT,
107 Html.encode(server.getApplicationProperties().get(ApplicationProperties.STARTUP_WINDOW_TEXT)));
108 initialVariableData.set(VariableData.STARTUP_NO_SCRIPT_ERROR_TEXT,
109 Html.encode(server.getApplicationProperties().get(ApplicationProperties.STARTUP_NO_SCRIPT_ERROR_TEXT)));
110 initialVariableData.set(VariableData.STARTUP_DELAY,
111 server.getApplicationProperties().get(ApplicationProperties.STARTUP_DELAY));
112 initialVariableData.set(VariableData.SPLASH_SCREEN_URI,
113 conn.correctUri(server.getApplicationProperties().get(ApplicationProperties.SPLASH_SCREEN_URI)));
114 initialVariableData.set(VariableData.SESSION_EXPIRATION_URI, server.getSessionExpirationUri(conn.getApplicationUri()));
115 if (conn.getServer().getApplicationProperties().get(ApplicationProperties.SPLASH_SCREEN_URI) == null) {
116 initialVariableData.set(VariableData.CLIENT_DETECT_TARGET_FRAME, "_self");
117 } else {
118 initialVariableData.set(VariableData.CLIENT_DETECT_URI, ClientObjects.getServiceUri(conn, SERVICE_CLIENT_DETECT));
119 initialVariableData.set(VariableData.CLIENT_DETECT_TARGET_FRAME, "_parent");
120 }
121
122 return initialVariableData;
123 }
124
125 /**
126 * Creates an Initializer service. Only initializer service is used by the
127 * entire application.
128 */
129 Initializer() {
130 super();
131 }
132
133 /**
134 * @see nextapp.echoservlet.Service#getId()
135 */
136 public Id getId() {
137 return ID;
138 }
139
140 /**
141 * Saves the initial servlet parameters so that they
142 * can later be available to the application
143 * instance.
144 *
145 * @param conn The Connection with the parameters
146 * @param parameterMapSessionKey The session key in which to store the parameter map.
147 */
148 private void saveInitialParameters(Connection conn, String parameterMapSessionKey) {
149 Map parameterMap = new HashMap();
150 for (Enumeration e = conn.getParameterNames(); e.hasMoreElements();) {
151 String paramName = (String) e.nextElement();
152 parameterMap.put(paramName,conn.getRequest().getParameterValues(paramName));
153 }
154 if (parameterMap.size() > 0) {
155 // We only save the parameter map into the session if there are any extra parameters
156 // on startup. Otherwise the session will not be created here.
157 conn.getRequest().getSession(true).setAttribute(parameterMapSessionKey,parameterMap);
158 }
159 }
160
161 /**
162 * @see nextapp.echoservlet.Service#service(Connection)
163 */
164 public void service(Connection conn)
165 throws IOException {
166
167 // Our initial parameter session key
168 String parameterMapSessionKey = PARAMETERS_KEY_PREFIX + conn.getApplicationUri();
169
170 if (conn.getParameter(PARAMETER_DETECT_COMPLETE) == null) {
171 // First request (client detection has not taken place): Do client detection.
172 if (conn.getServer().getApplicationProperties().get(ApplicationProperties.SPLASH_SCREEN_URI) == null) {
173 // save any intial URL parameters for later
174 saveInitialParameters(conn,parameterMapSessionKey);
175
176 // Display visible client detection page with "application starting" message.
177 SERVICE_CLIENT_DETECT.service(conn);
178 } else {
179 // Display frameset containing hidden client detection page and visible splash screen.
180 SERVICE_STARTUP_CONTAINER.service(conn);
181 }
182 } else {
183 // Second request (client detection has taken place): Start application, display initial window content.
184
185 // Create client properties object from connection.
186 ClientProperties clientProperties = new ClientProperties(conn);
187
188 // Create a new instance of the application. (The instance is not yet initialized, as its init() method has not yet
189 // been called.)
190 EchoInstance instance = conn.getServer().newInstance();
191
192 // Mark the EchoInstance as active for this thread, such that invoking
193 // EchoInstance.getEchoInstance() will return it.
194 instance.markActive(true);
195
196 if (instance.getLocale() == null) {
197 // Only set locale if application has not set it.
198 instance.setLocale(conn.getServer().getDefaultLocale(conn.getRequest()));
199 }
200
201 // Create an InstancePeer for the new instance.
202 InstancePeer instancePeer = new InstancePeer(instance, conn.getApplicationUri());
203
204 // Add the connection to the InstancePeer.
205 instancePeer.addConnection(conn);
206
207 // Store the client properties.
208 instancePeer.setClientProperties(clientProperties);
209
210 // Store the principal.
211 instancePeer.setUserPrincipal(conn.getRequest().getUserPrincipal());
212
213 // Assign the connection to the InstancePeer.
214 conn.setInstancePeer(instancePeer);
215
216 // Store initial parameters if required.
217 HttpSession session = conn.getRequest().getSession(true);
218 Map parameterMap = (Map) session.getAttribute(parameterMapSessionKey);
219 session.removeAttribute(parameterMapSessionKey);
220 if (parameterMap != null) {
221 instance.setAttribute(InitialParameters.ATTRIBUTE_NAME, new InitialParametersImpl(parameterMap));
222 }
223
224 // Retrieve any persistent cookies.
225 instancePeer.getCookieManager().retrieveCookies(conn.getRequest());
226
227 // Initialize the InstancePeer. The InstancePeer initialization code will initialize the application
228 // instance by calling EchoInstance.init(),
229 instancePeer.init();
230
231 // Set any cookies stored in the initialization request.
232 instancePeer.getCookieManager().storeCookies(conn.getResponse());
233
234 // Render the main window.
235 ((Service) instancePeer.getPeer(instancePeer.getDefaultWindowId())).service(conn);
236
237 // Remove the connection from the InstancePeer.
238 instancePeer.removeConnection(conn);
239
240 // Mark the EchoInstance as inactive for this thread, such that invoking
241 // EchoInstance.getEchoInstance() will return null
242 instance.markActive(false);
243 }
244 }
245 }