View Javadoc

1   /*
2    * $Id: MemoryUserDatabase.java 471754 2006-11-06 14:55:09Z husted $
3    *
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *  http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  package org.apache.struts.apps.mailreader.dao.impl.memory;
22  
23  
24  import java.io.BufferedInputStream;
25  import java.io.File;
26  import java.io.FileInputStream;
27  import java.io.FileOutputStream;
28  import java.io.IOException;
29  import java.io.OutputStreamWriter;
30  import java.io.PrintWriter;
31  import java.util.HashMap;
32  
33  import org.apache.commons.digester.Digester;
34  import org.apache.commons.digester.ObjectCreationFactory;
35  import org.apache.commons.logging.Log;
36  import org.apache.commons.logging.LogFactory;
37  import org.apache.struts.apps.mailreader.dao.Subscription;
38  import org.apache.struts.apps.mailreader.dao.User;
39  import org.apache.struts.apps.mailreader.dao.UserDatabase;
40  import org.xml.sax.Attributes;
41  
42  
43  /**
44   * <p>Concrete implementation of {@link UserDatabase} for an in-memory
45   * database backed by an XML data file.</p>
46   *
47   * @version $Rev: 471754 $ $Date: 2006-11-06 08:55:09 -0600 (Mon, 06 Nov 2006) $
48   * @since Struts 1.1
49   */
50  
51  public class MemoryUserDatabase implements UserDatabase {
52  
53  
54      // ----------------------------------------------------------- Constructors
55  
56  
57      // ----------------------------------------------------- Instance Variables
58  
59  
60      /**
61       * Logging output for this user database instance.
62       */
63      private Log log = LogFactory.getLog(this.getClass());
64  
65  
66      /**
67       * The {@link User}s associated with this UserDatabase, keyed by username.
68       */
69      private HashMap users = new HashMap();
70  
71      private boolean open = false;
72  
73  
74      // ------------------------------------------------------------- Properties
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      // --------------------------------------------------------- Public Methods
99  
100 
101     // See interface for Javadoc
102     public void close() throws Exception {
103 
104         save();
105         this.open = false;
106 
107     }
108 
109 
110     // See interface for Javadoc
111     public User createUser(String username) {
112 
113         synchronized (users) {
114             if (users.get(username) != null) {
115                 throw new IllegalArgumentException("Duplicate user '" +
116                                                    username + "'");
117             }
118             if (log.isTraceEnabled()) {
119                 log.trace("Creating user '" + username + "'");
120             }
121             MemoryUser user = new MemoryUser(this, username);
122             synchronized (users) {
123                 users.put(username, user);
124             }
125             return (user);
126         }
127 
128     }
129 
130 
131     // See interface for Javadoc
132     public User findUser(String username)  {
133 
134         synchronized (users) {
135             return ((User) users.get(username));
136         }
137 
138     }
139 
140 
141     // See interface for Javadoc
142     public User[] findUsers() {
143 
144         synchronized (users) {
145             User results[] = new User[users.size()];
146             return ((User[]) users.values().toArray(results));
147         }
148 
149     }
150 
151 
152     // See interface for Javadoc
153     public void open() throws Exception {
154 
155         FileInputStream fis = null;
156         BufferedInputStream bis = null;
157 
158         try {
159 
160             // Acquire an input stream to our database file
161             if (log.isDebugEnabled()) {
162                 log.debug("Loading database from '" + pathname + "'");
163             }
164             fis = new FileInputStream(pathname);
165             bis = new BufferedInputStream(fis);
166 
167             // Construct a digester to use for parsing
168             Digester digester = new Digester();
169             digester.push(this);
170             digester.setValidating(false);
171             digester.addFactoryCreate
172                 ("database/user",
173                  new MemoryUserCreationFactory(this));
174             digester.addFactoryCreate
175                 ("database/user/subscription",
176                  new MemorySubscriptionCreationFactory());
177 
178             // Parse the input stream to initialize our database
179             digester.parse(bis);
180             bis.close();
181             bis = null;
182             fis = null;
183             this.open = true;
184 
185         } catch (Exception e) {
186 
187             log.error("Loading database from '" + pathname + "':", e);
188             throw e;
189 
190         } finally {
191 
192             if (bis != null) {
193                 try {
194                     bis.close();
195                 } catch (Throwable t) {
196                     // do nothing
197                 }
198                 bis = null;
199                 fis = null;
200             }
201 
202         }
203 
204     }
205 
206 
207     // See interface for Javadoc
208     public void removeUser(User user) {
209 
210         if (!(this == user.getDatabase())) {
211             throw new IllegalArgumentException
212                 ("User not associated with this database");
213         }
214         if (log.isTraceEnabled()) {
215             log.trace("Removing user '" + user.getUsername() + "'");
216         }
217         synchronized (users) {
218             users.remove(user.getUsername());
219         }
220 
221     }
222 
223 
224     // See interface for Javadoc
225     public void save() throws Exception {
226 
227         if (log.isDebugEnabled()) {
228             log.debug("Saving database to '" + pathname + "'");
229         }
230         File fileNew = new File(pathnameNew);
231         PrintWriter writer = null;
232 
233         try {
234 
235             // Configure our PrintWriter
236             FileOutputStream fos = new FileOutputStream(fileNew);
237             OutputStreamWriter osw = new OutputStreamWriter(fos);
238             writer = new PrintWriter(osw);
239 
240             // Print the file prolog
241             writer.println("<?xml version='1.0'?>");
242             writer.println("<database>");
243 
244             // Print entries for each defined user and associated subscriptions
245             User yusers[] = findUsers();
246             for (int i = 0; i < yusers.length; i++) {
247                 writer.print("  ");
248                 writer.println(yusers[i]);
249                 Subscription subscriptions[] =
250                     yusers[i].getSubscriptions();
251                 for (int j = 0; j < subscriptions.length; j++) {
252                     writer.print("    ");
253                     writer.println(subscriptions[j]);
254                     writer.print("    ");
255                     writer.println("</subscription>");
256                 }
257                 writer.print("  ");
258                 writer.println("</user>");
259             }
260 
261             // Print the file epilog
262             writer.println("</database>");
263 
264             // Check for errors that occurred while printing
265             if (writer.checkError()) {
266                 writer.close();
267                 fileNew.delete();
268                 throw new IOException
269                     ("Saving database to '" + pathname + "'");
270             }
271             writer.close();
272             writer = null;
273 
274         } catch (IOException e) {
275 
276             if (writer != null) {
277                 writer.close();
278             }
279             fileNew.delete();
280             throw e;
281 
282         }
283 
284 
285         // Perform the required renames to permanently save this file
286         File fileOrig = new File(pathname);
287         File fileOld = new File(pathnameOld);
288         if (fileOrig.exists()) {
289             fileOld.delete();
290             if (!fileOrig.renameTo(fileOld)) {
291                 throw new IOException
292                     ("Renaming '" + pathname + "' to '" + pathnameOld + "'");
293             }
294         }
295         if (!fileNew.renameTo(fileOrig)) {
296             if (fileOld.exists()) {
297                 fileOld.renameTo(fileOrig);
298             }
299             throw new IOException
300                 ("Renaming '" + pathnameNew + "' to '" + pathname + "'");
301         }
302         fileOld.delete();
303 
304     }
305 
306     public boolean isOpen() {
307         return this.open;
308     }
309 
310 
311 
312 
313 }
314 
315 
316 /**
317  * Digester object creation factory for subscription instances.
318  */
319 class MemorySubscriptionCreationFactory implements ObjectCreationFactory {
320 
321     private Digester digester = null;
322 
323     public Digester getDigester() {
324         return (this.digester);
325     }
326 
327     public void setDigester(Digester digester) {
328         this.digester = digester;
329     }
330 
331     public Object createObject(Attributes attributes) {
332         String host = attributes.getValue("host");
333         User user = (User) digester.peek();
334         Subscription subscription = user.createSubscription(host);
335         String autoConnect = attributes.getValue("autoConnect");
336         if (autoConnect == null) {
337             autoConnect = "false";
338         }
339         if ("true".equalsIgnoreCase(autoConnect) ||
340             "yes".equalsIgnoreCase(autoConnect)) {
341             subscription.setAutoConnect(true);
342         } else {
343             subscription.setAutoConnect(false);
344         }
345         subscription.setPassword(attributes.getValue("password"));
346         subscription.setType(attributes.getValue("type"));
347         subscription.setUsername(attributes.getValue("username"));
348         return (subscription);
349     }
350 
351 }
352 
353 
354 /**
355  * Digester object creation factory for user instances.
356  */
357 class MemoryUserCreationFactory implements ObjectCreationFactory {
358 
359     public MemoryUserCreationFactory(MemoryUserDatabase database) {
360         this.database = database;
361     }
362 
363     private MemoryUserDatabase database = null;
364 
365     private Digester digester = null;
366 
367     public Digester getDigester() {
368         return (this.digester);
369     }
370 
371     public void setDigester(Digester digester) {
372         this.digester = digester;
373     }
374 
375     public Object createObject(Attributes attributes) {
376         String username = attributes.getValue("username");
377         User user = database.createUser(username);
378         user.setFromAddress(attributes.getValue("fromAddress"));
379         user.setFullName(attributes.getValue("fullName"));
380         user.setPassword(attributes.getValue("password"));
381         user.setReplyToAddress(attributes.getValue("replyToAddress"));
382         return (user);
383     }
384 
385 }