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  
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      // ----------------------------------------------------------- Constructors
57  
58  
59      // ----------------------------------------------------- Instance Variables
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      // ------------------------------------------------------------- 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     /**
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             // Acquire an input stream to our database file
183             if (log.isDebugEnabled()) {
184                 log.debug("Loading database from '" + pathname + "'");
185             }
186             fis = new FileInputStream(pathname);
187             bis = new BufferedInputStream(fis);
188 
189             // Construct a digester to use for parsing
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             // Parse the input stream to initialize our database
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             // Configure our PrintWriter
268             FileOutputStream fos = new FileOutputStream(fileNew);
269             OutputStreamWriter osw = new OutputStreamWriter(fos);
270             writer = new PrintWriter(osw);
271 
272             // Print the file prolog
273             writer.println("<?xml version='1.0'?>");
274             writer.println("<database>");
275 
276             // Print entries for each defined user and associated subscriptions
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             // Print the file epilog
294             writer.println("</database>");
295 
296             // Check for errors that occurred while printing
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         // Perform the required renames to permanently save this file
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 }