Skip to content

Method: recalibrateMaxAssociationID()

1: package de.fhdw.wtf.persistence.facade;
2:
3: import java.io.File;
4: import java.io.FileInputStream;
5: import java.io.FileNotFoundException;
6: import java.io.FileOutputStream;
7: import java.io.IOException;
8: import java.sql.CallableStatement;
9: import java.sql.ResultSet;
10: import java.sql.SQLException;
11: import java.util.Iterator;
12: import java.util.Properties;
13:
14: import oracle.jdbc.OracleCallableStatement;
15: import oracle.jdbc.OracleTypes;
16: import de.fhdw.wtf.persistence.exception.IDContractViolationException;
17: import de.fhdw.wtf.persistence.exception.IDNotFoundForNameException;
18: import de.fhdw.wtf.persistence.exception.IDPersistenceFilesNotFound;
19: import de.fhdw.wtf.persistence.exception.OtherSQLException;
20: import de.fhdw.wtf.persistence.exception.PersistenceException;
21: import de.fhdw.wtf.persistence.meta.IntegerType;
22: import de.fhdw.wtf.persistence.meta.StringType;
23:
24: /**
25: * TODO generate the greatest id for associations and types dynamically from a set of the singleton base type classes.
26: *
27: * Description: - This class manages the id distribution for types and associations. - If a new type or association is
28: * created, the id for this new item should be pulled from this manager. - if you want to know the id for a model item
29: * (association or type) ask this manager. - This class is also able to save the information about the current relation
30: * (name -> id) to a file. See persistIDRelationsToFile. This should be called everytime you want to close the process
31: * which has the idmanager instance. - On creation of this manager the (name -> id) relation will be loaded from a file
32: * if it exists. Otherwise the database will be requested for the ids.
33: */
34: public final class IDManager {
35:         /**
36:          * The Id which was the highest given ID for types during the initialization, it must match the contract. Ids must
37:          * be given in successive manner therefore a value of zero means there are no types initialized. This value is also
38:          * contracted by the pl sql script for the classfacade. See initialize operation.
39:          */
40:         private static final long MAX_BASE_TYPES = 2; // 2 because of 2 base types
41:                                                                                                         // {String, Integer}
42:         
43:         /**
44:          * Because after the creation of the Base Types there must not be any association, the contracted value is zero.
45:          */
46:         private static final long MAX_CONTRACT_ASSOCIATION_ID = 0;
47:         
48:         /** Specifies the filename of the file for the type name -> id relation. */
49:         public static final String TYPE_IDS_FILENAME = "types.properties";
50:         
51:         /**
52:          * Specifies the filename of the file for the association name -> id relation.
53:          */
54:         public static final String ASSOCIATION_IDS_FILENAME = "associations.properties";
55:         
56:         private static IDManager instance = null;
57:         private final Properties typeNameIdRelation;
58:         private final Properties associationNameIdRelation;
59:         private long currentlyUsedTypeMaxId;
60:         private long currentlyUsedAssociationMaxId;
61:         
62:         private IDManager() {
63:                 this.typeNameIdRelation = new Properties();
64:                 this.associationNameIdRelation = new Properties();
65:                 this.currentlyUsedAssociationMaxId = getMaxAssociationContractID();
66:                 this.currentlyUsedTypeMaxId = getMaxBaseTypeID();
67:         }
68:         
69:         /**
70:          * This operation searches in the property the highest type id and sets the member currentlyUsedTypeMaxId to it.
71:          * This is necessary to generate the next id unused it properly after the initialization of this class.
72:          */
73:         private void recalibrateMaxTypeID() {
74:                 final Iterator<String> it = this.typeNameIdRelation.stringPropertyNames().iterator();
75:                 while (it.hasNext()) {
76:                         final String currentKey = it.next();
77:                         final Long currentId = Long.parseLong(this.typeNameIdRelation.getProperty(currentKey));
78:                         if (this.currentlyUsedTypeMaxId < currentId) {
79:                                 this.currentlyUsedTypeMaxId = currentId;
80:                         }
81:                 }
82:                 
83:         }
84:         
85:         /**
86:          * This operation searches for the highest association id and sets the member currentlyUsedAssociationMaxId to it.
87:          * This is necessary to generate the next id unused it properly after the initialization of this class.
88:          */
89:         private void recalibrateMaxAssociationID() {
90:                 final Iterator<String> it = this.associationNameIdRelation.stringPropertyNames().iterator();
91:•                while (it.hasNext()) {
92:                         final String currentKey = it.next();
93:                         final Long currentId = Long.parseLong(this.associationNameIdRelation.getProperty(currentKey));
94:•                        if (this.currentlyUsedAssociationMaxId < currentId) {
95:                                 this.currentlyUsedAssociationMaxId = currentId;
96:                         }
97:                 }
98:         }
99:         
100:         /**
101:          * Checks if the files with the id informations are available and initializes the internal data structures with
102:          * these values.
103:          *
104:          * @param typeIdsFilename
105:          * the filename to the file contains the typeIds
106:          * @param associationIdsFilename
107:          * the filename to the file contains the assoctiationIds
108:          * @throws IOException
109:          * @throws FileNotFoundException
110:          */
111:         public void initializeRelationsFromFile(final String typeIdsFilename, final String associationIdsFilename)
112:                         throws FileNotFoundException, IOException {
113:                 final File fileType = new File(typeIdsFilename);
114:                 final File fileAssociation = new File(associationIdsFilename);
115:                 
116:                 if (fileType.exists() && !fileType.isDirectory() && fileAssociation.exists() && !fileAssociation.isDirectory()) {
117:                         try (final FileInputStream fileTypeInputStream = new FileInputStream(fileType);
118:                                         final FileInputStream fileAssociationInputStream = new FileInputStream(fileAssociation)) {
119:                                 this.typeNameIdRelation.load(fileTypeInputStream);
120:                                 this.associationNameIdRelation.load(fileAssociationInputStream);
121:                         }
122:                 } else {
123:                         throw new IDPersistenceFilesNotFound();
124:                 }
125:                 this.typeNameIdRelation.setProperty(StringType.STRING_NAME, Long.toString(StringType.String_ID));
126:                 this.typeNameIdRelation.setProperty(IntegerType.INTEGER_NAME, Long.toString(IntegerType.Integer_ID));
127:                 this.recalibrateMaxTypeID();
128:                 this.recalibrateMaxAssociationID();
129:                 
130:         }
131:         
132:         /**
133:          * Initializes the name->id relations from the database. TODO create some operations in the classfacade which
134:          * provide the types and associations directly from the database. This is not part of the id-manager.
135:          *
136:          * @throws PersistenceException
137:          * @throws SQLException
138:          */
139:         public void initializeRelationsFromDatabase() throws PersistenceException, SQLException {
140:                 final OracleDatabaseManager database = OracleDatabaseManager.getInstance();
141:                 try {
142:                         database.getConnection();
143:                 } catch (final PersistenceException e) {
144:                         database.connect();
145:                 }
146:                 
147:                 try (final CallableStatement call =
148:                                 database.getConnection().prepareCall(
149:                                                 "begin ?:= " + database.getSchemaName() + ".classfacade.getAllTypes; end;")) {
150:                         call.registerOutParameter(1, OracleTypes.CURSOR);
151:                         call.execute();
152:                         
153:                         try (final ResultSet result = ((OracleCallableStatement) call).getCursor(1)) {
154:                                 while (result.next()) {
155:                                         this.typeNameIdRelation.setProperty(result.getString(2), Long.toString(result.getLong(1)));
156:                                 }
157:                         }
158:                 } catch (final SQLException e) {
159:                         throw new OtherSQLException(e);
160:                 }
161:                 
162:                 try (final CallableStatement call =
163:                                 database.getConnection().prepareCall(
164:                                                 "begin ?:= " + database.getSchemaName() + ".classfacade.getAllUnidirAssociations; end;")) {
165:                         call.registerOutParameter(1, OracleTypes.CURSOR);
166:                         call.execute();
167:                         
168:                         try (final ResultSet result = ((OracleCallableStatement) call).getCursor(1)) {
169:                                 while (result.next()) {
170:                                         this.associationNameIdRelation.setProperty(result.getString(2), Long.toString(result.getLong(1)));
171:                                 }
172:                         }
173:                 } catch (final SQLException e) {
174:                         throw new OtherSQLException(e);
175:                 }
176:                 
177:                 try (final CallableStatement call =
178:                                 database.getConnection().prepareCall(
179:                                                 "begin ?:= " + database.getSchemaName() + ".classfacade.getAllMapAssociations; end;")) {
180:                         call.registerOutParameter(1, OracleTypes.CURSOR);
181:                         call.execute();
182:                         
183:                         try (final ResultSet result = ((OracleCallableStatement) call).getCursor(1)) {
184:                                 while (result.next()) {
185:                                         this.associationNameIdRelation.setProperty(result.getString(2), Long.toString(result.getLong(1)));
186:                                 }
187:                         }
188:                 } catch (final SQLException e) {
189:                         throw new OtherSQLException(e);
190:                 }
191:                 
192:                 this.typeNameIdRelation.setProperty(StringType.STRING_NAME, Long.toString(StringType.String_ID));
193:                 this.typeNameIdRelation.setProperty(IntegerType.INTEGER_NAME, Long.toString(IntegerType.Integer_ID));
194:                 this.recalibrateMaxTypeID();
195:                 this.recalibrateMaxAssociationID();
196:         }
197:         
198:         /**
199:          * returns the Instance of the IDManager.
200:          *
201:          * @return the instance
202:          */
203:         public static synchronized IDManager instance() {
204:                 if (instance == null) {
205:                         instance = new IDManager();
206:                 }
207:                 return instance;
208:         }
209:         
210:         /**
211:          * Warning: Use it for Test-Cases only! This operations forces a recreation of the singleton for the next time and
212:          * frees the old one for garbage collection.
213:          */
214:         public void clearInformation() {
215:                 this.typeNameIdRelation.clear();
216:                 this.associationNameIdRelation.clear();
217:                 this.currentlyUsedAssociationMaxId = getMaxAssociationContractID();
218:                 this.currentlyUsedTypeMaxId = getMaxBaseTypeID();
219:                 
220:         }
221:         
222:         /**
223:          *
224:          * @return the highest id for base types.
225:          */
226:         public static long getMaxBaseTypeID() {
227:                 return MAX_BASE_TYPES;
228:         }
229:         
230:         /**
231:          * @return the highest association id which will be initialized by the pl-sql script at initialization. The highest
232:          * id is contracted.
233:          */
234:         public static long getMaxAssociationContractID() {
235:                 return MAX_CONTRACT_ASSOCIATION_ID;
236:         }
237:         
238:         /**
239:          * This operation persists all (name -> id) informations to files.
240:          *
241:          * @param typeIdsFilename
242:          * the filename to the file contains the typeIds
243:          * @param associationIdsFilename
244:          * the filename to the file contains the associationIds
245:          * @throws IOException
246:          */
247:         public void persistIDRelationsToFile(final String typeIdsFilename, final String associationIdsFilename)
248:                         throws IOException {
249:                 try (FileOutputStream file = new FileOutputStream(typeIdsFilename)) {
250:                         this.typeNameIdRelation.store(file, "");
251:                         file.flush();
252:                 }
253:                 
254:                 try (FileOutputStream file = new FileOutputStream(associationIdsFilename)) {
255:                         this.associationNameIdRelation.store(file, "");
256:                         file.flush();
257:                 }
258:         }
259:         
260:         /**
261:          * This operation returns the next unused id for a new type.
262:          *
263:          * @param typeName
264:          * is the name of the type for which a new id will be registered.
265:          * @return the id
266:          */
267:         public synchronized long pullNextUnusedTypeID(final String typeName) {
268:                 if (typeName.equals(StringType.STRING_NAME) || typeName.equals(IntegerType.INTEGER_NAME)) {
269:                         throw new IDContractViolationException();
270:                 }
271:                 
272:                 this.currentlyUsedTypeMaxId += 1;
273:                 this.typeNameIdRelation.setProperty(typeName, Long.toString(this.currentlyUsedTypeMaxId));
274:                 return this.currentlyUsedTypeMaxId;
275:         }
276:         
277:         /**
278:          * This operation returns the next unused id for a new association.
279:          *
280:          * @param associationName
281:          * is the name of the association for which a new id will be registered.
282:          * @return the id
283:          */
284:         public synchronized long pullNextUnusedAssociationID(final String associationName) {
285:                 this.currentlyUsedAssociationMaxId += 1;
286:                 this.associationNameIdRelation.setProperty(associationName, Long.toString(this.currentlyUsedAssociationMaxId));
287:                 return this.currentlyUsedAssociationMaxId;
288:         }
289:         
290:         /**
291:          * This operations delivers the id for a type by the name of the parameter.
292:          *
293:          * @param typeName
294:          * the name of the type to look for
295:          * @return the Id of the type when found
296:          * @exception IDNotFoundForNameException
297:          * is thrown if no id was found.
298:          */
299:         public long findIdForType(final String typeName) throws IDNotFoundForNameException {
300:                 if (!this.typeNameIdRelation.containsKey(typeName)) {
301:                         throw new IDNotFoundForNameException();
302:                 }
303:                 return Long.parseLong(this.typeNameIdRelation.getProperty(typeName));
304:         }
305:         
306:         /**
307:          * This operations delivers the id for a association by the name of the parameter.
308:          *
309:          * @param associationName
310:          * the name of the association to look for
311:          * @return the id of the association if found
312:          * @exception IDNotFoundForNameException
313:          * is thrown if no id was found.
314:          */
315:         public long findIdForAssociation(final String associationName) throws IDNotFoundForNameException {
316:                 if (!this.associationNameIdRelation.containsKey(associationName)) {
317:                         throw new IDNotFoundForNameException();
318:                 }
319:                 return Long.parseLong(this.associationNameIdRelation.getProperty(associationName));
320:         }
321: }