View Javadoc
1   package de.fhdw.wtf.context.core;
2   
3   import java.io.IOException;
4   import java.lang.reflect.Modifier;
5   
6   import de.fhdw.wtf.context.exception.DatabaseManagerFactoryNotInstantiableException;
7   import de.fhdw.wtf.context.exception.FrameworkException;
8   import de.fhdw.wtf.context.model.collections.PersistentList;
9   import de.fhdw.wtf.context.model.collections.PersistentListFactory;
10  import de.fhdw.wtf.context.model.collections.PersistentMap;
11  import de.fhdw.wtf.context.model.collections.PersistentMapFactory;
12  import de.fhdw.wtf.persistence.exception.ClassFacadeUninitializedException;
13  import de.fhdw.wtf.persistence.exception.PersistenceException;
14  import de.fhdw.wtf.persistence.exception.TypeOrAssociationNotFoundException;
15  import de.fhdw.wtf.persistence.facade.ClassFacade;
16  import de.fhdw.wtf.persistence.facade.DatabaseManager;
17  import de.fhdw.wtf.persistence.facade.DatabaseManagerFactory;
18  import de.fhdw.wtf.persistence.facade.TypeManager;
19  import de.fhdw.wtf.persistence.utils.PropertiesReader;
20  import de.fhdw.wtf.persistence.utils.PropertiesReaderFactory;
21  import de.fhdw.wtf.persistence.utils.PropertiesReaderFile;
22  
23  /**
24   * An application starter configures the successful launch of the complete application. It initializes the Database
25   * Manager, the logger and the object factories. A developer has to subclass this class and provide the location of
26   * configuration files etc.
27   *
28   */
29  public abstract class ApplicationStarter {
30  	
31  	/**
32  	 * The delimiter to separate packages in a path.
33  	 */
34  	private static final String PACKAGE_DELIMITER = ".";
35  	
36  	/**
37  	 * The path to search for DatabaseManagers.
38  	 */
39  	private static final String SOURCE_PACKAGE_OF_DATABASE_MANAGER_FACTORIES = "de" + PACKAGE_DELIMITER + "fhdw"
40  			+ PACKAGE_DELIMITER + "wtf" + PACKAGE_DELIMITER + "persistence" + PACKAGE_DELIMITER + "facade";
41  	
42  	/**
43  	 * 
44  	 */
45  	public static final Object ORACLE_DATABASE_MANAGER_FACTORY_NAME = "OracleDatabaseManagerFactory";
46  	
47  	/**
48  	 * This method launches the application in a standalone java environment. After calling this method, the Application
49  	 * Container can be used as central application registry.
50  	 */
51  	public void startStandalone() {
52  		this.start(this.getResourcesPathJava());
53  	}
54  	
55  	/**
56  	 * This method launches the application for deployment inside an JEE container. After calling this method, the
57  	 * Application Container can be used as central application registry.
58  	 */
59  	public void startServer() {
60  		this.start(this.getResourcesPathServer());
61  	}
62  	
63  	/**
64  	 * This method launches the application. After calling this method, the object facade can be used.
65  	 * 
66  	 * @param resourcesPath
67  	 *            Needs a reference to the location of the application configuration files.
68  	 */
69  	public void start(final String resourcesPath) {
70  		PropertiesReader prop;
71  		try {
72  			prop = new PropertiesReaderFile();
73  			prop.initialize(resourcesPath + "/" + this.getApplicationConfigFileName());
74  			// fileStream = new FileInputStream(new File(resourcesPath + "/" + this.getApplicationConfigFileName()));
75  		} catch (final Exception e) {
76  			throw new FrameworkException(e.getMessage());
77  		}
78  		try {
79  			// final Properties applicationProperties = new Properties();
80  			// applicationProperties.load(fileStream);
81  			this.initializeLogger();
82  			ApplicationContainer.getInstance().setAppName(prop.getProperty("application-name"));
83  			ApplicationContainer.getInstance().setUsedDatabaseManagerFactoryName(prop.getProperty("database"));
84  			this.initializeDatabase(resourcesPath, ApplicationContainer.getInstance()
85  					.getUsedDatabaseManagerFactoryName());
86  			this.initializeRuntimePersistence();
87  			
88  			// ClassFacade classFacade =
89  			// ApplicationContainer.getInstance().getDatabaseManager().getClassFacade();
90  			// UserType mutableSet =
91  			// classFacade.createUserType("de>fhdw>wtf>context>model>collections>MutableList",
92  			// false, false);
93  			// UserType persistentSet =
94  			// classFacade.createUserType("de>fhdw>wtf>context>model>collections>PersistentList",
95  			// false, false);
96  			// UserType mutableMap =
97  			// classFacade.createUserType("de>fhdw>wtf>context>model>collections>MutableMap",
98  			// false, false);
99  			// UserType persistentMap =
100 			// classFacade.createUserType("de>fhdw>wtf>context>model>collections>PersistentMap",
101 			// false, false);
102 			//
103 			// classFacade.createSpecializationBetween(mutableSet,
104 			// persistentSet);
105 			// classFacade.createSpecializationBetween(mutableMap,
106 			// persistentMap);
107 			
108 			this.initializeFactories();
109 			this.registerActivities();
110 		} catch (final PersistenceException e) {
111 			throw new FrameworkException(e.getMessage());
112 		} catch (final IOException e) {
113 			throw new FrameworkException(e.getMessage());
114 		} finally {
115 		}
116 	}
117 	
118 	/**
119 	 * This method represents a hook, to enable the programmer to define the application specific Activities and the
120 	 * according View Facades.
121 	 */
122 	protected abstract void registerActivities();
123 	
124 	/**
125 	 * This method stops the application. The database connection will be closed.
126 	 */
127 	public void stop() {
128 		try {
129 			ApplicationContainer.getInstance().getDatabaseManager().disconnect();
130 		} catch (final PersistenceException e) {
131 			throw new FrameworkException(e.getMessage());
132 		}
133 	}
134 	
135 	/**
136 	 * This method initializes the user provides Object factories.
137 	 * 
138 	 * @throws ClassFacadeUninitializedException
139 	 *             If the ClassFacade has not been initialized before, this exception will be thrown.
140 	 * @throws TypeOrAssociationNotFoundException
141 	 *             If Type created by a factory is not known inside the database, an exception will be thrown.
142 	 */
143 	private void initializeFactories() throws ClassFacadeUninitializedException, TypeOrAssociationNotFoundException {
144 		final ObjectFactoryProvider factoryProvider = ObjectFactoryProvider.instance();
145 		ApplicationContainer.getInstance().setFactoryProvider(factoryProvider);
146 		final TypeManager typeManager =
147 				ApplicationContainer.getInstance().getDatabaseManager().getClassFacade().getTypeManager();
148 		try {
149 			factoryProvider.registerTypeFactory(
150 					typeManager,
151 					PersistentList.class.getName(),
152 					new PersistentListFactory());
153 		} catch (final TypeOrAssociationNotFoundException e) {
154 			/* if the application does not use lists, no factory is necessary */
155 		}
156 		try {
157 			factoryProvider.registerTypeFactory(typeManager, PersistentMap.class.getName(), new PersistentMapFactory());
158 		} catch (final TypeOrAssociationNotFoundException e) {
159 			/* if the application does not use maps, no factory is necessary */
160 		}
161 		this.registerTypeFactories(factoryProvider, typeManager);
162 	}
163 	
164 	/**
165 	 * This method must be implemented by a subclass to register the type factories to instantiate user defined types.
166 	 * 
167 	 * @param factoryProvider
168 	 *            The holder of the different factories, on it an the register method will be called.
169 	 * @param typeManager
170 	 *            The Type Manager can be used to provide the id of a type for which a factory is created.
171 	 * @throws TypeOrAssociationNotFoundException
172 	 *             If Type created by a factory is not known inside the database, an exception will be thrown.
173 	 */
174 	protected abstract void registerTypeFactories(ObjectFactoryProvider factoryProvider, TypeManager typeManager)
175 			throws TypeOrAssociationNotFoundException;
176 	
177 	/**
178 	 * This method initializes the Persistence for Runtime.
179 	 * 
180 	 * @throws PersistenceException
181 	 *             An exception will be thrown if there are any exception when contacting the database.
182 	 */
183 	private void initializeRuntimePersistence() throws PersistenceException {
184 		final ClassFacade classFacade = ApplicationContainer.getInstance().getDatabaseManager().getClassFacade();
185 		classFacade.initializeForRuntime();
186 	}
187 	
188 	/**
189 	 * This method initializes the logger component.
190 	 */
191 	private void initializeLogger() {
192 		ApplicationContainer.getInstance().setLogger(Logger.getInstance());
193 	}
194 	
195 	/**
196 	 * This method will initialize the Database Manager and its connection with the Database.
197 	 * 
198 	 * @param resourcesPath
199 	 *            Path where the resources for the DBMS connection are stored.
200 	 * @param databaseManagerFactoryClassName
201 	 *            A String which is the value of the parameter 'database' in the central configuration file. It should
202 	 *            be identical to the name of a concrete type which is a descendant of {@link DatabaseManagerFactory}.
203 	 * @throws IOException
204 	 *             An exception will be thrown if the database configuration file could not be read,
205 	 * @throws PersistenceException
206 	 *             if some database error occurs.
207 	 */
208 	private void initializeDatabase(final String resourcesPath, final String databaseManagerFactoryClassName)
209 			throws IOException, PersistenceException {
210 		Class<?> newClass;
211 		try {
212 			newClass =
213 					Class.forName(ApplicationStarter.SOURCE_PACKAGE_OF_DATABASE_MANAGER_FACTORIES + PACKAGE_DELIMITER
214 							+ databaseManagerFactoryClassName);
215 		} catch (final ClassNotFoundException e) {
216 			throw new DatabaseManagerFactoryNotInstantiableException(e.getMessage());
217 		}
218 		if (Modifier.isAbstract(newClass.getModifiers()) || !DatabaseManagerFactory.class.isAssignableFrom(newClass)) {
219 			throw new DatabaseManagerFactoryNotInstantiableException(
220 					DatabaseManagerFactoryNotInstantiableException.CLASS_IS_A_CONCRETE_DATABASE_MANAGER_FACTORY_REASON);
221 		}
222 		DatabaseManager database;
223 		try {
224 			database = ((DatabaseManagerFactory) newClass.newInstance()).getInstance();
225 		} catch (final InstantiationException | IllegalAccessException e) {
226 			throw new DatabaseManagerFactoryNotInstantiableException(e.getMessage());
227 		}
228 		
229 		database.setConnectionConstantsFromFile(PropertiesReaderFactory.getInstance().getPropertiesReaderFile(
230 				resourcesPath + "/" + this.getDatabaseConfigFileName()));
231 		database.connect();
232 		ApplicationContainer.getInstance().setDatabaseManager(database);
233 		
234 	}
235 	
236 	/**
237 	 * This Path specifies the directory where all the config files reside. It has to be specified by the developer of
238 	 * an application. By convention the path is: $(ProjectRoot)/src/main/resources/config
239 	 * 
240 	 * @return A String specifying the resources path.
241 	 */
242 	protected abstract String getResourcesPathJava();
243 	
244 	/**
245 	 * This Path specified the directory where all the config files reside when packaged as a Web Archive for deployment
246 	 * on a server. It has to be specified by the developer of an application. By convention path is: WEB-INF/config
247 	 * 
248 	 * @return A string spcifyong the resources path.
249 	 */
250 	protected abstract String getResourcesPathServer();
251 	
252 	/**
253 	 * The file name of the central application config file. In this file the database provider and other essential
254 	 * informations are stored. By convention it is named: application.properties
255 	 * 
256 	 * @return A String representing the File Name of the central application config file.
257 	 */
258 	protected abstract String getApplicationConfigFileName();
259 	
260 	/**
261 	 * The file name of the config file containing all needed information for the chosen persistence provider.
262 	 * 
263 	 * @return A String representing the file Name of the Persistence config file.
264 	 */
265 	protected abstract String getDatabaseConfigFileName();
266 	
267 	/**
268 	 * The package hierarchy to the root path of the model classes. It represents the technical prefix all model classes
269 	 * get, when placed in this package. By convention it is: generated.model
270 	 * 
271 	 * @return A String representing the model prefix.
272 	 */
273 	protected abstract String getModelPrefix();
274 	
275 }