View Javadoc

1   /**
2    *
3    * Copyright 2003-2004 The Apache Software Foundation
4    *
5    *  Licensed under the Apache License, Version 2.0 (the "License");
6    *  you may not use this file except in compliance with the License.
7    *  You may obtain a copy of the License at
8    *
9    *     http://www.apache.org/licenses/LICENSE-2.0
10   *
11   *  Unless required by applicable law or agreed to in writing, software
12   *  distributed under the License is distributed on an "AS IS" BASIS,
13   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   *  See the License for the specific language governing permissions and
15   *  limitations under the License.
16   */
17  package org.apache.geronimo.deployment.hot;
18  
19  import org.apache.geronimo.gbean.GBeanLifecycle;
20  import org.apache.geronimo.gbean.GBeanInfo;
21  import org.apache.geronimo.gbean.GBeanInfoBuilder;
22  import org.apache.geronimo.gbean.AbstractName;
23  import org.apache.geronimo.gbean.AbstractNameQuery;
24  import org.apache.geronimo.system.serverinfo.ServerInfo;
25  import org.apache.geronimo.deployment.plugin.factories.DeploymentFactoryImpl;
26  import org.apache.geronimo.deployment.plugin.jmx.JMXDeploymentManager;
27  import org.apache.geronimo.deployment.cli.DeployUtils;
28  import org.apache.geronimo.common.DeploymentException;
29  import org.apache.geronimo.kernel.config.PersistentConfigurationList;
30  import org.apache.geronimo.kernel.config.ConfigurationManager;
31  import org.apache.geronimo.kernel.config.Configuration;
32  import org.apache.geronimo.kernel.config.DeploymentWatcher;
33  import org.apache.geronimo.kernel.Kernel;
34  import org.apache.geronimo.kernel.repository.Artifact;
35  import org.apache.geronimo.kernel.repository.MissingDependencyException;
36  import org.apache.commons.logging.Log;
37  import org.apache.commons.logging.LogFactory;
38  
39  import javax.enterprise.deploy.spi.DeploymentManager;
40  import javax.enterprise.deploy.spi.TargetModuleID;
41  import javax.enterprise.deploy.spi.Target;
42  import javax.enterprise.deploy.spi.exceptions.DeploymentManagerCreationException;
43  import javax.enterprise.deploy.spi.status.ProgressObject;
44  import javax.enterprise.deploy.spi.factories.DeploymentFactory;
45  
46  import java.io.File;
47  import java.util.Set;
48  import java.util.Iterator;
49  
50  /**
51   * A directory-scanning hot deployer
52   *
53   * @version $Rev: 437623 $ $Date: 2006-08-28 02:48:23 -0700 (Mon, 28 Aug 2006) $
54   */
55  public class DirectoryHotDeployer implements HotDeployer, DeploymentWatcher, GBeanLifecycle { //todo: write unit tests
56      private static final Log log = LogFactory.getLog(DirectoryHotDeployer.class);
57  
58      // Try to make this stand out as the user is likely to get a ton of errors if this comes up
59      private static final String BAD_LAYOUT_MESSAGE = "CANNOT DEPLOY: It looks like you unpacked an application or module " +
60              "directly into the hot deployment directory.  THIS DOES NOT WORK.  You need to unpack into a " +
61              "subdirectory directly under the hot deploy directory.  For example, if the hot deploy directory " +
62              "is 'deploy/' and your file is 'webapp.war' then you could unpack it into a directory 'deploy/webapp.war/'";
63      private DirectoryMonitor monitor;
64      private String path;
65      private ServerInfo serverInfo;
66      private ConfigurationManager configManager;
67      private int pollIntervalMillis;
68      private String deploymentURI = "deployer:geronimo:inVM";
69      private String deploymentUser;
70      private String deploymentPassword;
71      private transient Kernel kernel;
72      private transient DeploymentFactory factory;
73      private transient TargetModuleID[] startupModules = null;
74      private transient boolean serverRunning = false;
75  
76      public DirectoryHotDeployer(String path, int pollIntervalMillis, ServerInfo serverInfo, ConfigurationManager configManager, Kernel kernel) {
77          this.path = path;
78          this.serverInfo = serverInfo;
79          this.pollIntervalMillis = pollIntervalMillis;
80          this.kernel = kernel;
81          this.configManager = configManager;
82      }
83  
84      public void deployed(Artifact id) {
85          // no action when something is deployed
86      }
87  
88      public void undeployed(Artifact id) {
89          // check to see whether the artifact was hot deployed, and if so, delete it
90          monitor.removeModuleId(id);
91      }
92  
93      public String getPath() {
94          return path;
95      }
96  
97      public void setPath(String path) {
98          this.path = path;
99      }
100 
101     public ServerInfo getServerInfo() {
102         return serverInfo;
103     }
104 
105     public void setServerInfo(ServerInfo serverInfo) {
106         this.serverInfo = serverInfo;
107     }
108 
109     public int getPollIntervalMillis() {
110         return pollIntervalMillis;
111     }
112 
113     public void setPollIntervalMillis(int pollIntervalMillis) {
114         this.pollIntervalMillis = pollIntervalMillis;
115     }
116 
117     public String getDeploymentURI() {
118         return deploymentURI;
119     }
120 
121     public void setDeploymentURI(String deploymentURI) {
122         if (deploymentURI != null && !deploymentURI.trim().equals("")) {
123             this.deploymentURI = deploymentURI.trim();
124         }
125     }
126 
127     public String getDeploymentUser() {
128         return deploymentUser;
129     }
130 
131     public void setDeploymentUser(String deploymentUser) {
132         this.deploymentUser = deploymentUser;
133     }
134 
135     public String getDeploymentPassword() {
136         return deploymentPassword;
137     }
138 
139     public void setDeploymentPassword(String deploymentPassword) {
140         this.deploymentPassword = deploymentPassword;
141     }
142 
143     public void doStart() throws Exception {
144         if (factory == null) {
145             factory = new DeploymentFactoryImpl();
146         }
147         File dir = serverInfo.resolve(path);
148         if (!dir.exists()) {
149             if (!dir.mkdirs()) {
150                 throw new IllegalStateException("Hot deploy directory " + dir.getAbsolutePath() + " does not exist and cannot be created!");
151             }
152         } else if (!dir.canRead() || !dir.isDirectory()) {
153             throw new IllegalStateException("Hot deploy directory " + dir.getAbsolutePath() + " is not a readable directory!");
154         }
155         DeploymentManager mgr = null;
156         try {
157             mgr = getDeploymentManager();
158             Target[] targets = mgr.getTargets();
159             startupModules = mgr.getAvailableModules(null, targets);
160             mgr.release();
161             mgr = null;
162             monitor = new DirectoryMonitor(dir, this, pollIntervalMillis);
163             log.debug("Hot deploy scanner intialized; starting main loop.");
164             Thread t = new Thread(monitor, "Geronimo hot deploy scanner");
165             t.setDaemon(true);
166             t.start();
167         } finally {
168             if (mgr != null) mgr.release();
169         }
170     }
171 
172     public void doStop() throws Exception {
173         monitor.close();
174     }
175 
176     public void doFail() {
177         if (monitor != null) {
178             monitor.close();
179         }
180     }
181 
182     public boolean isFileDeployed(File file, String configId) {
183         try {
184             DeployUtils.identifyTargetModuleIDs(startupModules, configId, true).toArray(new TargetModuleID[0]);
185             return true;
186         } catch (DeploymentException e) {
187             log.debug("Found new file in deploy directory on startup with ID " + configId);
188             return false;
189         }
190     }
191 
192     public boolean isServerRunning() {
193         if (serverRunning) {
194             return true;
195         }
196 
197         // a bit of a hack, but the PersistentConfigurationList is the only thing that knows whether the server is full started!
198         Set configLists = kernel.listGBeans(new AbstractNameQuery(PersistentConfigurationList.class.getName()));
199         for (Iterator i = configLists.iterator(); i.hasNext();) {
200             AbstractName configListName = (AbstractName) i.next();
201             try {
202                 Boolean result = (Boolean) kernel.getAttribute(configListName, "kernelFullyStarted");
203                 if (!result.booleanValue()) {
204                     return false;
205                 }
206             } catch (Exception e) {
207                 log.warn("Hot deployer unable to determine whether kernel is started", e);
208             }
209         }
210         serverRunning = true;
211         return true;
212     }
213 
214     public long getDeploymentTime(File file, String configId) {
215         try {
216             Artifact art = configManager.getArtifactResolver().resolveInClassLoader(Artifact.create(configId));
217             Configuration config = configManager.getConfiguration(art);
218             return config.getCreated();
219         } catch (MissingDependencyException e) {
220             log.error("Unknown configuration "+configId);
221             return -1;
222         }
223     }
224 
225     public void started() {
226         startupModules = null;
227         log.debug("Initialization complete; directory scanner entering normal scan mode");
228     }
229 
230     public boolean validateFile(File file, String configId) {
231         //todo: some more detailed evaluation
232         if (file.isDirectory() && (file.getName().equals("WEB-INF") || file.getName().equals("META-INF"))) {
233             log.error("(" + file.getName() + ") " + BAD_LAYOUT_MESSAGE);
234             return false;
235         }
236         return true;
237     }
238 
239     public String fileAdded(File file) {
240         log.info("Deploying " + file.getName());
241         DeploymentManager mgr = null;
242         TargetModuleID[] modules = null;
243         boolean completed = false;
244         try {
245             mgr = getDeploymentManager();
246             Target[] targets = mgr.getTargets();
247             ProgressObject po;
248             if (DeployUtils.isJarFile(file) || file.isDirectory()) {
249                 po = mgr.distribute(targets, file, null);
250             } else {
251                 po = mgr.distribute(targets, null, file);
252             }
253             waitForProgress(po);
254             if (po.getDeploymentStatus().isCompleted()) {
255                 modules = po.getResultTargetModuleIDs();
256                 po = mgr.start(modules);
257                 waitForProgress(po);
258                 if (po.getDeploymentStatus().isCompleted()) {
259                     completed = true;
260                 } else {
261                     log.warn("Unable to start some modules for " + file.getAbsolutePath());
262                 }
263                 modules = po.getResultTargetModuleIDs();
264                 for (int i = 0; i < modules.length; i++) {
265                     TargetModuleID result = modules[i];
266                     log.info(DeployUtils.reformat("Deployed " + result.getModuleID() + (targets.length > 1 ? " to " + result.getTarget().getName() : "") + (result.getWebURL() == null ? "" : " @ " + result.getWebURL()), 4, 72));
267                     if (result.getChildTargetModuleID() != null) {
268                         for (int j = 0; j < result.getChildTargetModuleID().length; j++) {
269                             TargetModuleID child = result.getChildTargetModuleID()[j];
270                             log.info(DeployUtils.reformat("  `-> " + child.getModuleID() + (child.getWebURL() == null ? "" : " @ " + child.getWebURL()), 4, 72));
271                         }
272                     }
273                 }
274             } else {
275                 log.error("Unable to deploy: " + po.getDeploymentStatus().getMessage());
276                 return null;
277             }
278         } catch (DeploymentManagerCreationException e) {
279             log.error("Unable to open deployer", e);
280             return null;
281         } catch (DeploymentException e) {
282             log.error("Unable to determine if file is a jar", e);
283         } finally {
284             if (mgr != null) mgr.release();
285         }
286         if (completed && modules != null) {
287             if (modules.length == 1) {
288                 return modules[0].getModuleID();
289             } else {
290                 return "";
291             }
292         } else if (modules != null) { //distribute completed but not start or something like that
293             return "";
294         } else {
295             return null;
296         }
297     }
298 
299     private DeploymentManager getDeploymentManager() throws DeploymentManagerCreationException {
300         DeploymentManager manager = factory.getDeploymentManager(deploymentURI, deploymentUser, deploymentPassword);
301         if (manager instanceof JMXDeploymentManager) {
302             ((JMXDeploymentManager) manager).setLogConfiguration(false, true);
303         }
304         return manager;
305     }
306 
307     public boolean fileRemoved(File file, String configId) {
308         log.info("Undeploying " + file.getName());
309         DeploymentManager mgr = null;
310         try {
311             mgr = getDeploymentManager();
312             Target[] targets = mgr.getTargets();
313             TargetModuleID[] ids = mgr.getAvailableModules(null, targets);
314             ids = (TargetModuleID[]) DeployUtils.identifyTargetModuleIDs(ids, configId, true).toArray(new TargetModuleID[0]);
315             ProgressObject po = mgr.undeploy(ids);
316             waitForProgress(po);
317             if (po.getDeploymentStatus().isCompleted()) {
318                 TargetModuleID[] modules = po.getResultTargetModuleIDs();
319                 for (int i = 0; i < modules.length; i++) {
320                     TargetModuleID result = modules[i];
321                     log.info(DeployUtils.reformat("Undeployed " + result.getModuleID() + (targets.length > 1 ? " to " + result.getTarget().getName() : ""), 4, 72));
322                 }
323             } else {
324                 log.error("Unable to undeploy " + file.getAbsolutePath() + "(" + configId + ")" + po.getDeploymentStatus().getMessage());
325                 return false;
326             }
327         } catch (DeploymentManagerCreationException e) {
328             log.error("Unable to open deployer", e);
329             return false;
330         } catch (Exception e) {
331             log.error("Unable to undeploy", e);
332             return false;
333         } finally {
334             if (mgr != null) mgr.release();
335         }
336         return true;
337     }
338 
339     public void fileUpdated(File file, String configId) {
340         log.info("Redeploying " + file.getName());
341         DeploymentManager mgr = null;
342         try {
343             mgr = getDeploymentManager();
344             Target[] targets = mgr.getTargets();
345             TargetModuleID[] ids = mgr.getAvailableModules(null, targets);
346             ids = (TargetModuleID[]) DeployUtils.identifyTargetModuleIDs(ids, configId, true).toArray(new TargetModuleID[0]);
347             ProgressObject po;
348             if (DeployUtils.isJarFile(file) || file.isDirectory()) {
349                 po = mgr.redeploy(ids, file, null);
350             } else {
351                 po = mgr.redeploy(ids, null, file);
352             }
353             waitForProgress(po);
354             if (po.getDeploymentStatus().isCompleted()) {
355                 TargetModuleID[] modules = po.getResultTargetModuleIDs();
356                 for (int i = 0; i < modules.length; i++) {
357                     TargetModuleID result = modules[i];
358                     log.info(DeployUtils.reformat("Redeployed " + result.getModuleID() + (targets.length > 1 ? " to " + result.getTarget().getName() : "") + (result.getWebURL() == null ? "" : " @ " + result.getWebURL()), 4, 72));
359                     if (result.getChildTargetModuleID() != null) {
360                         for (int j = 0; j < result.getChildTargetModuleID().length; j++) {
361                             TargetModuleID child = result.getChildTargetModuleID()[j];
362                             log.info(DeployUtils.reformat("  `-> " + child.getModuleID() + (child.getWebURL() == null ? "" : " @ " + child.getWebURL()), 4, 72));
363                         }
364                     }
365                 }
366             } else {
367                 log.error("Unable to undeploy " + file.getAbsolutePath() + "(" + configId + ")" + po.getDeploymentStatus().getMessage());
368             }
369         } catch (DeploymentManagerCreationException e) {
370             log.error("Unable to open deployer", e);
371         } catch (Exception e) {
372             log.error("Unable to undeploy", e);
373         } finally {
374             if (mgr != null) mgr.release();
375         }
376     }
377 
378     private void waitForProgress(ProgressObject po) {
379         while (po.getDeploymentStatus().isRunning()) {
380             try {
381                 Thread.sleep(100);
382             } catch (InterruptedException e) {
383                 log.error(e.getMessage(), e);
384             }
385         }
386     }
387 
388     public static final GBeanInfo GBEAN_INFO;
389 
390     static {
391         GBeanInfoBuilder infoFactory = GBeanInfoBuilder.createStatic(DirectoryHotDeployer.class);
392 
393         infoFactory.addAttribute("path", String.class, true, true);
394         infoFactory.addAttribute("pollIntervalMillis", int.class, true, true);
395 
396         // The next 3 args can be used to configure the hot deployer for a remote (out of VM) server
397         infoFactory.addAttribute("deploymentURI", String.class, true, true);
398         infoFactory.addAttribute("deploymentUser", String.class, true, true);
399         infoFactory.addAttribute("deploymentPassword", String.class, true, true);
400 
401         infoFactory.addReference("ConfigManager", ConfigurationManager.class, "ConfigurationManager");
402         infoFactory.addReference("ServerInfo", ServerInfo.class, "GBean");
403         infoFactory.addAttribute("kernel", Kernel.class, false, false);
404         infoFactory.addInterface(HotDeployer.class);
405 
406         infoFactory.setConstructor(new String[]{"path", "pollIntervalMillis", "ServerInfo", "ConfigManager", "kernel"});
407 
408         GBEAN_INFO = infoFactory.getBeanInfo();
409     }
410 
411     public static GBeanInfo getGBeanInfo() {
412         return GBEAN_INFO;
413     }
414 }