001: package dalma.container;
002:
003: import java.io.File;
004: import java.util.Hashtable;
005: import java.util.Map;
006: import java.util.concurrent.Callable;
007: import java.util.concurrent.Future;
008: import java.util.concurrent.FutureTask;
009: import java.util.concurrent.ExecutionException;
010: import java.util.logging.Level;
011: import java.util.logging.Logger;
012:
013: /**
014: * Monitors a ".dar" file for its update and redeploy
015: * if necessary.
016: *
017: * @author Kohsuke Kawaguchi
018: */
019: final class Redeployer extends FileChangeMonitor {
020:
021: private static final Logger logger = Logger
022: .getLogger(Redeployer.class.getName());
023:
024: private final Container container;
025:
026: static class PassiveFutureTask<V> extends FutureTask<V> {
027: private NoopCallable<V> callable;
028:
029: public PassiveFutureTask() {
030: this (new NoopCallable<V>());
031: }
032:
033: private PassiveFutureTask(NoopCallable<V> c) {
034: super (c);
035: this .callable = c;
036: }
037:
038: /**
039: * Sets this {@link Future} as 'completed' with the given value.
040: */
041: public void completeWith(V v) {
042: this .callable.v = v;
043: run();
044: }
045:
046: public void abortWith(Exception e) {
047: this .callable.e = e;
048: run();
049: }
050:
051: static class NoopCallable<V> implements Callable<V> {
052: private V v;
053: private Exception e;
054:
055: public V call() throws Exception {
056: if (e != null)
057: throw e;
058: return v;
059: }
060: }
061: }
062:
063: /**
064: * {@link Future} objects that are listening for the completion of the redeployment.
065: *
066: * Access is synchronized.
067: */
068: private final Map<File, PassiveFutureTask<WorkflowApplication>> futures = new Hashtable<File, PassiveFutureTask<WorkflowApplication>>();
069:
070: Redeployer(Container container) {
071: super (container.appsDir);
072: this .container = container;
073: }
074:
075: /**
076: * Gets a {@link Future} object that receives the result of
077: * install/uninstall/reinstall operations.
078: *
079: * @param dirName
080: * This has to be a directory (either existing or expected to be created)
081: * under the redeployer's supervision.
082: *
083: * @return
084: * always non-null. This future object receives a {@link WorkflowApplication} object
085: * if the update/installation is successful, or else
086: * {@link ExecutionException} that wraps {@link FailedOperationException},
087: * indicating a failure.
088: */
089: public Future<WorkflowApplication> getFuture(File dirName) {
090: synchronized (futures) {
091: PassiveFutureTask<WorkflowApplication> ft = futures
092: .get(dirName);
093: if (ft == null) {
094: ft = new PassiveFutureTask<WorkflowApplication>();
095: futures.put(dirName, ft);
096: }
097: return ft;
098: }
099: }
100:
101: @Override
102: protected void onAdded(File file) {
103: if (isDar(file))
104: Container.explode(file);
105: if (file.isDirectory()) {
106: logger.info("New application '" + file.getName()
107: + "' detected. Deploying.");
108: try {
109: WorkflowApplication wa = container.deploy(file);
110: notifyFutures(file, wa);
111: } catch (FailedOperationException e) {
112: logger.log(Level.SEVERE, "Unable to deploy", e);
113: notifyFutures(file, e);
114: }
115: }
116: }
117:
118: @Override
119: protected void onUpdated(File file) {
120: if (isDar(file))
121: Container.explode(file);
122: if (file.isDirectory()) {
123: try {
124: WorkflowApplication wa = container.getApplication(file
125: .getName());
126: if (wa != null) {
127: logger.info("Changed detected in application '"
128: + wa.getName() + "'. Re-deploying.");
129: wa.unload();
130: wa.start();
131: }
132: notifyFutures(file, wa);
133: } catch (FailedOperationException e) {
134: logger.log(Level.SEVERE, "Unable to redeploy", e);
135: notifyFutures(file, e);
136: }
137: }
138: }
139:
140: protected void onDeleted(File file) {
141: WorkflowApplication wa = container.getApplication(file
142: .getName());
143: if (wa != null) {
144: logger.info("Application '" + file.getName()
145: + "' is removed. Undeploying.");
146: wa.remove();
147: notifyFutures(file, (WorkflowApplication) null);
148: }
149: }
150:
151: /**
152: * Updates {@link Future} objects blocking on a directory.
153: */
154: private void notifyFutures(File file, FailedOperationException e) {
155: PassiveFutureTask<WorkflowApplication> ft = futures
156: .remove(file);
157: if (ft != null) {
158: ft.abortWith(e);
159: }
160: }
161:
162: private void notifyFutures(File file, WorkflowApplication wa) {
163: PassiveFutureTask<WorkflowApplication> ft = futures
164: .remove(file);
165: if (ft != null) {
166: ft.completeWith(wa);
167: }
168: }
169:
170: private static boolean isDar(File f) {
171: return f.getName().endsWith(".dar");
172: }
173:
174: }
|