View Javadoc

1   /*
2    * Copyright (c) 2003, Andreas Pataki 
3    * All rights reserved.
4    *
5    * Redistribution and use in source and binary forms, with or without
6    * modification, are permitted provided that the following conditions are
7    * met:
8    *
9    * Redistributions of source code must retain the above copyright notice,
10   * this list of conditions and the following disclaimer.
11   * 
12   * Redistributions in binary form must reproduce the above copyright notice,
13   * this list of conditions and the following disclaimer in the documentation
14   * and/or other materials provided with the distribution.
15   * 
16   * Neither the name of the author nor the names of its contributors
17   * may be used to endorse or promote products derived from this software
18   * without specific prior written permission.
19   *
20   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS
21   * IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
22   * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23   * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
24   * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25   * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26   * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27   * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28   * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30   * POSSIBILITY OF SUCH DAMAGE.
31   */
32  
33  package net.sf.tm.plugin;
34  
35  import java.io.FileNotFoundException;
36  import java.io.FileReader;
37  import java.io.FileWriter;
38  import java.io.IOException;
39  import java.util.ArrayList;
40  import java.util.Iterator;
41  
42  import org.apache.commons.codec.binary.Base64;
43  import org.eclipse.core.runtime.IPath;
44  import org.eclipse.ui.IMemento;
45  import org.eclipse.ui.WorkbenchException;
46  import org.eclipse.ui.XMLMemento;
47  
48  import net.sf.tm.tomcat.TomcatServer;
49  
50  /***
51   * Implementation of a persistence store for server objects. All modifications
52   * off the store are saved immediately.
53   * 
54   * <p>The store is saved as a XMLMemento und the filename server.xml in the
55   * plugin directory.
56   * 
57   * @author Andreas Pataki
58   * @version $Id: ServerStore.java,v 1.9 2004/01/25 10:37:15 apataki Exp $
59   */
60  public final class ServerStore {
61      
62      /***
63       * Prefix for password
64       */
65      private static final String CRYPT_PREFIX = "%4E$";
66      
67      /***
68       * Definition of the config filename.  
69       */
70      private static final String CONFIG = "server.xml";
71  
72      /***
73       * Root Element
74       */
75      private static final String ELEMENT_SERVERLIST = "server-list";
76      /***
77       * Element for a single server
78       */
79      private static final String ELEMENT_SERVER = "server";
80      /***
81       * Server name
82       */
83      private static final String ATTR_NAME = "name";
84      /***
85       * Host attribute
86       */
87      private static final String ATTR_HOST = "host";
88      /***
89       * Port Attribute, port is an integer value
90       */
91      private static final String ATTR_PORT = "port";
92      /***
93       * Protocol attribute
94       */
95      private static final String ATTR_PROTOCOL = "protocol";
96      /***
97       * Manager context attribute
98       */
99      private static final String ATTR_MANAGER = "manager";
100     /***
101      * User attribute
102      */
103     private static final String ATTR_USER = "user";
104     /***
105      * password attribute
106      */
107     private static final String ATTR_PWD = "password";
108 
109     /***
110      * Initial size of the store. 
111      */
112     private static final int INITIAL_SIZE = 5;
113 
114     /***
115      * Actual store containing the @{link TomcatServer} objects. 
116      */
117     private ArrayList store;
118 
119     /***
120      * Singleton instance 
121      */
122     private static ServerStore instance = new ServerStore();
123 
124     /***
125      * Default Constructor 
126      */
127     private ServerStore() {
128 
129         // create empty store
130         store = new ArrayList(INITIAL_SIZE);
131         loadStore();
132     }
133 
134     /***
135      * Tries to load a server store. If no store is found the resulting error
136      * is ignored and it's assumed that a new store has to be initialized
137      * when the data get's saved the next time. 
138      */
139     private void loadStore() {
140         try {
141             IPath path =
142                 ContextManagerPlugin.getDefault().getStateLocation().append(
143                     CONFIG);
144 
145             if (Logger.isDebugEnabled()) {
146                 Logger.debug("Loading store from " + path.toString());
147             }
148 
149             // read root document
150             XMLMemento memento =
151                 XMLMemento.createReadRoot(new FileReader(path.toFile()));
152 
153             // process all server elements
154             IMemento[] servers = memento.getChildren(ELEMENT_SERVER);
155             for (int i = 0; i < servers.length; i++) {
156                 IMemento xmlServer = servers[i];
157                 TomcatServer server = new TomcatServer();
158 
159                 server.setHost(xmlServer.getString(ATTR_HOST));
160                 server.setPort(xmlServer.getInteger(ATTR_PORT).intValue());
161                 server.setProtocol(xmlServer.getString(ATTR_PROTOCOL));
162                 server.setManager(xmlServer.getString(ATTR_MANAGER));
163                 server.setUser(xmlServer.getString(ATTR_USER));
164                 server.setPassword(
165                     decryptPassword(xmlServer.getString(ATTR_PWD)));
166 
167                 // handle server name
168                 String serverName = xmlServer.getString(ATTR_NAME);
169                 if (serverName == null) {
170                     // name not available, use host:port as name
171                     serverName = server.getHost() + ":" + server.getPort();
172                 }
173                 server.setName(serverName);
174 
175                 add(server);
176 
177             }
178 
179         } catch (WorkbenchException e) {
180 
181             Logger.error("error loading server store", e);
182 
183         } catch (FileNotFoundException e) {
184             // config does not exist
185             if (Logger.isDebugEnabled()) {
186                 Logger.debug("Store doesn't exist, create new one.");
187             }
188         }
189     }
190 
191     /***
192      * Saves the server store to disk. Filename is specified in {@link CONFIG}.
193      */
194     private void saveStore() {
195         // create root element
196         XMLMemento memento = XMLMemento.createWriteRoot(ELEMENT_SERVERLIST);
197 
198         // iterate over all servers and create server elements
199         for (Iterator i = store.iterator(); i.hasNext();) {
200             TomcatServer server = (TomcatServer) i.next();
201 
202             IMemento child = memento.createChild(ELEMENT_SERVER);
203             child.putString(ATTR_NAME, server.getName());
204             child.putString(ATTR_HOST, server.getHost());
205             child.putString(ATTR_MANAGER, server.getManager());
206             child.putInteger(ATTR_PORT, server.getPort());
207             child.putString(ATTR_PROTOCOL, server.getProtocol());
208             child.putString(ATTR_USER, server.getUser());
209             child.putString(ATTR_PWD, encryptPassword(server.getPassword()));
210         }
211 
212         try {
213             memento.save(
214                 new FileWriter(
215                     ContextManagerPlugin
216                         .getDefault()
217                         .getStateLocation()
218                         .append(CONFIG)
219                         .toFile()));
220 
221             if (Logger.isDebugEnabled()) {
222                 Logger.debug("server store saved");
223             }
224 
225         } catch (IOException e) {
226 
227             Logger.error("error saving server store", e);
228 
229         }
230 
231     }
232 
233     /***
234      * @return the current size of the store 
235      */
236     public int getSize() {
237         return store.size();
238     }
239 
240     /***
241      * Adds a new server to the store. The store will get saved.
242      * 
243      * @param server New server
244      */
245     public void add(TomcatServer server) {
246         store.add(server);
247         saveStore();
248     }
249 
250     /***
251      * Removes a server from the store. The store will get saved.
252      * 
253      * @param server Server which shall be deleted.
254      */
255     public void remove(TomcatServer server) {
256         int index = store.indexOf(server);
257         if (index >= 0) {
258             store.remove(index);
259         }
260         saveStore();
261     }
262 
263     /***
264      * @param server Server to look for
265      * @return true if the server is allready in the store
266      */
267     public boolean contains(TomcatServer server) {
268         return store.contains(server);
269     }
270 
271     /***
272      * @param index Index of the server
273      * @return The server found at the given index
274      */
275     public TomcatServer get(int index) {
276         return (TomcatServer) store.get(index);
277     }
278 
279     /***
280      * @return Iterator of the server store
281      */
282     public Iterator iterator() {
283         return store.iterator();
284     }
285 
286     /***
287      * Sets the given seren. The old entry is replaced by this new one. 
288      * 
289      * @param server Server to be updated
290      */
291     public void set(TomcatServer server) {
292         store.set(store.indexOf(server), server);
293         saveStore();
294     }
295 
296     /***
297      * @return singelton instance
298      */
299     public static ServerStore getInstance() {
300         return instance;
301     }
302 
303     /***
304      * Encrypts the given password. The encrypted password is prefixed 
305      * with <b>%4E$</b>.  
306      * 
307      * @param original Plain text password
308      * @return Encrypted password
309      */
310     private String encryptPassword(String original) {
311         String crypted = new String(Base64.encodeBase64(original.getBytes()));
312         return CRYPT_PREFIX + crypted;
313     }
314 
315     /***
316      * Decrypts the given encrypted password. If the password doesn't start 
317      * with <b>%4E$</b> the password is not encrypted and thus the given 
318      * password is returned.
319      * 
320      * @param crypted The encrypted password
321      * @return The decrypted password
322      */
323     private String decryptPassword(String crypted) {
324         if (crypted.startsWith(CRYPT_PREFIX)) {
325             return new String(
326                 Base64.decodeBase64(
327                     crypted.substring(CRYPT_PREFIX.length()).getBytes()));
328         } else {
329             return crypted;
330         }
331     }
332 
333 }