001: /*
002: * $Id: MemoryUserDatabase.java 471754 2006-11-06 14:55:09Z husted $
003: *
004: * Licensed to the Apache Software Foundation (ASF) under one
005: * or more contributor license agreements. See the NOTICE file
006: * distributed with this work for additional information
007: * regarding copyright ownership. The ASF licenses this file
008: * to you under the Apache License, Version 2.0 (the
009: * "License"); you may not use this file except in compliance
010: * with the License. You may obtain a copy of the License at
011: *
012: * http://www.apache.org/licenses/LICENSE-2.0
013: *
014: * Unless required by applicable law or agreed to in writing,
015: * software distributed under the License is distributed on an
016: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017: * KIND, either express or implied. See the License for the
018: * specific language governing permissions and limitations
019: * under the License.
020: */
021:
022: package org.apache.struts.webapp.example.memory;
023:
024: import java.io.BufferedInputStream;
025: import java.io.File;
026: import java.io.FileInputStream;
027: import java.io.FileOutputStream;
028: import java.io.IOException;
029: import java.io.OutputStreamWriter;
030: import java.io.PrintWriter;
031: import java.util.HashMap;
032: import org.apache.commons.digester.Digester;
033: import org.apache.commons.digester.ObjectCreationFactory;
034: import org.apache.commons.logging.Log;
035: import org.apache.commons.logging.LogFactory;
036: import org.apache.struts.webapp.example.Subscription;
037: import org.apache.struts.webapp.example.User;
038: import org.apache.struts.webapp.example.UserDatabase;
039: import org.xml.sax.Attributes;
040:
041: /**
042: * <p>Concrete implementation of {@link UserDatabase} for an in-memory
043: * database backed by an XML data file.</p>
044: *
045: * @author Craig R. McClanahan
046: * @version $Rev: 471754 $ $Date: 2006-11-06 08:55:09 -0600 (Mon, 06 Nov 2006) $
047: * @since Struts 1.1
048: */
049:
050: public final class MemoryUserDatabase implements UserDatabase {
051:
052: // ----------------------------------------------------------- Constructors
053:
054: // ----------------------------------------------------- Instance Variables
055:
056: /**
057: * Logging output for this user database instance.
058: */
059: private Log log = LogFactory.getLog(this .getClass());
060:
061: /**
062: * The {@link User}s associated with this UserDatabase, keyed by username.
063: */
064: private HashMap users = new HashMap();
065:
066: // ------------------------------------------------------------- Properties
067:
068: /**
069: * Absolute pathname to the persistent file we use for loading and storing
070: * persistent data.
071: */
072: private String pathname = null;
073:
074: private String pathnameOld = null;
075:
076: private String pathnameNew = null;
077:
078: public String getPathname() {
079: return (this .pathname);
080: }
081:
082: public void setPathname(String pathname) {
083: this .pathname = pathname;
084: pathnameOld = pathname + ".old";
085: pathnameNew = pathname + ".new";
086: }
087:
088: // --------------------------------------------------------- Public Methods
089:
090: /**
091: * <p>Finalize access to the underlying persistence layer.</p>
092: *
093: * @exception Exception if a database access error occurs
094: */
095: public void close() throws Exception {
096:
097: save();
098:
099: }
100:
101: /**
102: * <p>Create and return a new {@link User} defined in this user database.
103: * </p>
104: *
105: * @param username Username of the new user
106: *
107: * @exception IllegalArgumentExceptionif the specified username
108: * is not unique
109: */
110: public User createUser(String username) {
111:
112: synchronized (users) {
113: if (users.get(username) != null) {
114: throw new IllegalArgumentException("Duplicate user '"
115: + username + "'");
116: }
117: if (log.isTraceEnabled()) {
118: log.trace("Creating user '" + username + "'");
119: }
120: MemoryUser user = new MemoryUser(this , username);
121: synchronized (users) {
122: users.put(username, user);
123: }
124: return (user);
125: }
126:
127: }
128:
129: /**
130: * <p>Return the existing {@link User} with the specified username,
131: * if any; otherwise return <code>null</code>.</p>
132: *
133: * @param username Username of the user to retrieve
134: */
135: public User findUser(String username) {
136:
137: synchronized (users) {
138: return ((User) users.get(username));
139: }
140:
141: }
142:
143: /**
144: * <p>Return the set of {@link User}s defined in this user database.</p>
145: */
146: public User[] findUsers() {
147:
148: synchronized (users) {
149: User results[] = new User[users.size()];
150: return ((User[]) users.values().toArray(results));
151: }
152:
153: }
154:
155: /**
156: * <p>Initiate access to the underlying persistence layer.</p>
157: *
158: * @exception Exception if a database access error occurs
159: */
160: public void open() throws Exception {
161:
162: FileInputStream fis = null;
163: BufferedInputStream bis = null;
164:
165: try {
166:
167: // Acquire an input stream to our database file
168: if (log.isDebugEnabled()) {
169: log.debug("Loading database from '" + pathname + "'");
170: }
171: fis = new FileInputStream(pathname);
172: bis = new BufferedInputStream(fis);
173:
174: // Construct a digester to use for parsing
175: Digester digester = new Digester();
176: digester.push(this );
177: digester.setValidating(false);
178: digester.addFactoryCreate("database/user",
179: new MemoryUserCreationFactory(this ));
180: digester.addFactoryCreate("database/user/subscription",
181: new MemorySubscriptionCreationFactory(this ));
182:
183: // Parse the input stream to initialize our database
184: digester.parse(bis);
185: bis.close();
186: bis = null;
187: fis = null;
188:
189: } catch (Exception e) {
190:
191: log.error("Loading database from '" + pathname + "':", e);
192: throw e;
193:
194: } finally {
195:
196: if (bis != null) {
197: try {
198: bis.close();
199: } catch (Throwable t) {
200: ;
201: }
202: bis = null;
203: fis = null;
204: }
205:
206: }
207:
208: }
209:
210: /**
211: * Remove the specified {@link User} from this database.
212: *
213: * @param user User to be removed
214: *
215: * @exception IllegalArgumentException if the specified user is not
216: * associated with this database
217: */
218: public void removeUser(User user) {
219:
220: if (!(this == user.getDatabase())) {
221: throw new IllegalArgumentException(
222: "User not associated with this database");
223: }
224: if (log.isTraceEnabled()) {
225: log.trace("Removing user '" + user.getUsername() + "'");
226: }
227: synchronized (users) {
228: users.remove(user.getUsername());
229: }
230:
231: }
232:
233: /**
234: * <p>Save any pending changes to the underlying persistence layer.</p>
235: *
236: * @exception Exception if a database access error occurs
237: */
238: public void save() throws Exception {
239:
240: if (log.isDebugEnabled()) {
241: log.debug("Saving database to '" + pathname + "'");
242: }
243: File fileNew = new File(pathnameNew);
244: PrintWriter writer = null;
245:
246: try {
247:
248: // Configure our PrintWriter
249: FileOutputStream fos = new FileOutputStream(fileNew);
250: OutputStreamWriter osw = new OutputStreamWriter(fos);
251: writer = new PrintWriter(osw);
252:
253: // Print the file prolog
254: writer.println("<?xml version='1.0'?>");
255: writer.println("<database>");
256:
257: // Print entries for each defined user and associated subscriptions
258: User users[] = findUsers();
259: for (int i = 0; i < users.length; i++) {
260: writer.print(" ");
261: writer.println(users[i]);
262: Subscription subscriptions[] = users[i]
263: .getSubscriptions();
264: for (int j = 0; j < subscriptions.length; j++) {
265: writer.print(" ");
266: writer.println(subscriptions[j]);
267: writer.print(" ");
268: writer.println("</subscription>");
269: }
270: writer.print(" ");
271: writer.println("</user>");
272: }
273:
274: // Print the file epilog
275: writer.println("</database>");
276:
277: // Check for errors that occurred while printing
278: if (writer.checkError()) {
279: writer.close();
280: fileNew.delete();
281: throw new IOException("Saving database to '" + pathname
282: + "'");
283: }
284: writer.close();
285: writer = null;
286:
287: } catch (IOException e) {
288:
289: if (writer != null) {
290: writer.close();
291: }
292: fileNew.delete();
293: throw e;
294:
295: }
296:
297: // Perform the required renames to permanently save this file
298: File fileOrig = new File(pathname);
299: File fileOld = new File(pathnameOld);
300: if (fileOrig.exists()) {
301: fileOld.delete();
302: if (!fileOrig.renameTo(fileOld)) {
303: throw new IOException("Renaming '" + pathname
304: + "' to '" + pathnameOld + "'");
305: }
306: }
307: if (!fileNew.renameTo(fileOrig)) {
308: if (fileOld.exists()) {
309: fileOld.renameTo(fileOrig);
310: }
311: throw new IOException("Renaming '" + pathnameNew + "' to '"
312: + pathname + "'");
313: }
314: fileOld.delete();
315:
316: }
317:
318: }
319:
320: /**
321: * Digester object creation factory for subscription instances.
322: */
323: class MemorySubscriptionCreationFactory implements
324: ObjectCreationFactory {
325:
326: public MemorySubscriptionCreationFactory(MemoryUserDatabase database) {
327: }
328:
329: private Digester digester = null;
330:
331: public Digester getDigester() {
332: return (this .digester);
333: }
334:
335: public void setDigester(Digester digester) {
336: this .digester = digester;
337: }
338:
339: public Object createObject(Attributes attributes) {
340: String host = attributes.getValue("host");
341: User user = (User) digester.peek();
342: Subscription subscription = user.createSubscription(host);
343: String autoConnect = attributes.getValue("autoConnect");
344: if (autoConnect == null) {
345: autoConnect = "false";
346: }
347: if ("true".equalsIgnoreCase(autoConnect)
348: || "yes".equalsIgnoreCase(autoConnect)) {
349: subscription.setAutoConnect(true);
350: } else {
351: subscription.setAutoConnect(false);
352: }
353: subscription.setPassword(attributes.getValue("password"));
354: subscription.setType(attributes.getValue("type"));
355: subscription.setUsername(attributes.getValue("username"));
356: return (subscription);
357: }
358:
359: }
360:
361: /**
362: * Digester object creation factory for user instances.
363: */
364: class MemoryUserCreationFactory implements ObjectCreationFactory {
365:
366: public MemoryUserCreationFactory(MemoryUserDatabase database) {
367: this .database = database;
368: }
369:
370: private MemoryUserDatabase database = null;
371:
372: private Digester digester = null;
373:
374: public Digester getDigester() {
375: return (this .digester);
376: }
377:
378: public void setDigester(Digester digester) {
379: this .digester = digester;
380: }
381:
382: public Object createObject(Attributes attributes) {
383: String username = attributes.getValue("username");
384: User user = database.createUser(username);
385: user.setFromAddress(attributes.getValue("fromAddress"));
386: user.setFullName(attributes.getValue("fullName"));
387: user.setPassword(attributes.getValue("password"));
388: user.setReplyToAddress(attributes.getValue("replyToAddress"));
389: return (user);
390: }
391:
392: }
|