001 /**
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017 package org.apache.geronimo.deployment.hot;
018
019 import java.io.File;
020 import java.util.Iterator;
021 import java.util.Set;
022
023 import javax.enterprise.deploy.spi.DeploymentManager;
024 import javax.enterprise.deploy.spi.Target;
025 import javax.enterprise.deploy.spi.TargetModuleID;
026 import javax.enterprise.deploy.spi.exceptions.DeploymentManagerCreationException;
027 import javax.enterprise.deploy.spi.factories.DeploymentFactory;
028 import javax.enterprise.deploy.spi.status.ProgressObject;
029
030 import org.apache.commons.logging.Log;
031 import org.apache.commons.logging.LogFactory;
032 import org.apache.geronimo.common.DeploymentException;
033 import org.apache.geronimo.deployment.cli.DeployUtils;
034 import org.apache.geronimo.deployment.plugin.factories.DeploymentFactoryWithKernel;
035 import org.apache.geronimo.deployment.plugin.jmx.JMXDeploymentManager;
036 import org.apache.geronimo.deployment.util.DeploymentUtil;
037 import org.apache.geronimo.gbean.AbstractName;
038 import org.apache.geronimo.gbean.AbstractNameQuery;
039 import org.apache.geronimo.gbean.GBeanInfo;
040 import org.apache.geronimo.gbean.GBeanInfoBuilder;
041 import org.apache.geronimo.gbean.GBeanLifecycle;
042 import org.apache.geronimo.kernel.Kernel;
043 import org.apache.geronimo.kernel.config.Configuration;
044 import org.apache.geronimo.kernel.config.ConfigurationManager;
045 import org.apache.geronimo.kernel.config.DeploymentWatcher;
046 import org.apache.geronimo.kernel.config.PersistentConfigurationList;
047 import org.apache.geronimo.kernel.repository.Artifact;
048 import org.apache.geronimo.kernel.repository.MissingDependencyException;
049 import org.apache.geronimo.system.serverinfo.ServerInfo;
050
051 /**
052 * A directory-scanning hot deployer
053 *
054 * @version $Rev: 706640 $ $Date: 2008-10-21 14:44:05 +0000 (Tue, 21 Oct 2008) $
055 */
056 public class DirectoryHotDeployer implements HotDeployer, DeploymentWatcher, GBeanLifecycle { //todo: write unit tests
057 private static final Log log = LogFactory.getLog(DirectoryHotDeployer.class);
058
059 // Try to make this stand out as the user is likely to get a ton of errors if this comes up
060 private static final String BAD_LAYOUT_MESSAGE = "CANNOT DEPLOY: It looks like you unpacked an application or module " +
061 "directly into the hot deployment directory. THIS DOES NOT WORK. You need to unpack into a " +
062 "subdirectory directly under the hot deploy directory. For example, if the hot deploy directory " +
063 "is 'deploy/' and your file is 'webapp.war' then you could unpack it into a directory 'deploy/webapp.war/'";
064 private DirectoryMonitor monitor;
065 private String path;
066 private ServerInfo serverInfo;
067 private ConfigurationManager configManager;
068 private int pollIntervalMillis;
069 private String deploymentURI = "deployer:geronimo:inVM";
070 private String deploymentUser;
071 private String deploymentPassword;
072 private transient Kernel kernel;
073 private transient DeploymentFactory factory;
074 private transient TargetModuleID[] startupModules = null;
075 private transient boolean serverRunning = false;
076
077 public DirectoryHotDeployer(String path, int pollIntervalMillis, ServerInfo serverInfo, ConfigurationManager configManager, Kernel kernel) {
078 this.path = path;
079 this.serverInfo = serverInfo;
080 this.pollIntervalMillis = pollIntervalMillis;
081 this.kernel = kernel;
082 this.configManager = configManager;
083 }
084
085 public void deployed(Artifact id) {
086 // no action when something is deployed
087 }
088
089 public void undeployed(Artifact id) {
090 // check to see whether the artifact was hot deployed, and if so, delete it
091 monitor.removeModuleId(id);
092 }
093
094 public String getPath() {
095 return path;
096 }
097
098 public void setPath(String path) {
099 this.path = path;
100 }
101
102 public ServerInfo getServerInfo() {
103 return serverInfo;
104 }
105
106 public void setServerInfo(ServerInfo serverInfo) {
107 this.serverInfo = serverInfo;
108 }
109
110 public int getPollIntervalMillis() {
111 return pollIntervalMillis;
112 }
113
114 public void setPollIntervalMillis(int pollIntervalMillis) {
115 this.pollIntervalMillis = pollIntervalMillis;
116 }
117
118 public String getDeploymentURI() {
119 return deploymentURI;
120 }
121
122 public void setDeploymentURI(String deploymentURI) {
123 if (deploymentURI != null && !deploymentURI.trim().equals("")) {
124 this.deploymentURI = deploymentURI.trim();
125 }
126 }
127
128 public String getDeploymentUser() {
129 return deploymentUser;
130 }
131
132 public void setDeploymentUser(String deploymentUser) {
133 this.deploymentUser = deploymentUser;
134 }
135
136 public String getDeploymentPassword() {
137 return deploymentPassword;
138 }
139
140 public void setDeploymentPassword(String deploymentPassword) {
141 this.deploymentPassword = deploymentPassword;
142 }
143
144 public void doStart() throws Exception {
145 if (factory == null) {
146 factory = new DeploymentFactoryWithKernel(kernel);
147 }
148 File dir = serverInfo.resolveServer(path);
149 if (!dir.exists()) {
150 if (!dir.mkdirs()) {
151 throw new IllegalStateException("Hot deploy directory " + dir.getAbsolutePath() + " does not exist and cannot be created!");
152 }
153 } else if (!dir.canRead() || !dir.isDirectory()) {
154 throw new IllegalStateException("Hot deploy directory " + dir.getAbsolutePath() + " is not a readable directory!");
155 }
156 DeploymentManager mgr = null;
157 try {
158 mgr = getDeploymentManager();
159 Target[] targets = mgr.getTargets();
160 startupModules = mgr.getAvailableModules(null, targets);
161 mgr.release();
162 mgr = null;
163 monitor = new DirectoryMonitor(dir, this, pollIntervalMillis);
164 log.debug("Hot deploy scanner intialized; starting main loop.");
165 Thread t = new Thread(monitor, "Geronimo hot deploy scanner");
166 t.setDaemon(true);
167 t.start();
168 } finally {
169 if (mgr != null) mgr.release();
170 }
171 }
172
173 public void doStop() throws Exception {
174 monitor.close();
175 }
176
177 public void doFail() {
178 if (monitor != null) {
179 monitor.close();
180 }
181 }
182
183 public boolean isFileDeployed(File file, String configId) {
184 DeploymentManager mgr = null;
185 try {
186 if (startupModules != null) {
187 DeployUtils.identifyTargetModuleIDs(startupModules, configId, true).toArray(new TargetModuleID[0]);
188 }
189 else {
190 mgr = getDeploymentManager();
191 Target[] targets = mgr.getTargets();
192 TargetModuleID[] ids = mgr.getAvailableModules(null, targets);
193 DeployUtils.identifyTargetModuleIDs(ids, configId, true).toArray(new TargetModuleID[0]);
194 mgr.release();
195 mgr = null;
196 }
197 return true;
198 } catch (DeploymentException e) {
199 log.debug("Found new file in deploy directory on startup with ID " + configId);
200 } catch (Exception e) {
201 log.error("Unable to check status", e);
202 } finally {
203 if (mgr != null) {
204 mgr.release();
205 mgr = null;
206 }
207 }
208 return false;
209 }
210
211 public boolean isServerRunning() {
212 if (serverRunning) {
213 return true;
214 }
215
216 // a bit of a hack, but the PersistentConfigurationList is the only thing that knows whether the server is full started!
217 Set configLists = kernel.listGBeans(new AbstractNameQuery(PersistentConfigurationList.class.getName()));
218 for (Iterator i = configLists.iterator(); i.hasNext();) {
219 AbstractName configListName = (AbstractName) i.next();
220 try {
221 Boolean result = (Boolean) kernel.getAttribute(configListName, "kernelFullyStarted");
222 if (!result.booleanValue()) {
223 return false;
224 }
225 } catch (Exception e) {
226 log.warn("Hot deployer unable to determine whether kernel is started", e);
227 }
228 }
229 serverRunning = true;
230 return true;
231 }
232
233 public long getDeploymentTime(File file, String configId) {
234 try {
235 Artifact art = configManager.getArtifactResolver().resolveInClassLoader(Artifact.create(configId));
236 Configuration config = configManager.getConfiguration(art);
237 return config.getCreated();
238 } catch (MissingDependencyException e) {
239 log.error("Unknown configuration "+configId);
240 return -1;
241 }
242 }
243
244 public void started() {
245 startupModules = null;
246 log.debug("Initialization complete; directory scanner entering normal scan mode");
247 }
248
249 public boolean validateFile(File file, String configId) {
250 //todo: some more detailed evaluation
251 if (file.isDirectory() && (file.getName().equals("WEB-INF") || file.getName().equals("META-INF"))) {
252 log.error("(" + file.getName() + ") " + BAD_LAYOUT_MESSAGE);
253 return false;
254 }
255 return true;
256 }
257
258 public String fileAdded(File file) {
259 log.info("Deploying " + file.getName());
260 DeploymentManager mgr = null;
261 TargetModuleID[] modules = null;
262 boolean completed = false;
263 try {
264 mgr = getDeploymentManager();
265 Target[] targets = mgr.getTargets();
266 if (null == targets) {
267 throw new IllegalStateException("No target to distribute to");
268 }
269 targets = new Target[] {targets[0]};
270
271 ProgressObject po;
272 if (DeployUtils.isJarFile(file) || file.isDirectory()) {
273 po = mgr.distribute(targets, file, null);
274 } else {
275 po = mgr.distribute(targets, null, file);
276 }
277 waitForProgress(po);
278 if (po.getDeploymentStatus().isCompleted()) {
279 modules = po.getResultTargetModuleIDs();
280 po = mgr.start(modules);
281 waitForProgress(po);
282 if (po.getDeploymentStatus().isCompleted()) {
283 completed = true;
284 } else {
285 log.warn("Unable to start some modules for " + file.getAbsolutePath());
286 }
287 modules = po.getResultTargetModuleIDs();
288 for (int i = 0; i < modules.length; i++) {
289 TargetModuleID result = modules[i];
290 log.info(DeployUtils.reformat("Deployed " + result.getModuleID() + (targets.length > 1 ? " to " + result.getTarget().getName() : "") + (result.getWebURL() == null ? "" : " @ " + result.getWebURL()), 4, 72));
291 if (result.getChildTargetModuleID() != null) {
292 for (int j = 0; j < result.getChildTargetModuleID().length; j++) {
293 TargetModuleID child = result.getChildTargetModuleID()[j];
294 log.info(DeployUtils.reformat(" `-> " + child.getModuleID() + (child.getWebURL() == null ? "" : " @ " + child.getWebURL()), 4, 72));
295 }
296 }
297 }
298 } else {
299 //Try to delete the module , that failed to successfully hot-deploy
300 log.error("Unable to deploy: " + po.getDeploymentStatus().getMessage());
301 String delfile=file.getAbsolutePath();
302 File fd = new File(delfile);
303 if(fd.isDirectory()){
304 log.info("Deleting the Directory: "+delfile);
305 if(DeploymentUtil.recursiveDelete(fd))
306 log.debug("Successfully deleted the Directory: "+delfile);
307 else
308 log.error("Couldn't delete the hot deployed directory"+delfile);
309 }else if(fd.isFile()){
310 log.info("Deleting the File: "+delfile);
311 if(fd.delete()){
312 log.debug("Successfully deleted the File: "+delfile);
313 }else
314 log.error("Couldn't delete the hot deployed directory"+delfile);
315 }
316
317 return null;
318 }
319 } catch (DeploymentManagerCreationException e) {
320 log.error("Unable to open deployer", e);
321 return null;
322 } catch (DeploymentException e) {
323 log.error("Unable to determine if file is a jar", e);
324 } finally {
325 if (mgr != null) mgr.release();
326 }
327 if (completed && modules != null) {
328 if (modules.length == 1) {
329 return modules[0].getModuleID();
330 } else {
331 return "";
332 }
333 } else if (modules != null) { //distribute completed but not start or something like that
334 return "";
335 } else {
336 return null;
337 }
338 }
339
340 private DeploymentManager getDeploymentManager() throws DeploymentManagerCreationException {
341 DeploymentManager manager = factory.getDeploymentManager(deploymentURI, deploymentUser, deploymentPassword);
342 if (manager instanceof JMXDeploymentManager) {
343 ((JMXDeploymentManager) manager).setLogConfiguration(false, true);
344 }
345 return manager;
346 }
347
348 public boolean fileRemoved(File file, String configId) {
349 log.info("Undeploying " + file.getName());
350 DeploymentManager mgr = null;
351 try {
352 mgr = getDeploymentManager();
353 Target[] targets = mgr.getTargets();
354 TargetModuleID[] ids = mgr.getAvailableModules(null, targets);
355 ids = (TargetModuleID[]) DeployUtils.identifyTargetModuleIDs(ids, configId, true).toArray(new TargetModuleID[0]);
356 ProgressObject po = mgr.undeploy(ids);
357 waitForProgress(po);
358 if (po.getDeploymentStatus().isCompleted()) {
359 TargetModuleID[] modules = po.getResultTargetModuleIDs();
360 for (int i = 0; i < modules.length; i++) {
361 TargetModuleID result = modules[i];
362 log.info(DeployUtils.reformat("Undeployed " + result.getModuleID() + (targets.length > 1 ? " to " + result.getTarget().getName() : ""), 4, 72));
363 }
364 } else {
365 log.error("Unable to undeploy " + file.getAbsolutePath() + "(" + configId + ")" + po.getDeploymentStatus().getMessage());
366 return false;
367 }
368 } catch (DeploymentManagerCreationException e) {
369 log.error("Unable to open deployer", e);
370 return false;
371 } catch (Exception e) {
372 log.error("Unable to undeploy", e);
373 return false;
374 } finally {
375 if (mgr != null) mgr.release();
376 }
377 return true;
378 }
379
380 public String getModuleId(String config) {
381 DeploymentManager mgr = null;
382 TargetModuleID[] modules = null;
383 try {
384 mgr = getDeploymentManager();
385 Target[] targets = mgr.getTargets();
386 TargetModuleID[] ids = mgr.getAvailableModules(null, targets);
387 for(int j=0;j<ids.length;j++) {
388 String moduleId=ids[j].getModuleID();
389 String[] parts = moduleId.split("/", -1);
390 if (parts.length != 4) {
391 continue;
392 }
393 if(parts[1] != null && parts[1].equals(config))
394 return ids[j].getModuleID();
395 }
396 } catch(Exception ex){
397 log.error("Unable to getModuleId",ex);
398 }
399 return config;
400 }
401
402 public String fileUpdated(File file, String configId) {
403 log.info("Redeploying " + file.getName());
404 DeploymentManager mgr = null;
405 TargetModuleID[] modules = null;
406 try {
407 mgr = getDeploymentManager();
408 Target[] targets = mgr.getTargets();
409 TargetModuleID[] ids = mgr.getAvailableModules(null, targets);
410 ids = (TargetModuleID[]) DeployUtils.identifyTargetModuleIDs(ids, configId, true).toArray(new TargetModuleID[0]);
411 ProgressObject po;
412 if (DeployUtils.isJarFile(file) || file.isDirectory()) {
413 po = mgr.redeploy(ids, file, null);
414 } else {
415 po = mgr.redeploy(ids, null, file);
416 }
417 waitForProgress(po);
418 if (po.getDeploymentStatus().isCompleted()) {
419 modules = po.getResultTargetModuleIDs();
420 for (int i = 0; i < modules.length; i++) {
421 TargetModuleID result = modules[i];
422 log.info(DeployUtils.reformat("Redeployed " + result.getModuleID() + (targets.length > 1 ? " to " + result.getTarget().getName() : "") + (result.getWebURL() == null ? "" : " @ " + result.getWebURL()), 4, 72));
423 if (result.getChildTargetModuleID() != null) {
424 for (int j = 0; j < result.getChildTargetModuleID().length; j++) {
425 TargetModuleID child = result.getChildTargetModuleID()[j];
426 log.info(DeployUtils.reformat(" `-> " + child.getModuleID() + (child.getWebURL() == null ? "" : " @ " + child.getWebURL()), 4, 72));
427 }
428 }
429 }
430 } else {
431 log.error("Unable to undeploy " + file.getAbsolutePath() + "(" + configId + ")" + po.getDeploymentStatus().getMessage());
432 }
433 } catch (DeploymentManagerCreationException e) {
434 log.error("Unable to open deployer", e);
435 } catch (Exception e) {
436 log.error("Unable to undeploy", e);
437 } finally {
438 if (mgr != null) mgr.release();
439 }
440 if (modules != null) {
441 if (modules.length == 1) {
442 return modules[0].getModuleID();
443 } else {
444 return "";
445 }
446 } else {
447 return null;
448 }
449 }
450
451 private void waitForProgress(ProgressObject po) {
452 while (po.getDeploymentStatus().isRunning()) {
453 try {
454 Thread.sleep(100);
455 } catch (InterruptedException e) {
456 log.error(e.getMessage(), e);
457 }
458 }
459 }
460
461 public static final GBeanInfo GBEAN_INFO;
462
463 static {
464 GBeanInfoBuilder infoFactory = GBeanInfoBuilder.createStatic(DirectoryHotDeployer.class);
465
466 infoFactory.addAttribute("path", String.class, true, true);
467 infoFactory.addAttribute("pollIntervalMillis", int.class, true, true);
468
469 // The next 3 args can be used to configure the hot deployer for a remote (out of VM) server
470 infoFactory.addAttribute("deploymentURI", String.class, true, true);
471 infoFactory.addAttribute("deploymentUser", String.class, true, true);
472 infoFactory.addAttribute("deploymentPassword", String.class, true, true);
473
474 infoFactory.addReference("ConfigManager", ConfigurationManager.class, "ConfigurationManager");
475 infoFactory.addReference("ServerInfo", ServerInfo.class, "GBean");
476 infoFactory.addAttribute("kernel", Kernel.class, false, false);
477 infoFactory.addInterface(HotDeployer.class);
478
479 infoFactory.setConstructor(new String[]{"path", "pollIntervalMillis", "ServerInfo", "ConfigManager", "kernel"});
480
481 GBEAN_INFO = infoFactory.getBeanInfo();
482 }
483
484 public static GBeanInfo getGBeanInfo() {
485 return GBEAN_INFO;
486 }
487 }