View Javadoc
1   package de.fhdw.wtf.persistence.facade;
2   
3   import java.sql.CallableStatement;
4   import java.sql.ResultSet;
5   import java.sql.SQLException;
6   import java.util.Collection;
7   import java.util.Iterator;
8   import java.util.List;
9   import java.util.Vector;
10  
11  import oracle.jdbc.OracleCallableStatement;
12  import oracle.jdbc.OracleTypes;
13  import oracle.sql.ARRAY;
14  import oracle.sql.ArrayDescriptor;
15  import de.fhdw.wtf.persistence.exception.ClassFacadeUninitializedException;
16  import de.fhdw.wtf.persistence.exception.InitializingDatabaseContractViolationException;
17  import de.fhdw.wtf.persistence.exception.OtherSQLException;
18  import de.fhdw.wtf.persistence.exception.PersistenceException;
19  import de.fhdw.wtf.persistence.exception.SpecializationCycleDetected;
20  import de.fhdw.wtf.persistence.meta.Association;
21  import de.fhdw.wtf.persistence.meta.IntegerType;
22  import de.fhdw.wtf.persistence.meta.MapAssociation;
23  import de.fhdw.wtf.persistence.meta.StringType;
24  import de.fhdw.wtf.persistence.meta.Type;
25  import de.fhdw.wtf.persistence.meta.UnidirectionalAssociation;
26  import de.fhdw.wtf.persistence.meta.UserType;
27  import de.fhdw.wtf.persistence.utils.IntegerConstants;
28  
29  /**
30   * A class to represent an implementation of the ClassFacade Interface for the Oracle Database. Supports a Database
31   * Initializer.
32   */
33  public class OracleClassFacadeImplementation implements ClassFacade {
34  	
35  	/**
36  	 * A flag to determine, if the ClassFacade was initialized,e.g. TypeManager is filled with information and base
37  	 * types are initialized.
38  	 */
39  	private boolean initialized;
40  	
41  	/**
42  	 * The Type Manager which stores the Model Item informations.
43  	 */
44  	private TypeManagerImplementation typeManager;
45  	
46  	/**
47  	 * The Oracle Database Manager, which is needed to call the Database.
48  	 */
49  	private final OracleDatabaseManager database;
50  	
51  	/**
52  	 * Constructor for a new OracleClassFacadeImplementation.
53  	 * 
54  	 * @param database
55  	 *            The OracleDatabaseManager, which has to be connected.
56  	 */
57  	public OracleClassFacadeImplementation(final OracleDatabaseManager database) {
58  		this.database = database;
59  		this.initialized = false;
60  		this.typeManager = TypeManagerImplementation.getInstance();
61  	}
62  	
63  	@Override
64  	public UserType createUserType(final String name, final boolean abs, final boolean transaction)
65  			throws PersistenceException {
66  		final UserType result = new UserType(IDManager.instance().pullNextUnusedTypeID(name), name, abs, transaction);
67  		try (final CallableStatement call =
68  				this.database.getConnection().prepareCall(
69  						"begin " + this.database.getSchemaName() + ".classfacade.createUserType(?,?,?,?); end;")) {
70  			call.setLong(1, result.getId());
71  			call.setString(2, result.getName());
72  			call.setInt(IntegerConstants.THREE, result.isAbs() ? 1 : 0);
73  			call.setInt(IntegerConstants.FOUR, result.isTrans() ? 1 : 0);
74  			call.execute();
75  		} catch (final SQLException e) {
76  			throw new OtherSQLException(e);
77  		}
78  		this.typeManager.saveType(result);
79  		return result;
80  	}
81  	
82  	@Override
83  	public UnidirectionalAssociation createUnidirectionalAssociation(final String name,
84  			final boolean essential,
85  			final boolean unique,
86  			final UserType owner,
87  			final Type target) throws PersistenceException {
88  		final UnidirectionalAssociation result =
89  				new UnidirectionalAssociation(IDManager.instance().pullNextUnusedAssociationID(name), name, owner,
90  						target, essential, unique);
91  		try (final CallableStatement call =
92  				this.database.getConnection().prepareCall(
93  						"begin " + this.database.getSchemaName()
94  								+ ".classfacade.createUnidirAssociation(?,?,?,?,?,?); end;")) {
95  			call.setLong(1, result.getId());
96  			call.setString(2, result.getName());
97  			call.setLong(IntegerConstants.THREE, result.getOwner().getId());
98  			call.setLong(IntegerConstants.FOUR, result.getTarget().getId());
99  			call.setInt(IntegerConstants.FIVE, result.isEssential() ? 1 : 0);
100 			call.setInt(IntegerConstants.SIX, result.isUnique() ? 1 : 0);
101 			call.execute();
102 		} catch (final SQLException e) {
103 			throw new OtherSQLException(e);
104 		}
105 		this.typeManager.saveAssociation(result);
106 		return result;
107 	}
108 	
109 	@Override
110 	public MapAssociation createMapAssociation(final String name,
111 			final boolean essential,
112 			final UserType owner,
113 			final Type target,
114 			final Type keyType) throws PersistenceException {
115 		final MapAssociation result =
116 				new MapAssociation(IDManager.instance().pullNextUnusedAssociationID(name), name, owner, target,
117 						keyType, essential);
118 		try (final CallableStatement call =
119 				this.database.getConnection().prepareCall(
120 						"begin " + this.database.getSchemaName()
121 								+ ".classfacade.createMapAssociation(?,?,?,?,?,?,?); end;")) {
122 			call.setLong(1, result.getId());
123 			call.setString(2, result.getName());
124 			call.setLong(IntegerConstants.THREE, result.getOwner().getId());
125 			call.setLong(IntegerConstants.FOUR, result.getTarget().getId());
126 			call.setInt(IntegerConstants.FIVE, result.isEssential() ? 1 : 0);
127 			call.setInt(IntegerConstants.SIX, 1);
128 			call.setLong(IntegerConstants.SEVEN, result.getKeyType().getId());
129 			call.execute();
130 		} catch (final SQLException e) {
131 			throw new OtherSQLException(e);
132 		}
133 		this.typeManager.saveAssociation(result);
134 		return result;
135 	}
136 	
137 	@Override
138 	public void createSpecializationBetween(final UserType ancestor, final Type descendant) throws PersistenceException {
139 		try (final CallableStatement call =
140 				this.database.getConnection().prepareCall(
141 						"begin " + this.database.getSchemaName() + ".classfacade.createSpecialization(?,?); end;")) {
142 			call.setLong(1, ancestor.getId());
143 			call.setLong(2, descendant.getId());
144 			call.execute();
145 		} catch (final SQLException e) {
146 			throw new OtherSQLException(e);
147 		}
148 	}
149 	
150 	@Override
151 	public void initialize() throws PersistenceException {
152 		try (final CallableStatement call =
153 				this.database.getConnection().prepareCall(
154 						"begin " + this.database.getSchemaName() + ".classfacade.initialize; end;")) {
155 			call.execute();
156 		} catch (final SQLException e) {
157 			throw new OtherSQLException(e);
158 		}
159 		
160 		try (final CallableStatement call =
161 				this.database.getConnection().prepareCall(
162 						"begin ? :=" + this.database.getSchemaName() + ".classfacade.getMaxIdFromType; end;")) {
163 			call.registerOutParameter(1, OracleTypes.NUMBER);
164 			call.execute();
165 			if (IDManager.getMaxBaseTypeID() != call.getLong(1)) {
166 				throw new InitializingDatabaseContractViolationException();
167 			}
168 		} catch (final SQLException e) {
169 			throw new OtherSQLException(e);
170 		}
171 		
172 		try (final CallableStatement call =
173 				this.database.getConnection().prepareCall(
174 						"begin ? :=" + this.database.getSchemaName() + ".classfacade.getMaxIdFromAssociation; end;")) {
175 			call.registerOutParameter(1, OracleTypes.NUMBER);
176 			call.execute();
177 			if (IDManager.getMaxAssociationContractID() != call.getLong(1)) {
178 				throw new InitializingDatabaseContractViolationException();
179 			}
180 		} catch (final SQLException e) {
181 			throw new OtherSQLException(e);
182 		}
183 		
184 		this.initializeBaseTypes();
185 		this.finalizeSpecialization();
186 		this.initialized = true;
187 	}
188 	
189 	@Override
190 	public boolean isSuperClassTo(final Type ancestor, final Type descendant) throws PersistenceException {
191 		try (final CallableStatement call =
192 				this.database.getConnection().prepareCall(
193 						"begin ?:=" + this.database.getSchemaName() + ".classfacade.isSuperclassTo(?,?); end;")) {
194 			call.registerOutParameter(1, OracleTypes.NUMBER);
195 			call.setLong(2, ancestor.getId());
196 			call.setLong(IntegerConstants.THREE, descendant.getId());
197 			call.execute();
198 			return call.getInt(1) == 1 ? true : false;
199 		} catch (final SQLException e) {
200 			e.printStackTrace();
201 			throw new OtherSQLException(e);
202 		}
203 	}
204 	
205 	@Override
206 	public void clear() throws PersistenceException {
207 		try (final CallableStatement call =
208 				this.database.getConnection().prepareCall(
209 						"begin " + this.database.getSchemaName() + ".classfacade.clearAll; end;")) {
210 			call.execute();
211 		} catch (final SQLException e) {
212 			throw new OtherSQLException(e);
213 		}
214 	}
215 	
216 	@Override
217 	public void finalizeSpecialization() throws PersistenceException {
218 		try (final CallableStatement call =
219 				this.database.getConnection().prepareCall(
220 						"begin " + this.database.getSchemaName() + ".classfacade.finalizeSpecialization; end;")) {
221 			call.execute();
222 		} catch (final SQLException e) {
223 			if (e.getErrorCode() == SpecializationCycleDetected.ERRORCODE) {
224 				throw new SpecializationCycleDetected(e);
225 			}
226 			e.printStackTrace();
227 			throw new OtherSQLException(e);
228 		}
229 	}
230 	
231 	@Override
232 	public void renameType(final Long typeId, final String newName) throws PersistenceException {
233 		try (final CallableStatement call =
234 				this.database.getConnection().prepareCall(
235 						"begin " + this.database.getSchemaName() + ".classfacade.renameType(?,?); end;")) {
236 			call.setLong(1, typeId);
237 			call.setString(2, newName);
238 			call.execute();
239 		} catch (final SQLException e) {
240 			e.printStackTrace();
241 			throw new OtherSQLException(e);
242 		}
243 	}
244 	
245 	@Override
246 	public void renameAssociation(final Long associationId, final String newName) throws PersistenceException {
247 		try (final CallableStatement call =
248 				this.database.getConnection().prepareCall(
249 						"begin " + this.database.getSchemaName() + ".classfacade.renameAssociation(?,?); end;")) {
250 			call.setLong(1, associationId);
251 			call.setString(2, newName);
252 			call.execute();
253 		} catch (final SQLException e) {
254 			e.printStackTrace();
255 			throw new OtherSQLException(e);
256 		}
257 	}
258 	
259 	@Override
260 	public void deleteAssociation(final Long assoId) throws PersistenceException {
261 		this.typeManager.deleteAssociation(assoId);
262 		try (final CallableStatement call =
263 				this.database.getConnection().prepareCall(
264 						"begin " + this.database.getSchemaName() + ".classfacade.deleteAssociation(?); end;")) {
265 			call.setLong(1, assoId);
266 			call.execute();
267 		} catch (final SQLException e) {
268 			e.printStackTrace();
269 			throw new OtherSQLException(e);
270 		}
271 	}
272 	
273 	@Override
274 	public void updateLinksToNewAssociation(final Long assoId, final Collection<Long> newAssociationIds)
275 			throws PersistenceException {
276 		try (final CallableStatement call =
277 				this.database.getConnection().prepareCall(
278 						"begin " + this.database.getSchemaName() + ".classfacade.pushDownLinks(?,?); end;")) {
279 			call.setLong(1, assoId);
280 			final ArrayDescriptor des =
281 					ArrayDescriptor.createDescriptor(
282 							this.database.getSchemaName().toUpperCase() + ".ARRAY_INT",
283 							this.database.getConnection());
284 			final Long[] newAssos = newAssociationIds.toArray(new Long[newAssociationIds.size()]);
285 			final ARRAY arrayNewAssociation = new ARRAY(des, this.database.getConnection(), newAssos);
286 			call.setArray(2, arrayNewAssociation);
287 			call.execute();
288 		} catch (final SQLException e) {
289 			e.printStackTrace();
290 			throw new OtherSQLException(e);
291 		}
292 	}
293 	
294 	@Override
295 	public void deleteUserType(final Long typeId) throws PersistenceException {
296 		this.typeManager.deleteType(typeId);
297 		try (final CallableStatement call =
298 				this.database.getConnection().prepareCall(
299 						"begin " + this.database.getSchemaName() + ".classfacade.deleteUserTypeAndSpec(?); end;")) {
300 			call.setLong(1, typeId);
301 			call.execute();
302 		} catch (final SQLException e) {
303 			e.printStackTrace();
304 			throw new OtherSQLException(e);
305 		}
306 	}
307 	
308 	@Override
309 	public void moveLinksAndCreateObjects(final List<Long> oldAssoIds,
310 			final Association newAsso,
311 			final UserType newType,
312 			final List<Long> newAssoIds) throws PersistenceException {
313 		try (final CallableStatement call =
314 				this.database.getConnection().prepareCall(
315 						"begin " + this.database.getSchemaName()
316 								+ ".classfacade.moveLinksAndCreateObjects(?,?,?,?); end;")) {
317 			// TODO Implement database function!
318 			final ArrayDescriptor des =
319 					ArrayDescriptor.createDescriptor(
320 							this.database.getSchemaName().toUpperCase() + ".ARRAY_INT",
321 							this.database.getConnection());
322 			final Long[] oldAssosA = oldAssoIds.toArray(new Long[oldAssoIds.size()]);
323 			final Long[] newAssosA = newAssoIds.toArray(new Long[newAssoIds.size()]);
324 			final ARRAY arrayNewAssociations = new ARRAY(des, this.database.getConnection(), newAssosA);
325 			final ARRAY arrayOldAssociations = new ARRAY(des, this.database.getConnection(), oldAssosA);
326 			
327 			call.setArray(1, arrayOldAssociations);
328 			call.setLong(2, newAsso.getId());
329 			call.setLong(IntegerConstants.THREE, newType.getId());
330 			call.setArray(IntegerConstants.FOUR, arrayNewAssociations);
331 			call.execute();
332 		} catch (final SQLException e) {
333 			e.printStackTrace();
334 			throw new OtherSQLException(e);
335 		}
336 	}
337 	
338 	@Override
339 	public TypeManager getTypeManager() throws ClassFacadeUninitializedException {
340 		if (!this.hasBeenInitialized()) {
341 			throw new ClassFacadeUninitializedException();
342 		}
343 		return this.typeManager;
344 	}
345 	
346 	private void initializeBaseTypes() {
347 		this.typeManager.saveType(StringType.getInstance());
348 		this.typeManager.saveType(IntegerType.getInstance());
349 	}
350 	
351 	/**
352 	 * A private method to check if an Object exists in the types Collection with the type information (id, name) of the
353 	 * String Type from the contract and removes this object from the types Collection, if it exists.
354 	 * 
355 	 * @param types
356 	 *            A Collection of Types.
357 	 * @return Provides true if an Object with the string type information exists.
358 	 */
359 	private boolean checkIfStringIsCorrect(final Collection<Type> types) {
360 		final Iterator<Type> i = types.iterator();
361 		while (i.hasNext()) {
362 			final Type current = i.next();
363 			if (current.getId() == 1 && current.getName().equals(StringType.STRING_NAME)) {
364 				i.remove();
365 				return true;
366 			}
367 		}
368 		return false;
369 	}
370 	
371 	/**
372 	 * A private method to check if an Object exists in the types Collection with the type information (id, name) of the
373 	 * Integer Type from the contract and removes this object from the types Collection, if it exists.
374 	 * 
375 	 * @param types
376 	 *            A Collection of Types.
377 	 * @return Provides true if an Object with the integer type information exists.
378 	 */
379 	private boolean checkIfIntegerIsCorrect(final Collection<Type> types) {
380 		final Iterator<Type> i = types.iterator();
381 		while (i.hasNext()) {
382 			final Type current = i.next();
383 			if (current.getId() == 2 && current.getName().equals(IntegerType.INTEGER_NAME)) {
384 				i.remove();
385 				return true;
386 			}
387 		}
388 		return false;
389 	}
390 	
391 	@Override
392 	public boolean hasBeenInitialized() {
393 		return this.initialized;
394 	}
395 	
396 	@Override
397 	public void initializeForRuntime() throws PersistenceException {
398 		this.typeManager = TypeManagerImplementation.getInstance();
399 		final Collection<Type> types = new Vector<>();
400 		try (final CallableStatement call =
401 				this.database.getConnection().prepareCall(
402 						"begin ?:= " + this.database.getSchemaName() + ".classfacade.getAllTypes; end;")) {
403 			call.registerOutParameter(1, OracleTypes.CURSOR);
404 			call.execute();
405 			
406 			try (final ResultSet result = ((OracleCallableStatement) call).getCursor(1)) {
407 				while (result.next()) {
408 					types.add(new UserType(result.getLong(1), result.getString(2), result
409 							.getInt(IntegerConstants.THREE) == 1 ? true : false,
410 							result.getInt(IntegerConstants.FOUR) == 1 ? true : false));
411 				}
412 			}
413 		} catch (final SQLException e) {
414 			throw new OtherSQLException(e);
415 		}
416 		
417 		if (!this.checkIfStringIsCorrect(types)) {
418 			throw new InitializingDatabaseContractViolationException();
419 		}
420 		if (!this.checkIfIntegerIsCorrect(types)) {
421 			throw new InitializingDatabaseContractViolationException();
422 		}
423 		this.initializeBaseTypes();
424 		for (final Type t : types) {
425 			this.typeManager.saveType(t);
426 		}
427 		
428 		try (final CallableStatement call =
429 				this.database.getConnection().prepareCall(
430 						"begin ?:= " + this.database.getSchemaName() + ".classfacade.getAllUnidirAssociations; end;")) {
431 			call.registerOutParameter(1, OracleTypes.CURSOR);
432 			call.execute();
433 			
434 			final Collection<UnidirectionalAssociation> unidirectionalAssociations = new Vector<>();
435 			try (final ResultSet result = ((OracleCallableStatement) call).getCursor(1)) {
436 				while (result.next()) {
437 					unidirectionalAssociations.add(new UnidirectionalAssociation(result.getLong(1),
438 							result.getString(2), (UserType) this.typeManager.getTypeForId(result
439 									.getLong(IntegerConstants.THREE)), this.typeManager.getTypeForId(result
440 									.getLong(IntegerConstants.FOUR)), result.getInt(IntegerConstants.FIVE) == 1 ? true
441 									: false, result.getInt(IntegerConstants.SIX) == 1 ? true : false));
442 				}
443 			}
444 			final Iterator<UnidirectionalAssociation> i = unidirectionalAssociations.iterator();
445 			while (i.hasNext()) {
446 				final UnidirectionalAssociation current = i.next();
447 				this.typeManager.saveAssociation(current);
448 			}
449 		} catch (final SQLException e) {
450 			throw new OtherSQLException(e);
451 		}
452 		
453 		try (final CallableStatement call =
454 				this.database.getConnection().prepareCall(
455 						"begin ?:= " + this.database.getSchemaName() + ".classfacade.getAllMapAssociations; end;")) {
456 			call.registerOutParameter(1, OracleTypes.CURSOR);
457 			call.execute();
458 			
459 			final Collection<MapAssociation> mapAssociations = new Vector<>();
460 			try (final ResultSet result = ((OracleCallableStatement) call).getCursor(1)) {
461 				while (result.next()) {
462 					mapAssociations.add(new MapAssociation(result.getLong(1), result.getString(2),
463 							(UserType) this.typeManager.getTypeForId(result.getLong(IntegerConstants.THREE)),
464 							this.typeManager.getTypeForId(result.getLong(IntegerConstants.FOUR)), this.typeManager
465 									.getTypeForId(result.getLong(IntegerConstants.FIVE)), result
466 									.getInt(IntegerConstants.SIX) == 1 ? true : false));
467 				}
468 			}
469 			final Iterator<MapAssociation> i = mapAssociations.iterator();
470 			while (i.hasNext()) {
471 				final MapAssociation current = i.next();
472 				this.typeManager.saveAssociation(current);
473 			}
474 		} catch (final SQLException e) {
475 			throw new OtherSQLException(e);
476 		}
477 		
478 		this.initialized = true;
479 		// TODO set the lastSetTypeId and the lastSetAssociationId correctly
480 	}
481 	
482 }