001: /*
002: * CoadunationLib: The coaduntion implementation library.
003: * Copyright (C) 2006 Rift IT Contracting
004: *
005: * This library is free software; you can redistribute it and/or
006: * modify it under the terms of the GNU Lesser General Public
007: * License as published by the Free Software Foundation; either
008: * version 2.1 of the License, or (at your option) any later version.
009: *
010: * This library is distributed in the hope that it will be useful,
011: * but WITHOUT ANY WARRANTY; without even the implied warranty of
012: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
013: * Lesser General Public License for more details.
014: *
015: * You should have received a copy of the GNU Lesser General Public
016: * License along with this library; if not, write to the Free Software
017: * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
018: *
019: * XMLConfigurationException.java
020: *
021: * DeploymentManager.java
022: *
023: * This object is responsible for controlling the loading of Coadunation
024: * deployment files using the deployment loader and other management objects.
025: */
026:
027: // the package path
028: package com.rift.coad.lib.deployment;
029:
030: // java imports
031: import java.io.File;
032: import java.util.Arrays;
033: import java.util.Map;
034: import java.util.HashMap;
035: import java.util.HashSet;
036: import java.util.concurrent.ConcurrentHashMap;
037: import java.util.Iterator;
038: import java.util.Set;
039: import java.util.Vector;
040:
041: // logging import
042: import org.apache.log4j.Logger;
043:
044: // import path
045: import com.rift.coad.lib.common.FileUtil;
046: import com.rift.coad.lib.cache.CacheRegistry;
047: import com.rift.coad.lib.configuration.Configuration;
048: import com.rift.coad.lib.configuration.ConfigurationFactory;
049: import com.rift.coad.lib.deployment.bean.BeanManager;
050: import com.rift.coad.lib.deployment.jmxbean.JMXBeanManager;
051: import com.rift.coad.lib.deployment.webservice.WebServiceManager;
052: import com.rift.coad.lib.naming.NamingDirector;
053: import com.rift.coad.lib.thread.CoadunationThreadGroup;
054: import com.rift.coad.lib.thread.BasicThread;
055: import com.rift.coad.lib.thread.ThreadGroupManager;
056: import com.rift.coad.util.lock.ObjectLockFactory;
057: import com.rift.coad.util.transaction.TransactionManager;
058:
059: /**
060: * This object is responsible for controlling the loading of Coadunation
061: * deployment files using the deployment loader and other management objects.
062: *
063: * @author Brett Chaldecott
064: */
065: public class DeploymentManager {
066:
067: /**
068: * The deployment thread responsible for controlling the management of
069: * the deployed beans, jmx beans and web services.
070: */
071: public class DeploymentThread extends BasicThread {
072: // the reference to the deployment manager
073: private DeploymentManager deploymentManager = null;
074: private boolean terminated = false;
075: private long delayPeriod = 0;
076:
077: /**
078: * The constructor of the deployment thread.
079: *
080: * @param deploymentManager The reference to the deployment manager.
081: */
082: public DeploymentThread(DeploymentManager deploymentManager)
083: throws DeploymentException, Exception {
084: try {
085: this .deploymentManager = deploymentManager;
086:
087: // retrieve the configuration
088: Configuration config = ConfigurationFactory
089: .getInstance().getConfig(this .getClass());
090:
091: delayPeriod = config.getLong("delay");
092:
093: } catch (Exception ex) {
094: throw new DeploymentException(
095: "Failed to init the deployment thread", ex);
096: }
097: }
098:
099: /**
100: * This method will be called to like the run method in a tradition thread.
101: */
102: public void process() {
103: // reset the class loader
104: while (isTerminated() == false) {
105: deploymentManager.manage();
106: delay();
107: }
108: }
109:
110: /**
111: * This method will be implemented by child objects to terminate the
112: * processing of this thread.
113: */
114: public synchronized void terminate() {
115: terminated = true;
116: DeploymentMonitor.getInstance().terminate();
117: }
118:
119: /**
120: * This method returns the information about what this thread is processing.
121: *
122: * @return String containing a description of this thread
123: */
124: public String getInfo() {
125: return "Managing the Coadunation deployment files.";
126: }
127:
128: /**
129: * This method will be called to check the status of this thread.
130: */
131: private synchronized boolean isTerminated() {
132: return terminated;
133: }
134:
135: /**
136: * This method will delay the processing for the processing for the given
137: * period of time.
138: */
139: private synchronized void delay() {
140: try {
141: wait(delayPeriod);
142: } catch (Exception ex) {
143: // ignore
144: }
145: }
146: }
147:
148: /**
149: * This object is responsible for maintaining the loader list. It supplies
150: * thread safe access to the list within.
151: */
152: public class LoaderList {
153: // the classes member variables
154: private Map loaders = null;
155:
156: /**
157: * The constructor of the loader list.
158: */
159: public LoaderList() {
160: loaders = new ConcurrentHashMap();
161: }
162:
163: /**
164: * This method will return true if the loader represented by the file is
165: * contained
166: *
167: * @return TRUE if the object is found FALSE if it is not.
168: * @param file The file to find in the list
169: */
170: public synchronized boolean contains(File file) {
171: return loaders.containsKey(file);
172: }
173:
174: /**
175: * This method will add a file to the container.
176: *
177: * @param file The file to add to the container.
178: * @param obj The object to add to the container.
179: */
180: public synchronized void add(File file, Object obj) {
181: loaders.put(file, obj);
182: }
183:
184: /**
185: * This method will return the reference to the deployment loader
186: * identified by the file.
187: *
188: * @return The reference to the deployment loader.
189: * @param file The reference to the file.
190: */
191: public synchronized DeploymentLoader get(File file) {
192: return (DeploymentLoader) loaders.get(file);
193: }
194:
195: /**
196: * This method removes the loader from the loader list identified by the
197: * key.
198: *
199: * @param file The file to remove from the list.
200: */
201: public synchronized void remove(File file) {
202: loaders.remove(file);
203: }
204:
205: /**
206: * This method will return the keys for the loaders.
207: *
208: * @return The keys for the set.
209: */
210: public synchronized Set getKeys() {
211: return loaders.keySet();
212: }
213: }
214:
215: // the class log variable
216: protected Logger log = Logger.getLogger(DeploymentManager.class
217: .getName());
218:
219: // private member variables
220: private LoaderList loaderList = null;
221: private CoadunationThreadGroup threadGroup = null;
222: private BeanManager beanManager = null;
223: private JMXBeanManager jmxBeanManager = null;
224: private WebServiceManager webServiceManager = null;
225: private DeploymentThread deploymentThread = null;
226: private File deploymentDir = null;
227:
228: /**
229: * Creates a new instance of DeploymentManager.
230: *
231: * @param threadGroup The reference to the thread manager.
232: * @param beanManager The reference to the bean manager object.
233: * @param jmxBeanManager The reference to the jmx bean object.
234: * @param webServiceManager The web service manager object.
235: * @exception DeploymentException
236: */
237: public DeploymentManager(CoadunationThreadGroup threadGroup,
238: BeanManager beanManager, JMXBeanManager jmxBeanManager,
239: WebServiceManager webServiceManager)
240: throws DeploymentException {
241: try {
242: // setup the member variables
243: loaderList = new LoaderList();
244: this .threadGroup = threadGroup.createThreadGroup();
245: this .beanManager = beanManager;
246: this .jmxBeanManager = jmxBeanManager;
247: this .webServiceManager = webServiceManager;
248:
249: // maintain the tmp directory for the loaders
250: maintainTmpDirectory();
251:
252: // retrieve the configuration
253: Configuration config = ConfigurationFactory.getInstance()
254: .getConfig(this .getClass());
255:
256: // retrieve the user information
257: String user = config.getString("username");
258:
259: deploymentDir = new File(config.getString("directory"));
260: if (deploymentDir.isDirectory() == false) {
261: throw new DeploymentException(
262: "The deployment directory ["
263: + config.getString("directory")
264: + "] is invalid");
265: }
266:
267: // instanciate the deployment thread
268: deploymentThread = new DeploymentThread(this );
269: this .threadGroup.addThread(deploymentThread, user);
270: deploymentThread.start();
271:
272: } catch (Exception ex) {
273: throw new DeploymentException(
274: "Failed to start the deployment loader : "
275: + ex.getMessage(), ex);
276: }
277: }
278:
279: /**
280: * This method shutdown the processing of the deployment manager.
281: */
282: public void shutdown() {
283: threadGroup.terminate();
284:
285: // unload all the loaded entries
286: try {
287: deploymentThread.join();
288: Set keys = loaderList.getKeys();
289: File[] loadedFiles = new File[keys.size()];
290: loadedFiles = (File[]) keys.toArray(loadedFiles);
291: Arrays.sort(loadedFiles);
292: for (int count = 0; count < loadedFiles.length; count++) {
293: removeEntry(loadedFiles[loadedFiles.length
294: - (count + 1)]);
295: }
296: } catch (Exception ex) {
297: log.error("Failed to shut down an entry because : "
298: + ex.getMessage(), ex);
299: }
300: }
301:
302: /**
303: * This private method is responsible for controlling the loading, and unloading
304: * of deployment files.
305: */
306: private void manage() {
307: try {
308: // retrieve a list of files
309: File[] files = FileUtil.filter(deploymentDir.listFiles(),
310: ".jar");
311: Arrays.sort(files);
312:
313: // check to see which file dates have changed
314: for (int count = 0; count < files.length; count++) {
315: File file = files[count];
316: if (loaderList.contains(file) == false) {
317: continue;
318: } else if (loaderList.get(file).getLastModified() != file
319: .lastModified()) {
320: removeEntry(file);
321: }
322: }
323:
324: // load new files
325: for (int count = 0; count < files.length; count++) {
326: File file = files[count];
327: if (loaderList.contains(file) == false) {
328: loadEntry(file);
329: }
330: }
331:
332: // unload files that have date changes and are no longer there
333: Set keySet = new HashSet(loaderList.getKeys());
334: for (Iterator iter = keySet.iterator(); iter.hasNext();) {
335: // array for entry
336: File key = (File) iter.next();
337: boolean found = false;
338: for (int count = 0; count < files.length; count++) {
339: File file = files[count];
340: if (file.equals(key)) {
341: found = true;
342: break;
343: }
344: }
345: if (found == false) {
346: removeEntry(key);
347: }
348: }
349:
350: } catch (Exception ex) {
351: log.error("Failed to load the files : " + ex.getMessage(),
352: ex);
353: }
354: // set the deployment monitor
355: if (false == DeploymentMonitor.getInstance()
356: .isInitDeployComplete()) {
357: DeploymentMonitor.getInstance().initDeployCompleted();
358: }
359: }
360:
361: /**
362: * This method will be responsible for removing a loader from memory.
363: *
364: * @param file The path to the file to remove.
365: */
366: private void removeEntry(File file) {
367: ClassLoader originalLoader = null;
368: try {
369: // instanciate the
370: DeploymentLoader loader = loaderList.get(file);
371: if (loader.getSuccessful() == false) {
372: log.info("This object was not loaded succesfully ["
373: + file.getAbsolutePath()
374: + "] removing internal " + "reference");
375: loaderList.remove(file);
376: return;
377: }
378: log.info("Un-load the deployment file ["
379: + file.getAbsolutePath() + "]");
380: originalLoader = Thread.currentThread()
381: .getContextClassLoader();
382: Thread.currentThread().setContextClassLoader(
383: loader.getClassLoader());
384: CacheRegistry.getInstance().terminateCache();
385: beanManager.unLoad(loader);
386: jmxBeanManager.unLoad(loader);
387: webServiceManager.unLoad(loader);
388: ObjectLockFactory.fin();
389: TransactionManager.fin();
390: loaderList.remove(file);
391:
392: // remove the reference to the loader from the class loader lookup
393: DeploymentLoader.ClassLoaderLookup.getInstance()
394: .removeClassLoader(loader);
395:
396: NamingDirector.getInstance().releaseContext();
397: ThreadGroupManager.getInstance().terminateThreadGroup();
398: } catch (Exception ex) {
399: log.error("Failed to unload the file ["
400: + file.getAbsolutePath() + "]", ex);
401: } finally {
402: try {
403: if (originalLoader != null) {
404: // reset the class loader
405: Thread.currentThread().setContextClassLoader(
406: originalLoader);
407: }
408: } catch (Exception exc) {
409: // ignore
410: }
411: }
412: }
413:
414: /**
415: * This method will and entry into memory.
416: *
417: * @param file The file to load into memory.
418: */
419: private void loadEntry(File file) {
420: DeploymentLoader loader = null;
421: ClassLoader originalLoader = null;
422: try {
423: // instanciate the
424: log.info("Load the deployment file ["
425: + file.getAbsolutePath() + "]");
426: try {
427: loader = new DeploymentLoader(file);
428: } catch (Exception ex) {
429: log.warn("Failed to load the file ["
430: + file.getAbsolutePath()
431: + "] will try this it again later.", ex);
432: return;
433: }
434:
435: loaderList.add(file, loader);
436: if (loader.getSuccessful() == false) {
437: log.info("The failed to load the ["
438: + file.getAbsolutePath() + "].");
439: return;
440: }
441:
442: // set the class loader for the beans
443: originalLoader = Thread.currentThread()
444: .getContextClassLoader();
445: Thread.currentThread().setContextClassLoader(
446: loader.getClassLoader());
447: ThreadGroupManager.getInstance().initThreadGroup(
448: threadGroup);
449: NamingDirector.getInstance().initContext();
450: CacheRegistry.getInstance().initCache();
451: ObjectLockFactory.init();
452: TransactionManager.init();
453: beanManager.load(loader);
454: jmxBeanManager.load(loader);
455: webServiceManager.load(loader);
456:
457: log.info("The file [" + file.getAbsolutePath()
458: + "] was loaded successfully");
459: } catch (Exception ex) {
460: // unload if there was any problem while loading the entry.
461: try {
462: if (loader != null) {
463: beanManager.unLoad(loader);
464: jmxBeanManager.unLoad(loader);
465: webServiceManager.unLoad(loader);
466: }
467:
468: } catch (Exception exc) {
469: // ignore any exceptions
470: }
471: try {
472: CacheRegistry.getInstance().terminateCache();
473: } catch (Exception exc) {
474: // failed to terminate the cache
475: log.error("Failed to terminate the cache ["
476: + exc.getMessage() + "]", ex);
477: }
478: try {
479: ThreadGroupManager.getInstance().terminateThreadGroup();
480: } catch (Exception exc) {
481: // failed to terminate the cache
482: log.error("Failed to terminate the thread group ["
483: + exc.getMessage() + "]", ex);
484: }
485: log.error("Failed to load the file ["
486: + file.getAbsolutePath() + "]", ex);
487: } finally {
488: try {
489: if (originalLoader != null) {
490: // reset the class loader
491: Thread.currentThread().setContextClassLoader(
492: originalLoader);
493: }
494: } catch (Exception exc) {
495: // ignore
496: }
497: }
498: }
499:
500: /**
501: * This method maintains the jar temp directory if the directory does not
502: * exist it will create it, and it will delete all misc jar files that it
503: * finds.
504: *
505: * @exception DeploymentException
506: */
507: private void maintainTmpDirectory() throws DeploymentException {
508: try {
509: // retrieve the loader configuration.
510: Configuration loaderConfig = ConfigurationFactory
511: .getInstance().getConfig(DeploymentLoader.class);
512:
513: // retrieve the temp directory
514: String tmpDirPath = loaderConfig
515: .getString(DeploymentLoader.TEMP_DIRECTORY);
516:
517: // create the temporary directory
518: File tmpDir = new File(tmpDirPath);
519: tmpDir.mkdirs();
520: if (tmpDir.isDirectory() == false) {
521: throw new DeploymentException(
522: "Failed to create the directory [" + tmpDirPath
523: + "]");
524: }
525: deleteDirectoryRecursive(tmpDir);
526: } catch (Exception ex) {
527: throw new DeploymentException(
528: "Failed to clean the tmp directory : "
529: + ex.getMessage(), ex);
530: }
531: }
532:
533: /**
534: * This method is called to delete all the files from a given directory.
535: *
536: * @param dir The directory to delete the files from
537: */
538: private void deleteDirectoryRecursive(File dir) throws Exception {
539: // list the files
540: File[] tmpFiles = dir.listFiles();
541: for (int index = 0; index < tmpFiles.length; index++) {
542: if (tmpFiles[index].isDirectory()) {
543: deleteDirectoryRecursive(tmpFiles[index]);
544: }
545: tmpFiles[index].delete();
546: }
547: }
548: }
|