Source code: org/jeteam/session/HttpSessionCache.java
1 /*
2 * JETeam, Java Enterprise TeamWork
3 *
4 * Distributable under the GPL license.
5 * See terms of license at http://www.gnu.org
6 *
7 * $Source: /cvsroot/jeteam/jeteam/jeteam-swx/src/java/org/jeteam/session/HttpSessionCache.java,v $
8 * $Date: 2003/05/29 11:54:26 $
9 * $Author: draftdog $
10 * $Revision: 1.5 $
11 */
12 package org.jeteam.session;
13
14 import org.jeteam.bean.common.SettingConstants;
15 import org.jeteam.bean.config.SettingDTO;
16 import org.jeteam.bean.config.SettingServiceUtil;
17 import org.jeteam.bean.project.ProjectDTO;
18 import org.jeteam.bean.user.UserDTO;
19 import org.jeteam.bean.user.UserServiceRemote;
20 import org.jeteam.bean.user.UserServiceUtil;
21
22 import javax.servlet.http.HttpServletRequest;
23 import javax.servlet.http.HttpSession;
24 import javax.naming.NamingException;
25 import java.text.DateFormatSymbols;
26 import java.util.Calendar;
27 import java.util.Date;
28 import java.util.GregorianCalendar;
29 import java.util.Hashtable;
30 import java.util.Locale;
31
32 /**
33 * This cache keeps a java.util.Map internally to quickly lookup information that is associated with a specific session,
34 * this is to avoid to perform possibly costly operations more than necessary. In fact, the java.util.Map implementation
35 * maps HttpSessions instances to HttpSessionCache instance.
36 * <p>
37 * Get an instance for your session by passing the original request to the <code>getInstance(HttpServletRequest)</code>
38 * method.
39 * <p>
40 * It is also possible to invalidate the cache in case you want to make sure the cache is rebuilt the next time
41 * you do a call to <code>getInstance(HttpServletRequest)</code>.
42 * <p>
43 * You can do this in one call, by using <code>getInstance(HttpServletRequest,boolean)</code> and passing
44 * <code>true</code> as the second argument, this will force the cache to be replaced by the more recent one.
45 * <p>
46 * Furthermore, there are getter method for all available cache information.
47 * <p>
48 * This class is thread-safe in that it uses a java.util.Hashtable as the java.util.Map implementation.
49 *
50 * @see java.util.Hashtable
51 */
52 public class HttpSessionCache
53 {
54 /**
55 * This is the singleton instance map. We need it to be synchronized since many clients could access it
56 * simultanously, that's why we prefer a Hastable over a HashMap.
57 */
58 private final static Hashtable instanceMap = new Hashtable();
59
60 private UserDTO user = null;
61 private ProjectDTO projectContext = null;
62 private SettingDTO[] settings = null;
63 private ProjectDTO[] projects = null;
64 private String[] days = null;
65 private String[] months = null;
66 private String[] years = null;
67 private String[] percentages = null;
68
69 /**
70 * This class should not be instantiated directly.
71 * <p>
72 * By declaring it <code>private</code> we respect the singleton pattern.
73 */
74 private HttpSessionCache(HttpServletRequest request)
75 {
76 try
77 {
78 String userName = request.getUserPrincipal().getName();
79 UserServiceRemote userService = UserServiceUtil.getHome().create();
80
81 /*
82 * Set the business properties
83 */
84 user = userService.getUser(userName);
85 settings = userService.getSettings(user);
86 projects = userService.getProjects(user);
87
88 projectContext = getDefaultProjectContext();
89
90 // set percentages
91 percentages = new String[21];
92 for (int i = 0; i < percentages.length; i++)
93 {
94 percentages[i] = String.valueOf(i * 5);
95 }
96
97 // set date: days, months, years
98 Locale locale = request.getLocale();
99
100 // days
101 days = new String[31];
102 for (int i = 0; i < days.length; i++)
103 days[i] = String.valueOf(i + 1);
104
105 /**
106 * months
107 *
108 * apparently the months returned by DateFormatSymbols contain an additional
109 * empty String at the 13th index.
110 */
111 months = new String[12];
112 String[] dateMonths = new DateFormatSymbols(locale).getMonths();
113 for (int i = 0; i < 12; i++)
114 {
115 months[i] = dateMonths[i];
116 }
117
118 // years
119 GregorianCalendar calendar = new GregorianCalendar(locale);
120 calendar.setTime(new Date());
121 int year = calendar.get(Calendar.YEAR);
122 years = new String[5];
123 for (int i = 0; i < years.length; i++)
124 years[i] = String.valueOf(year + i);
125 }
126 catch (Exception e)
127 {
128 request.getSession().invalidate();
129 throw new IllegalStateException("Unable to setup session attributes: " + e);
130 }
131 }
132
133 /**
134 * Gets the singleton instance for the cache.
135 * <p>
136 * This is identical to calling <code>getInstance(request, false)</code>.
137 *
138 * @param request The request from which the cache will be built.
139 * @return The cache that is built from the argument request.
140 */
141 public static HttpSessionCache getInstance(HttpServletRequest request)
142 {
143 return getInstance(request, false);
144 }
145
146 /**
147 * Gets the singleton instance for the cache.
148 * <p>
149 * In case there is a session for the argument request and the session is known
150 * internally the cache will be returned immediately, otherwise the cache is
151 * constructed and returned.
152 * <p>
153 * Use the <code>force</code> parameter if you want to reconstruct the cache even if it
154 * already exists.
155 * <p>
156 * Please note that doing so implicitely invalidates the cache.
157 *
158 * @param request The request from which the cache will be built.
159 * @param force Set this to <code>true</code> if you want to make sure the cache is a recent as possible, this
160 * means you will not reuse the existing one (if any). Set to <code>false</code> otherwise.
161 * @return The cache that is built from the argument request.
162 */
163 public static HttpSessionCache getInstance(HttpServletRequest request, boolean force)
164 {
165 HttpSession session = request.getSession();
166 HttpSessionCache instance = (HttpSessionCache) instanceMap.get(session);
167
168 if (force || (instance == null))
169 {
170 instance = new HttpSessionCache(request);
171 instanceMap.put(session, instance);
172 }
173
174 return instance;
175 }
176
177 /**
178 * Invalidates this cache, the next time you need it you should get the instance by calling
179 * <code>getInstance(HttpServletRequest)</code> again.
180 * <p>
181 * Do this when you know the members of the cache have been updated by an external process.
182 * <p>
183 * <strong>IMPORTANT</strong>:
184 * <br>
185 * Do not use a cache instance after calling this method on a request which shares the same session as
186 * that cache. Each request from the same session uses the same cache.
187 *
188 * @param request The request who's cache will be invalidated.
189 */
190 public static void invalidateInstance(HttpServletRequest request)
191 {
192 instanceMap.remove(request.getSession());
193 }
194
195
196 /**
197 * Gets the ProjectDTO instance with the given name, please note that only projects associated with this
198 * session's user are being taken into account.
199 *
200 * @param projectName The project name to use in order to find the ProjectDTO instance, if the argument
201 * equals <code>null</code> it will be directly returned.
202 * @return The associated ProjectDTO instance with the given name, or <code>null</code>
203 * when no project can be found for the user.
204 */
205 private final ProjectDTO getProject(String projectName)
206 {
207 if (projectName == null)
208 {
209 return null;
210 }
211
212 ProjectDTO project = null;
213
214 for (int i = 0; i < projects.length; i++)
215 {
216 project = projects[i];
217 if (project.getName().equals(projectName))
218 {
219 break;
220 }
221 }
222
223 return project;
224 }
225
226 /**
227 * Searches the user's settings for a default project context name, this value is returned
228 * when found. In case it is not found this method returns <code>null</code>.
229 *
230 * @return The default project context name or <code>null</code> in case it cannot be found.
231 */
232 private final String getDefaultProjectContextName()
233 {
234 for (int i = 0; i < settings.length; i++)
235 {
236 SettingDTO setting = settings[i];
237 if (SettingConstants.DEFAULT_PROJECT.equals( setting.getKey() ))
238 {
239 return setting.getValue();
240 }
241 }
242 return null;
243 }
244
245 /**
246 * Checks if the user has a setting for a default project, if this is the case the corresponding ProjectDTO
247 * instance is returned. In case the user does not have such a setting or it is invalid for some reason the first
248 * project this user is assigned to will be returned and this value will be set as a new setting for that user.
249 *
250 * @return The ProjectDTO instance that represents the default project context for this user, or <code>null</code>
251 * if the user is not assigned to any projects.
252 */
253 private ProjectDTO getDefaultProjectContext()
254 {
255 ProjectDTO project = getProject( getDefaultProjectContextName() );
256
257 // the user has no setting for default project but is assigned to at least one project
258 if ( (project == null) && (projects.length > 0) )
259 {
260 project = projects[0];
261 SettingDTO setting = new SettingDTO(SettingConstants.DEFAULT_PROJECT, project.getName());
262 try
263 {
264 SettingServiceUtil.getHome().create().createSetting(setting,user);
265 settings = UserServiceUtil.getHome().create().getSettings(user);
266 }
267 catch (Exception e)
268 {
269 // do nothing, let this method return null
270 }
271 }
272
273 return project;
274 }
275
276 /**
277 * Returns the user value object that is cached, the information
278 * is derived from the request's principal.
279 *
280 * @return The user.
281 */
282 public UserDTO getUser()
283 {
284 return user;
285 }
286
287 /**
288 * Returns an array of SettingDTO instances that represent the user's preferences or settings.
289 *
290 * @return An array of SettingDTO's.
291 */
292 public SettingDTO[] getSettings()
293 {
294 return settings;
295 }
296
297 /**
298 * Returns an array of ProjectDTO instances that represent the user's projects.
299 *
300 * @return An array of ProjectDTO's.
301 */
302 public ProjectDTO[] getProjects()
303 {
304 return projects;
305 }
306
307 /**
308 * Returns an array of String instances that represent the days in a month.
309 *
310 * @return String instances "1" ... "31"
311 */
312 public String[] getDays()
313 {
314 return days;
315 }
316
317 /**
318 * The months of a year in the user's locale.
319 *
320 * @return The equivalent of <code>DateFormatSymbols.getMonths();</code>
321 */
322 public String[] getMonths()
323 {
324 return months;
325 }
326
327 /**
328 * Five years starting from today.
329 *
330 * @return The next five years.
331 */
332 public String[] getYears()
333 {
334 return years;
335 }
336
337 /**
338 * Percentages starting from 0 to 100, each time incremented by 5.
339 *
340 * @return The list of percentages
341 */
342 public String[] getPercentages()
343 {
344 return percentages;
345 }
346
347 /**
348 * The current project context, this value will be used when creating for example
349 * a task and no project is explicitely specified.
350 *
351 * @return The project
352 */
353 public ProjectDTO getProjectContext()
354 {
355 return projectContext;
356 }
357
358 }