View Javadoc

1   /*
2    * Copyright 1999-2002,2004 The Apache Software Foundation.
3    * 
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    * 
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    * 
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  
18  package org.apache.struts.webapp.example.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.example.Subscription;
34  import org.apache.struts.webapp.example.User;
35  import org.apache.struts.webapp.example.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: 421493 $ $Date: 2006-07-12 20:52:31 -0700 (Wed, 12 Jul 2006) $
45   * @since Struts 1.1
46   */
47  
48  public final class MemoryUserDatabase implements UserDatabase {
49  
50  
51      // ----------------------------------------------------------- Constructors
52  
53  
54      // ----------------------------------------------------- Instance Variables
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      // ------------------------------------------------------------- Properties
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      // --------------------------------------------------------- Public Methods
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             // Acquire an input stream to our database file
178             if (log.isDebugEnabled()) {
179                 log.debug("Loading database from '" + pathname + "'");
180             }
181             fis = new FileInputStream(pathname);
182             bis = new BufferedInputStream(fis);
183 
184             // Construct a digester to use for parsing
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             // Parse the input stream to initialize our database
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             // Configure our PrintWriter
263             FileOutputStream fos = new FileOutputStream(fileNew);
264             OutputStreamWriter osw = new OutputStreamWriter(fos);
265             writer = new PrintWriter(osw);
266 
267             // Print the file prolog
268             writer.println("<?xml version='1.0'?>");
269             writer.println("<database>");
270 
271             // Print entries for each defined user and associated subscriptions
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             // Print the file epilog
289             writer.println("</database>");
290 
291             // Check for errors that occurred while printing
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         // Perform the required renames to permanently save this file
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     private Digester digester = null;
346 
347     public Digester getDigester() {
348         return (this.digester);
349     }
350 
351     public void setDigester(Digester digester) {
352         this.digester = digester;
353     }
354 
355     public Object createObject(Attributes attributes) {
356         String host = attributes.getValue("host");
357         User user = (User) digester.peek();
358         Subscription subscription = user.createSubscription(host);
359         String autoConnect = attributes.getValue("autoConnect");
360         if (autoConnect == null) {
361             autoConnect = "false";
362         }
363         if ("true".equalsIgnoreCase(autoConnect) ||
364             "yes".equalsIgnoreCase(autoConnect)) {
365             subscription.setAutoConnect(true);
366         } else {
367             subscription.setAutoConnect(false);
368         }
369         subscription.setPassword(attributes.getValue("password"));
370         subscription.setType(attributes.getValue("type"));
371         subscription.setUsername(attributes.getValue("username"));
372         return (subscription);
373     }
374 
375 }
376 
377 
378 /***
379  * Digester object creation factory for user instances.
380  */
381 class MemoryUserCreationFactory implements ObjectCreationFactory {
382 
383     public MemoryUserCreationFactory(MemoryUserDatabase database) {
384         this.database = database;
385     }
386 
387     private MemoryUserDatabase database = null;
388 
389     private Digester digester = null;
390 
391     public Digester getDigester() {
392         return (this.digester);
393     }
394 
395     public void setDigester(Digester digester) {
396         this.digester = digester;
397     }
398 
399     public Object createObject(Attributes attributes) {
400         String username = attributes.getValue("username");
401         User user = database.createUser(username);
402         user.setFromAddress(attributes.getValue("fromAddress"));
403         user.setFullName(attributes.getValue("fullName"));
404         user.setPassword(attributes.getValue("password"));
405         user.setReplyToAddress(attributes.getValue("replyToAddress"));
406         return (user);
407     }
408 
409 }