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