1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.struts.webapp.example2.memory;
19
20
21 import java.io.BufferedInputStream;
22 import java.io.File;
23 import java.io.FileInputStream;
24 import java.io.FileOutputStream;
25 import java.io.IOException;
26 import java.io.OutputStreamWriter;
27 import java.io.PrintWriter;
28 import java.util.HashMap;
29 import org.apache.commons.digester.Digester;
30 import org.apache.commons.digester.ObjectCreationFactory;
31 import org.apache.commons.logging.Log;
32 import org.apache.commons.logging.LogFactory;
33 import org.apache.struts.webapp.example2.Subscription;
34 import org.apache.struts.webapp.example2.User;
35 import org.apache.struts.webapp.example2.UserDatabase;
36 import org.xml.sax.Attributes;
37
38
39 /***
40 * <p>Concrete implementation of {@link UserDatabase} for an in-memory
41 * database backed by an XML data file.</p>
42 *
43 * @author Craig R. McClanahan
44 * @version $Rev: 421494 $ $Date: 2006-07-12 20:55:17 -0700 (Wed, 12 Jul 2006) $
45 * @since Struts 1.1
46 */
47
48 public final class MemoryUserDatabase implements UserDatabase {
49
50
51
52
53
54
55
56
57 /***
58 * Logging output for this user database instance.
59 */
60 private Log log = LogFactory.getLog(this.getClass());
61
62
63 /***
64 * The {@link User}s associated with this UserDatabase, keyed by username.
65 */
66 private HashMap users = new HashMap();
67
68
69
70
71
72 /***
73 * Absolute pathname to the persistent file we use for loading and storing
74 * persistent data.
75 */
76 private String pathname = null;
77
78 private String pathnameOld = null;
79
80 private String pathnameNew = null;
81
82 public String getPathname() {
83 return (this.pathname);
84 }
85
86 public void setPathname(String pathname) {
87 this.pathname = pathname;
88 pathnameOld = pathname + ".old";
89 pathnameNew = pathname + ".new";
90 }
91
92
93
94
95
96 /***
97 * <p>Finalize access to the underlying persistence layer.</p>
98 *
99 * @exception Exception if a database access error occurs
100 */
101 public void close() throws Exception {
102
103 save();
104
105 }
106
107
108 /***
109 * <p>Create and return a new {@link User} defined in this user database.
110 * </p>
111 *
112 * @param username Username of the new user
113 *
114 * @exception IllegalArgumentExceptionif the specified username
115 * is not unique
116 */
117 public User createUser(String username) {
118
119 synchronized (users) {
120 if (users.get(username) != null) {
121 throw new IllegalArgumentException("Duplicate user '" +
122 username + "'");
123 }
124 if (log.isTraceEnabled()) {
125 log.trace("Creating user '" + username + "'");
126 }
127 MemoryUser user = new MemoryUser(this, username);
128 synchronized (users) {
129 users.put(username, user);
130 }
131 return (user);
132 }
133
134 }
135
136
137 /***
138 * <p>Return the existing {@link User} with the specified username,
139 * if any; otherwise return <code>null</code>.</p>
140 *
141 * @param username Username of the user to retrieve
142 */
143 public User findUser(String username) {
144
145 synchronized (users) {
146 return ((User) users.get(username));
147 }
148
149 }
150
151
152 /***
153 * <p>Return the set of {@link User}s defined in this user database.</p>
154 */
155 public User[] findUsers() {
156
157 synchronized (users) {
158 User results[] = new User[users.size()];
159 return ((User[]) users.values().toArray(results));
160 }
161
162 }
163
164
165 /***
166 * <p>Initiate access to the underlying persistence layer.</p>
167 *
168 * @exception Exception if a database access error occurs
169 */
170 public void open() throws Exception {
171
172 FileInputStream fis = null;
173 BufferedInputStream bis = null;
174
175 try {
176
177
178 if (log.isDebugEnabled()) {
179 log.debug("Loading database from '" + pathname + "'");
180 }
181 fis = new FileInputStream(pathname);
182 bis = new BufferedInputStream(fis);
183
184
185 Digester digester = new Digester();
186 digester.push(this);
187 digester.setValidating(false);
188 digester.addFactoryCreate
189 ("database/user",
190 new MemoryUserCreationFactory(this));
191 digester.addFactoryCreate
192 ("database/user/subscription",
193 new MemorySubscriptionCreationFactory(this));
194
195
196 digester.parse(bis);
197 bis.close();
198 bis = null;
199 fis = null;
200
201 } catch (Exception e) {
202
203 log.error("Loading database from '" + pathname + "':", e);
204 throw e;
205
206 } finally {
207
208 if (bis != null) {
209 try {
210 bis.close();
211 } catch (Throwable t) {
212 ;
213 }
214 bis = null;
215 fis = null;
216 }
217
218 }
219
220 }
221
222
223 /***
224 * Remove the specified {@link User} from this database.
225 *
226 * @param user User to be removed
227 *
228 * @exception IllegalArgumentException if the specified user is not
229 * associated with this database
230 */
231 public void removeUser(User user) {
232
233 if (!(this == user.getDatabase())) {
234 throw new IllegalArgumentException
235 ("User not associated with this database");
236 }
237 if (log.isTraceEnabled()) {
238 log.trace("Removing user '" + user.getUsername() + "'");
239 }
240 synchronized (users) {
241 users.remove(user.getUsername());
242 }
243
244 }
245
246
247 /***
248 * <p>Save any pending changes to the underlying persistence layer.</p>
249 *
250 * @exception Exception if a database access error occurs
251 */
252 public void save() throws Exception {
253
254 if (log.isDebugEnabled()) {
255 log.debug("Saving database to '" + pathname + "'");
256 }
257 File fileNew = new File(pathnameNew);
258 PrintWriter writer = null;
259
260 try {
261
262
263 FileOutputStream fos = new FileOutputStream(fileNew);
264 OutputStreamWriter osw = new OutputStreamWriter(fos);
265 writer = new PrintWriter(osw);
266
267
268 writer.println("<?xml version='1.0'?>");
269 writer.println("<database>");
270
271
272 User users[] = findUsers();
273 for (int i = 0; i < users.length; i++) {
274 writer.print(" ");
275 writer.println(users[i]);
276 Subscription subscriptions[] =
277 users[i].getSubscriptions();
278 for (int j = 0; j < subscriptions.length; j++) {
279 writer.print(" ");
280 writer.println(subscriptions[j]);
281 writer.print(" ");
282 writer.println("</subscription>");
283 }
284 writer.print(" ");
285 writer.println("</user>");
286 }
287
288
289 writer.println("</database>");
290
291
292 if (writer.checkError()) {
293 writer.close();
294 fileNew.delete();
295 throw new IOException
296 ("Saving database to '" + pathname + "'");
297 }
298 writer.close();
299 writer = null;
300
301 } catch (IOException e) {
302
303 if (writer != null) {
304 writer.close();
305 }
306 fileNew.delete();
307 throw e;
308
309 }
310
311
312
313 File fileOrig = new File(pathname);
314 File fileOld = new File(pathnameOld);
315 if (fileOrig.exists()) {
316 fileOld.delete();
317 if (!fileOrig.renameTo(fileOld)) {
318 throw new IOException
319 ("Renaming '" + pathname + "' to '" + pathnameOld + "'");
320 }
321 }
322 if (!fileNew.renameTo(fileOrig)) {
323 if (fileOld.exists()) {
324 fileOld.renameTo(fileOrig);
325 }
326 throw new IOException
327 ("Renaming '" + pathnameNew + "' to '" + pathname + "'");
328 }
329 fileOld.delete();
330
331 }
332
333
334 }
335
336
337 /***
338 * Digester object creation factory for subscription instances.
339 */
340 class MemorySubscriptionCreationFactory implements ObjectCreationFactory {
341
342 public MemorySubscriptionCreationFactory(MemoryUserDatabase database) {
343 }
344
345
346 private Digester digester = null;
347
348 public Digester getDigester() {
349 return (this.digester);
350 }
351
352 public void setDigester(Digester digester) {
353 this.digester = digester;
354 }
355
356 public Object createObject(Attributes attributes) {
357 String host = attributes.getValue("host");
358 User user = (User) digester.peek();
359 Subscription subscription = user.createSubscription(host);
360 String autoConnect = attributes.getValue("autoConnect");
361 if (autoConnect == null) {
362 autoConnect = "false";
363 }
364 if ("true".equalsIgnoreCase(autoConnect) ||
365 "yes".equalsIgnoreCase(autoConnect)) {
366 subscription.setAutoConnect(true);
367 } else {
368 subscription.setAutoConnect(false);
369 }
370 subscription.setPassword(attributes.getValue("password"));
371 subscription.setType(attributes.getValue("type"));
372 subscription.setUsername(attributes.getValue("username"));
373 return (subscription);
374 }
375
376 }
377
378
379 /***
380 * Digester object creation factory for user instances.
381 */
382 class MemoryUserCreationFactory implements ObjectCreationFactory {
383
384 public MemoryUserCreationFactory(MemoryUserDatabase database) {
385 this.database = database;
386 }
387
388 private MemoryUserDatabase database = null;
389
390 private Digester digester = null;
391
392 public Digester getDigester() {
393 return (this.digester);
394 }
395
396 public void setDigester(Digester digester) {
397 this.digester = digester;
398 }
399
400 public Object createObject(Attributes attributes) {
401 String username = attributes.getValue("username");
402 User user = database.createUser(username);
403 user.setFromAddress(attributes.getValue("fromAddress"));
404 user.setFullName(attributes.getValue("fullName"));
405 user.setPassword(attributes.getValue("password"));
406 user.setReplyToAddress(attributes.getValue("replyToAddress"));
407 return (user);
408 }
409
410 }