View Javadoc

1   /*
2    jGuard is a security framework based on top of jaas (java authentication and authorization security).
3    it is written for web applications, to resolve simply, access control problems.
4    version $Name$
5    http://sourceforge.net/projects/jguard/
6   
7    Copyright (C) 2004  Charles GAY
8   
9    This library is free software; you can redistribute it and/or
10   modify it under the terms of the GNU Lesser General Public
11   License as published by the Free Software Foundation; either
12   version 2.1 of the License, or (at your option) any later version.
13  
14   This library 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 GNU
17   Lesser General Public License for more details.
18  
19   You should have received a copy of the GNU Lesser General Public
20   License along with this library; if not, write to the Free Software
21   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22  
23  
24   jGuard project home page:
25   http://sourceforge.net/projects/jguard/
26  
27   */
28  package net.sf.jguard.ext.database;
29  
30  import java.sql.Connection;
31  import java.sql.DatabaseMetaData;
32  import java.sql.PreparedStatement;
33  import java.sql.ResultSet;
34  import java.sql.SQLException;
35  import java.util.Arrays;
36  import java.util.Iterator;
37  import java.util.List;
38  import java.util.Properties;
39  import org.slf4j.Logger;
40  import org.slf4j.LoggerFactory;
41  
42  /**
43   * utility class to create tables, sequences and foreign keys.
44   * @author <a href="mailto:diabolo512@users.sourceforge.net">Charles Gay</a>
45   */
46  public class DatabaseUtils {
47  	private static final Logger logger = LoggerFactory.getLogger(DatabaseUtils.class.getName());
48  
49  	/**
50  	 * create the missing table, sequences and foreign_keys.
51  	 *
52  	 * @param props
53  	 *            file containing sql queries
54  	 * @param connFactory connection factory
55  	 *            jdbc connection
56  	 * @param tablesNames
57  	 *            tables to create
58  	 * @param sequencesNames
59  	 *            sequences to create
60  	 * @param foreignkeysNames
61  	 *            foreign keys to create
62  	 */
63  	public static void createRequiredDatabaseEntities(Properties props, ConnectionFactory connFactory, List sequencesNames,
64  			List tablesNames, List foreignkeysNames) {
65  
66  		// we create sequences
67  		createEntities(props, connFactory, sequencesNames, "SEQUENCE_PRESENCE");
68  
69  		// we create tables
70  		createEntities(props, connFactory, tablesNames, "TABLE_PRESENCE");
71  
72  		// we create foreign keys
73  		createForeignKeys(props, connFactory, foreignkeysNames);
74  
75  	}
76  
77  	private static String handleSensitiveCase(String original, DatabaseMetaData dbMetaData) throws SQLException {
78  		boolean storesLowerCaseIdentifiers = dbMetaData.storesLowerCaseIdentifiers();
79  		logger.debug(" database store lower case identifiers =" + storesLowerCaseIdentifiers);
80  		boolean storesUpperCaseIdentifiers = dbMetaData.storesUpperCaseIdentifiers();
81  		logger.debug(" database store upper case identifiers =" + storesUpperCaseIdentifiers);
82  		if (storesLowerCaseIdentifiers) {
83  			original = original.toLowerCase();
84  		} else if (storesUpperCaseIdentifiers) {
85  			original = original.toUpperCase();
86  		}
87  
88  		return original;
89  
90  	}
91  
92  	private static void createForeignKeys(Properties props, ConnectionFactory connectionFactory, List foreignkeysNames) {
93  		Connection conn = null;
94  		try {
95  			conn = connectionFactory.getConnection();
96  			DatabaseMetaData dbMetaData = conn.getMetaData();
97  
98  			for (int i = 0; i < foreignkeysNames.size(); i++) {
99  				String foreignKeyName = (String) foreignkeysNames.get(i);
100 				String query = ((String) props.get(foreignKeyName));
101 				// is there any entity to create?
102 				// => we check its presence into properties file
103 				if (query != null) {
104 					try {
105 						// words have got this structure:
106 						// alter table ${FK_TABLE_NAME} add constraint ${CONSTRAINT_NAME} foreign key (${FOREIGN_KEY_COLUMN_NAME})
107 						// references ${PRIMARY_KEY_TABLE_NAME}
108 						// 0 1 2 3 4 5 6 7 8 9 10
109 						// 2,5,8,10 are the only grabbed words
110 						List words = Arrays.asList(query.split(" "));
111 						/*
112 						if (words.size() != 11) {
113 							logger.error(" the query for creating " + foreignKeyName + " must contains 11 words ");
114 							continue;
115 						}
116 						*/
117 						String fkTableName = (String) words.get(2);
118 						String constraintName = (String) words.get(5);
119 						String fKeyColumnName = (String) words.get(8);// remove ()
120 						fKeyColumnName = fKeyColumnName.replace('(', ' ');
121 						fKeyColumnName = fKeyColumnName.replace(')', ' ').trim();
122 						String pKeyTableName = (String) words.get(10);
123 						int pKeyIndex = pKeyTableName.indexOf("(");
124 						//SQL server include the column when we grab the primary key table name
125 						//we remove it
126 					    if (pKeyIndex>0){
127 					        pKeyTableName = pKeyTableName.substring(0,pKeyIndex);
128 					    }
129 					    
130 						ResultSet rs = dbMetaData.getImportedKeys(null, null, handleSensitiveCase(fkTableName, dbMetaData));
131 						boolean foreignKeyFound = false;
132 						while (rs.next()) {
133 							logger.debug("importedKeys for=" + fkTableName);
134 							String pkeyTableName2 = rs.getString("PKTABLE_NAME");
135 							logger.debug("PKTABLE_NAME=" + pkeyTableName2);
136 							String pkeyColumnName2 = rs.getString("PKCOLUMN_NAME");
137 							logger.debug("PKCOLUMN_NAME=" + pkeyColumnName2);
138 							String fkeyTableName2 = rs.getString("FKTABLE_NAME");
139 							logger.debug("FKTABLE_NAME=" + fkeyTableName2);
140 							String fkeyColumnName2 = rs.getString("FKCOLUMN_NAME");
141 							logger.debug("FKCOLUMN_NAME=" + fkeyColumnName2);
142 							String constraintName2 = rs.getString("FK_NAME");
143 							logger.debug("FK_NAME=" + constraintName2);
144 							String pkeyName2 = rs.getString("PK_NAME");
145 							logger.debug("PK_NAME=" + pkeyName2);
146 							if (fkTableName.equalsIgnoreCase(fkeyTableName2) && fKeyColumnName.equalsIgnoreCase(fkeyColumnName2)
147 									&& pKeyTableName.equalsIgnoreCase(pkeyTableName2)
148 									&& constraintName.equalsIgnoreCase(constraintName2)) {
149 								// foreign key is already present
150 								foreignKeyFound = true;
151 								break;
152 							}
153 
154 						}
155 
156 						if (foreignKeyFound) {
157 							continue;
158 						}
159 						// we create the entity
160 
161 						logger.debug("entity=" + foreignKeyName + " query=" + query);
162 						PreparedStatement ps2 = conn.prepareStatement(query);
163 						ps2.execute();
164 					} catch (SQLException sqle) {
165 						logger.error("entity=" + foreignKeyName + " cannot be created . " + sqle.getMessage());
166 					}
167 
168 				} else {
169 					logger.info(foreignKeyName + " entry is not present in the properties file ");
170 				}
171 
172 			}
173 		} catch (SQLException e) {
174 			logger.error(" database metadata cannot be grabbed from the SQL connection ");
175 		} finally {
176 			try {
177 				conn.close();
178 			} catch (SQLException e) {
179 				logger.error(" connexion cannot be closed " + e.getMessage());
180 			}
181 		}
182 
183 	}
184 
185 	/**
186 	 * create database entities (sequences or tables).
187 	 * @param props
188 	 * @param connectionFactory connection Factory
189 	 * @param entitiesNames
190 	 *            tables name which permit to build sequence name
191 	 * @param presenceQueryKey
192 	 */
193 	private static void createEntities(Properties props, ConnectionFactory connectionFactory, List entitiesNames,
194 			String presenceQueryKey) {
195 		Connection conn = null;
196 		String presenceQuery = props.getProperty(presenceQueryKey);
197 		try {
198 			conn = connectionFactory.getConnection();
199 			DatabaseMetaData dbMetaData = conn.getMetaData();
200 			for (int i = 0; i < entitiesNames.size(); i++) {
201 				String entityName = (String) entitiesNames.get(i);
202 
203 				// is there any entity to create?
204 				// => we check its presence into properties file
205 				if ((String) props.get(entityName) != null) {
206 					try {
207 						// does the entity is already present in the database?
208 						String replacedPresenceQuery = presenceQuery.replaceFirst("\\?", handleSensitiveCase(entityName,
209 								dbMetaData));
210 						PreparedStatement ps1 = conn.prepareStatement(replacedPresenceQuery);
211 						logger.debug(replacedPresenceQuery);
212 						ResultSet rs1 = ps1.executeQuery();
213 						if (rs1.next()) {
214 							// entity is already present
215 							logger.debug(" entity " + entityName + " has been detected : " + rs1.getObject(1));
216 							continue;
217 						}
218 					} catch (SQLException e) {
219 						logger.debug(" entity " + entityName + " does not exists and will be created ");
220 					}
221 					try {
222 						// we create the entity
223 						String query2 = (String) props.get(entityName);
224 						logger.debug("entity=" + entityName + " query2=" + query2);
225 						PreparedStatement ps2 = conn.prepareStatement(handleSensitiveCase(query2, dbMetaData));
226 						ps2.execute();
227 					} catch (SQLException sqle) {
228 						logger.error("entity=" + entityName + " cannot be created . " + sqle.getMessage());
229 						throw new RuntimeException(sqle);
230 					}
231 
232 				} else {
233 					logger.info(entityName + " entry is not present in the properties file ");
234 				}
235 
236 			}
237 		} catch (SQLException e) {
238 			logger.error(e.getMessage());
239 		} finally {
240 			try {
241 				conn.close();
242 			} catch (SQLException e) {
243 				logger.error(" connexion cannot be closed " + e.getMessage());
244 			}
245 		}
246 	}
247 
248 	/**
249 	 * detect if the database has got some datas. return <i>true</i> if <strong>all</strong> jGuard tables involved in
250 	 * authorization are empty
251 	 *
252 	 * @param props
253 	 *            properties files containing SQL queries
254 	 * @param connectionFactory
255 	 *            connection factory used to check if the database is empty
256 	 * @param selectQueries
257 	 *            queries to execute
258 	 * @return true if the database schema is empty
259 	 */
260 	public static boolean isEmpty(Properties props, ConnectionFactory connectionFactory, List selectQueries) {
261 		if (props == null) {
262 			throw new IllegalArgumentException(" properties is null ");
263 		}
264 		if (connectionFactory == null) {
265 			throw new IllegalArgumentException(" connectionFactory is null ");
266 		}
267 		boolean empty = true;
268 		Connection conn = null;
269 		try {
270 			conn = connectionFactory.getConnection();
271 			PreparedStatement pst = null;
272 			ResultSet rs = null;
273 			Iterator it = selectQueries.iterator();
274 			while (it.hasNext()) {
275 				try {
276 					String key = (String) it.next();
277 					String query = (String) props.getProperty(key);
278 					pst = conn.prepareStatement(query);
279 					rs = pst.executeQuery();
280 					if (rs.next() != false) {
281 						logger.info(" there are some principals in database ");
282 						empty = false;
283 						break;
284 					}
285 
286 				} catch (SQLException e) {
287 					logger.debug(e.getMessage());
288 				}
289 			}
290 		} finally {
291 			try {
292 				conn.close();
293 			} catch (SQLException e) {
294                             logger.debug(e.getMessage());
295 			}
296 		}
297 		return empty;
298 	}
299 
300 }