001 package com.sptci.rwt;
002
003 import java.io.FileNotFoundException;
004 import java.io.Serializable;
005
006 import java.sql.Connection;
007
008 import java.util.Collection;
009 import java.util.Collections;
010 import java.util.TreeMap;
011
012 import com.thoughtworks.xstream.XStream;
013 import com.sptci.util.StringUtilities;
014
015 /**
016 * A serializable wrapper used to represent saved JDBC connections for
017 * the application. This class will be serialised to the following file
018 * and initialised from the same file during application load:
019 *
020 * <pre><sptrwt.data.directory>/connections.xml</pre>
021 *
022 * <p>© Copyright 2007 <a href='http://sptci.com/' target='_new'>Sans Pareil Technologies, Inc.</a></p>
023 * @author Rakesh Vidyadharan 2007-09-24
024 * @version $Id: Connections.java 4123 2008-05-25 21:49:01Z rakesh $
025 */
026 public class Connections implements Serializable
027 {
028 /**
029 * The encoding to use to serialise and deserialise instances of this
030 * class.
031 *
032 * {@value}
033 */
034 public static final String ENCODING = "UTF-8";
035
036 /**
037 * The name of the file to which this class will be serialised.
038 */
039 public static final String FILE_NAME = "connections.xml";
040
041 /**
042 * The system property used to configure the location of the root directory
043 * under which persistent state information for the application is stored.
044 *
045 * {@value}
046 */
047 public static final String DIRECTORY = "sptrwt.data.directory";
048
049 /**
050 * The {@link com.thoughtworks.xstream.XStream} instance used to serialise
051 * and deserialise instances of this class.
052 */
053 protected static final XStream xstream;
054
055 /**
056 * Static initialiser for {@link #xstream}.
057 */
058 static
059 {
060 xstream = new XStream();
061 xstream.alias( "connections", Connections.class );
062
063 xstream.alias( "databaseType", DatabaseType.class );
064 xstream.useAttributeFor( DatabaseType.class, "name" );
065 xstream.useAttributeFor( DatabaseType.class, "driver" );
066 xstream.useAttributeFor( DatabaseType.class, "urlPattern" );
067
068 xstream.alias( "connectionData", ConnectionData.class );
069 }
070
071 /**
072 * A map used to quickly look up {@link DatabaseType} instances by their
073 * {@link DatabaseType#name}.
074 */
075 private final TreeMap<String,DatabaseType> databases;
076
077 /**
078 * The fully qualified file name to use to serialise this instance into.
079 */
080 private transient String fileName;
081
082 /**
083 * Default constructor. Cannot be instantiated.
084 */
085 protected Connections()
086 {
087 databases = new TreeMap<String,DatabaseType>();
088 }
089
090 /**
091 * Create a new instance of the class for the specified user. Saved
092 * files are stored under a directory named after the <code>user</code>
093 * under {@link #DIRECTORY}.
094 *
095 * @see #deserialise
096 * @return The newly initialised instance of the class.
097 * @throws RuntimeException If errors are encountered while deserialising
098 * the persistent state of this instance.
099 */
100 public static Connections getInstance( final String user )
101 {
102 final Connections connections = new Connections();
103 connections.deserialise( user );
104 return connections;
105 }
106
107 /**
108 * Return a {@link java.sql.Connection} for the specified database type
109 * and saved with the specified unique name.
110 *
111 * @see #getConnectionParameters
112 * @see ConnectionFactory#open( ConnectionParameters )
113 * @param databaseType The name of the database engine.
114 * @param name The unique name used to identify the saved connection.
115 * @return The connection object or <code>null</code> if no saved
116 * information can be found for the specified parameters.
117 * @throws ConnectionException If errors are encountered while initiating
118 * the connection.
119 */
120 public Connection getConnection( final String databaseType,
121 final String name ) throws ConnectionException
122 {
123 Connection connection = null;
124 final ConnectionParameters parameters =
125 getConnectionParameters( databaseType, name );
126 if ( parameters != null )
127 {
128 connection = ConnectionFactory.open( parameters );
129 }
130
131 return connection;
132 }
133
134 /**
135 * Return a value object that represents all the connection information
136 * for the specified database type and saved with the specified unique
137 * name.
138 *
139 * @param databaseType The name of the database engine.
140 * @param name The unique name used to identify the saved connection.
141 * @return The appropriate value object or <code>null</code> if no
142 * matching saved connection is found.
143 */
144 public ConnectionParameters getConnectionParameters(
145 final String databaseType, final String name )
146 {
147 ConnectionParameters parameters = null;
148 final DatabaseType type = databases.get( databaseType );
149
150 if ( type != null )
151 {
152 final ConnectionData data = type.getConnectionData( name );
153 if ( data != null )
154 {
155 parameters = new ConnectionParameters(
156 data.getUserName(), data.getPassword(), data.getHost(),
157 data.getPort(), data.getDatabase(), type.getName(),
158 type.getUrlPattern(), type.getDriver() );
159 }
160 }
161
162 return parameters;
163 }
164
165 /**
166 * Add the specified connection parameters value object to the application
167 * persistent state.
168 *
169 * @see #serialise
170 * @param name The unique name to use to identify the saved connection.
171 * @param parameters The parameters to be saved to {@link #FILE_NAME}.
172 */
173 public void add( final String name, final ConnectionParameters parameters )
174 {
175 DatabaseType type = databases.get( parameters.databaseType );
176 if ( type == null )
177 {
178 type = new DatabaseType();
179 type.setName( parameters.databaseType );
180 type.setUrlPattern( parameters.urlPattern );
181 type.setDriver( parameters.driver );
182 databases.put( parameters.databaseType, type );
183 }
184
185 final ConnectionData data = new ConnectionData();
186 data.setHost( parameters.host );
187 data.setPort( parameters.port );
188 data.setDatabase( parameters.database );
189 data.setUserName( parameters.userName );
190 data.setPassword( parameters.password );
191
192 type.add( name, data );
193 serialise();
194 }
195
196 /**
197 * Remove the specified connection parameters from the application
198 * persistent state.
199 *
200 * @see #serialise
201 * @param databaseType The database type under which the connection was
202 * saved.
203 * @param name The unique name to use to identify the saved connection.
204 */
205 public void delete( final String databaseType, final String name )
206 {
207 final DatabaseType type = databases.get( databaseType );
208 if ( type != null ) type.delete( name );
209 serialise();
210 }
211
212 /**
213 * Remove the specified database from persistent state. This removes
214 * all saved connections for that database engine.
215 *
216 * @see #serialise
217 * @param name The name of the database engine to remove from saved state.
218 */
219 public void delete( final String name )
220 {
221 databases.remove( name );
222 serialise();
223 }
224
225 /**
226 * Deserialise the contents of {@link #FILE_NAME} into this instance.
227 *
228 * @param user The name of the user to use to construct the full filename.
229 * @throws RuntimeException If errors are encountered while deserialising
230 * the persistent state. No exceptions are thrown if the file does
231 * not exist.
232 */
233 protected void deserialise( final String user ) throws RuntimeException
234 {
235 final String separator =
236 System.getProperties().getProperty( "file.separator" );
237
238 final StringBuilder builder = new StringBuilder( 64 );
239 builder.append( System.getProperty( DIRECTORY ) );
240 builder.append( separator );
241 builder.append( user );
242 builder.append( separator );
243 builder.append( FILE_NAME );
244 fileName = builder.toString();
245
246 try
247 {
248 final String xml = StringUtilities.fromFile( fileName, ENCODING );
249 xstream.fromXML( xml, this );
250 }
251 catch ( FileNotFoundException fnfe ) {}
252 catch ( Throwable t )
253 {
254 throw new RuntimeException( "Error deserialising saved instance", t );
255 }
256 }
257
258 /**
259 * Serialise this instance to the {@link #FILE_NAME}.
260 *
261 * @throws RuntimeException If errors are encountered while serialising
262 * the instance.
263 */
264 protected void serialise() throws RuntimeException
265 {
266 try
267 {
268 final String xml = xstream.toXML( this );
269 StringUtilities.toFile( xml, fileName, ENCODING );
270 }
271 catch ( Throwable t )
272 {
273 throw new RuntimeException( t );
274 }
275 }
276
277 /**
278 * Returns the {@link DatabaseType} identified by the {@link DatabaseType#name}
279 * parameter specified.
280 *
281 * @param name The name to use to fetch the database type.
282 * @return The database type associated with the <code>name</code> or
283 * <code>null</code> if no such database type exists.
284 */
285 public DatabaseType getDatabaseType( final String name )
286 {
287 return databases.get( name );
288 }
289
290 /**
291 * Returns {@link #databases}.
292 *
293 * @return A read only view of the collection.
294 */
295 public Collection<DatabaseType> getDatabaseTypes()
296 {
297 return Collections.unmodifiableCollection( databases.values() );
298 }
299
300 /**
301 * Returns the collection of {@link DatabaseType#name} values stored as
302 * <code>key</code> in {@link #databases}.
303 *
304 * @return The collection of names.
305 */
306 public Collection<String> getDatabaseTypeNames()
307 {
308 return Collections.unmodifiableCollection( databases.keySet() );
309 }
310 }