1 /* DatabaseLocationParser.java
2  *
3  * created: Jun 2013
4  *
5  * This file is part of Artemis
6  *
7  * Copyright (C) 2001  Genome Research Limited
8  *
9  * This program is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU General Public License
11  * as published by the Free Software Foundation; either version 2
12  * of the License, or (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
22  *
23  */
24 package uk.ac.sanger.artemis.util;
25 
26 import java.net.URI;
27 import java.net.URISyntaxException;
28 import java.util.HashMap;
29 import java.util.Map;
30 import java.util.Set;
31 
32 /**
33  *
34  * @author Eric Rasche <rasche.eric@yandex.ru>
35  */
36 public class DatabaseLocationParser {
37 
38     private String host;
39     private String database;
40     private int port = 0;
41     private String db_engine = "postgresql";
42     private String protocol = "jdbc";
43     private static org.apache.log4j.Logger logger4j =
44             org.apache.log4j.Logger.getLogger(DatabaseLocationParser.class);
45     /**
46      * Desire use of Protocol in the final URL. i.e., "jdbc:"
47      */
48     public static final int PROTOCOL = 1;
49     /**
50      * Desire use of scheme in final url. i.e., "postgres://"
51      */
52     public static final int SCHEME = 2;
53     /**
54      * Desire use of database name in final url.
55      *
56      */
57     public static final int DATABASE_NAME = 4;
58     /**
59      * Desire listing of query parameters in final url. e.g.,
60      * "user=name&ssl=true"
61      */
62     public static final int QUERY_PARAMS = 8;
63     private Map<String, String> params = new HashMap<String, String>();
64 
65     /**
66      * Empty initializer
67      */
DatabaseLocationParser()68     public DatabaseLocationParser() {
69     }
70 
71     /**
72      * Create a new DLP object from a given URL
73      *
74      * @param url
75      */
DatabaseLocationParser(String url)76     public DatabaseLocationParser(String url) {
77         setFromURLString(url);
78     }
79 
80     /**
81      * Set the URL internally and parse out important portions.
82      *
83      * @param url
84      */
setFromURLString(String url)85     public void setFromURLString(String url) {
86         logger4j.debug("DLP was called with a URL of [" + url + "]");
87         try {
88             //"jdbc:postgres://localhost:5432/drupal6?user=drupal6&ssl=true";
89 
90             //If it's prefixed, remove that so URI parsing is correct
91             if (url.startsWith("jdbc:")) {
92                 url = url.substring(5);
93             }
94             if (!url.startsWith(db_engine + "://")) {
95                 url = db_engine + "://" + url;
96             }
97 
98 
99             URI db_loc = new URI(url);
100 
101             logger4j.debug("URI " + db_loc.toString());
102             logger4j.debug("Host: " + db_loc.getHost());
103             logger4j.debug("Port: " + db_loc.getPort());
104             logger4j.debug("Engine: " + db_loc.getScheme());
105             logger4j.debug("DB: " + db_loc.getPath());
106 
107             host = db_loc.getHost();
108 
109             database = db_loc.getPath().substring(1);
110 
111             port = db_loc.getPort();
112 
113             db_engine = db_loc.getScheme();
114 
115             //Split on '&' and parse each subunit
116             String[] query_params = db_loc.getQuery().split("&");
117             for (int i = 0; i < query_params.length; i++) {
118                 //Split based on the equals sign
119                 logger4j.debug("Given a parameter:" + query_params[i]);
120                 String[] parts = query_params[i].split("=");
121 
122 
123                 //This will fail for input like user=chad=o&ssl=true
124                 //As we'll only grab user=chad
125                 // Then again, who has an equals sign in their username
126                 if (parts.length > 1) {
127                     params.put(parts[0], parts[1]);
128                     logger4j.debug("[" + parts[0] + "," + parts[1] + "]");
129 
130                 } else {
131                     // This might fail strangely, but then again they're providing funky URLs
132                     params.put("user", parts[0]);
133                     logger4j.debug("[user," + parts[0] + "]");
134                 }
135             }
136         } catch (URISyntaxException ex) {
137             logger4j.warn("Error parsing URL [" + url + "]" + ex);
138         }
139         logger4j.debug("This has a complete_url of [" + getCompleteURL() + "]");
140     }
141 
142     /**
143      * Returns the complete URL
144      *
145      * @return complete url
146      */
getCompleteURL()147     public String getCompleteURL() {
148         return getURLWithFixes(PROTOCOL | SCHEME
149                 | DATABASE_NAME | QUERY_PARAMS);
150     }
151 
152     /**
153      * Returns the URL as required for a DriverManager.getConnection object
154      *
155      * @return url as required for a SQL connection
156      */
getConnectionString()157     public String getConnectionString() {
158         return getURLWithFixes(PROTOCOL | SCHEME
159                 | DATABASE_NAME | QUERY_PARAMS);
160     }
161 
162     /**
163      * Returns the unprefixed URL, for classes that automatically prepend
164      * 'jdbc:postgres://'.
165      *
166      * This is important for uk/ac/sanger/artemis/chado/DbSqlConfig.java
167      *
168      * @return unprefixed url with database and query parameters appended.
169      */
getUnprefixedURL()170     public String getUnprefixedURL() {
171         return getURLWithFixes(DATABASE_NAME | QUERY_PARAMS);
172     }
173 
174     /**
175      * Returns a URL with a selection of modifications.
176      *
177      * Using a binary OR, one can select which modifications should be applied
178      * to create the final URL. These modifications consist of:
179      *
180      * - PROTOCOL: "jdbc:" - SCHEME: "postgresql://" - DATABASE_NAME: The
181      * supplied database name - QUERY_PARAMS: The supplied query parameters
182      * (user, ssl, etc)
183      *
184      * @param modifications, a binary OR'd selection of PROTOCOL, SCHEME,
185      * DATABASE_NAME, QUERY_PARAMS, all of which are available as public final
186      * static integers from this class
187      * @return String version of a URL, modified according to the rules
188      * supplied.
189      */
getURLWithFixes(int modifications)190     private String getURLWithFixes(int modifications) {
191         try {
192             String scheme = new String(db_engine);
193             String userInfo = null;
194             int db_port = new Integer(port);
195             String db_name = "/" + database;
196             String query_params = "";
197             String fragment = null;
198 
199             String result = "";
200             if ((modifications & PROTOCOL) == PROTOCOL) {
201                 result += protocol + ":";
202             }
203             if ((modifications & SCHEME) != SCHEME) {
204                 scheme = null;
205             }
206             if ((modifications & DATABASE_NAME) != DATABASE_NAME) {
207                 db_name = null;
208             }
209 
210             // Query Parameters
211             // "user=chado_user&ssl=true"
212             if ((modifications & QUERY_PARAMS) == QUERY_PARAMS) {
213                 if (params.size() > 0) {
214                     /**
215                      * Handling of the query parameters. There are other ways to
216                      * do this, but it's probably never going to be more than a
217                      * "user" and an "ssl" parameter, which means that in the
218                      * grand scheme of things, joining a couple strings together
219                      * isn't a big issue
220                      */
221                     Set<String> keys = params.keySet();
222                     java.util.Iterator<String> it = keys.iterator();
223                     while (it.hasNext()) {
224                         String key = it.next();
225                         query_params += key + "=" + params.get(key) + "&";
226                     }
227                     query_params = query_params.substring(0, query_params.length() - 1);
228                 }
229             }
230             URI uri_result = new URI(scheme, userInfo, host, db_port, db_name, query_params, fragment);
231             logger4j.debug("Pre-final URL: " + uri_result.toString());
232 
233             result = result + uri_result.toString();
234             //Bugfix. Even if SCHEME is null, // is still prepended. so we remove
235             if ((modifications & SCHEME) != SCHEME && (modifications & PROTOCOL) != PROTOCOL) {
236                 result = result.substring(2);
237             }
238             return result;
239         } catch (URISyntaxException ex) {
240             logger4j.error("Could not construct URL. This will likely cause an "
241                     + "SQL connection failure. ");
242         }
243         return null;
244     }
245 
246     /**
247      * Checks whether SSL is enabled
248      *
249      * @return true if SSL is enabled
250      */
isSSLEnabled()251     public boolean isSSLEnabled() {
252         if (params.containsKey("ssl")) {
253             return params.get("ssl").equals("true");
254         } else {
255             return false;
256         }
257     }
258 
259     /**
260      * Returns the hostname
261      *
262      * @return the hostname
263      */
getHost()264     public String getHost() {
265         return host;
266     }
267 
268     /**
269      * Returns the database
270      *
271      * @return the database name
272      */
getDatabase()273     public String getDatabase() {
274         return database;
275     }
276 
277     /**
278      * Returns the port number
279      *
280      * @return port number
281      */
getPort()282     public int getPort() {
283         return port;
284     }
285 
286     /**
287      * Returns the username of the user connecting to the database
288      *
289      * @return username
290      */
getUsername()291     public String getUsername() {
292         if (params.containsKey("user")) {
293             return params.get("user");
294         } else {
295             return "chado"; //That's the default u/n afaik
296         }
297     }
298 
299     /**
300      * Returns the database engine
301      *
302      * @return database engine (usu. postgresql)
303      */
getDBEngine()304     public String getDBEngine() {
305         return db_engine;
306     }
307 
308     /**
309      * Sets the hostname
310      *
311      * @param hostname
312      */
setHost(String hostname)313     public void setHost(String hostname) {
314         host = hostname.trim();
315     }
316 
317     /**
318      * Sets the database name
319      *
320      * @param new_db_name
321      */
setDatabase(String new_db_name)322     public void setDatabase(String new_db_name) {
323         database = new_db_name.trim();
324     }
325 
326     /**
327      * Sets the port number
328      *
329      * @param new_port_number
330      */
setPort(String new_port_number)331     public void setPort(String new_port_number) {
332         port = Integer.parseInt(new_port_number.trim());
333     }
334 
335     /**
336      * Sets the port number
337      *
338      * @param new_port_number
339      */
setPort(int new_port_number)340     public void setPort(int new_port_number) {
341         port = new_port_number;
342     }
343 
344     /**
345      * Sets the username to connect with
346      *
347      * @param new_username
348      */
setUsername(String new_username)349     public void setUsername(String new_username) {
350         params.put("user", new_username.trim());
351     }
352 
353     /**
354      * Enables or disables SSL in connection URL
355      *
356      * @param is_enabled
357      */
setSSL(boolean is_enabled)358     public void setSSL(boolean is_enabled) {
359         if (is_enabled) {
360             params.put("ssl", "true");
361         } else {
362             params.remove("ssl");
363         }
364     }
365 
366     /**
367      * Sets the Database Engine
368      *
369      * @param new_db_engine
370      */
setDBEngine(String new_db_engine)371     public void setDBEngine(String new_db_engine) {
372         db_engine = new_db_engine.trim();
373     }
374 }
375