001 /** 002 * 003 * Copyright 2003-2004 The Apache Software Foundation 004 * 005 * Licensed under the Apache License, Version 2.0 (the "License"); 006 * you may not use this file except in compliance with the License. 007 * 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 org.apache.geronimo.gbean.GBeanLifecycle; 020 import org.apache.geronimo.gbean.GBeanInfo; 021 import org.apache.geronimo.gbean.GBeanInfoBuilder; 022 import org.apache.geronimo.gbean.AbstractName; 023 import org.apache.geronimo.gbean.AbstractNameQuery; 024 import org.apache.geronimo.system.serverinfo.ServerInfo; 025 import org.apache.geronimo.deployment.plugin.factories.DeploymentFactoryImpl; 026 import org.apache.geronimo.deployment.plugin.jmx.JMXDeploymentManager; 027 import org.apache.geronimo.deployment.cli.DeployUtils; 028 import org.apache.geronimo.common.DeploymentException; 029 import org.apache.geronimo.kernel.config.PersistentConfigurationList; 030 import org.apache.geronimo.kernel.config.ConfigurationManager; 031 import org.apache.geronimo.kernel.config.Configuration; 032 import org.apache.geronimo.kernel.config.DeploymentWatcher; 033 import org.apache.geronimo.kernel.Kernel; 034 import org.apache.geronimo.kernel.repository.Artifact; 035 import org.apache.geronimo.kernel.repository.MissingDependencyException; 036 import org.apache.commons.logging.Log; 037 import org.apache.commons.logging.LogFactory; 038 039 import javax.enterprise.deploy.spi.DeploymentManager; 040 import javax.enterprise.deploy.spi.TargetModuleID; 041 import javax.enterprise.deploy.spi.Target; 042 import javax.enterprise.deploy.spi.exceptions.DeploymentManagerCreationException; 043 import javax.enterprise.deploy.spi.status.ProgressObject; 044 import javax.enterprise.deploy.spi.factories.DeploymentFactory; 045 046 import java.io.File; 047 import java.util.Set; 048 import java.util.Iterator; 049 050 /** 051 * A directory-scanning hot deployer 052 * 053 * @version $Rev: 437623 $ $Date: 2006-08-28 02:48:23 -0700 (Mon, 28 Aug 2006) $ 054 */ 055 public class DirectoryHotDeployer implements HotDeployer, DeploymentWatcher, GBeanLifecycle { //todo: write unit tests 056 private static final Log log = LogFactory.getLog(DirectoryHotDeployer.class); 057 058 // Try to make this stand out as the user is likely to get a ton of errors if this comes up 059 private static final String BAD_LAYOUT_MESSAGE = "CANNOT DEPLOY: It looks like you unpacked an application or module " + 060 "directly into the hot deployment directory. THIS DOES NOT WORK. You need to unpack into a " + 061 "subdirectory directly under the hot deploy directory. For example, if the hot deploy directory " + 062 "is 'deploy/' and your file is 'webapp.war' then you could unpack it into a directory 'deploy/webapp.war/'"; 063 private DirectoryMonitor monitor; 064 private String path; 065 private ServerInfo serverInfo; 066 private ConfigurationManager configManager; 067 private int pollIntervalMillis; 068 private String deploymentURI = "deployer:geronimo:inVM"; 069 private String deploymentUser; 070 private String deploymentPassword; 071 private transient Kernel kernel; 072 private transient DeploymentFactory factory; 073 private transient TargetModuleID[] startupModules = null; 074 private transient boolean serverRunning = false; 075 076 public DirectoryHotDeployer(String path, int pollIntervalMillis, ServerInfo serverInfo, ConfigurationManager configManager, Kernel kernel) { 077 this.path = path; 078 this.serverInfo = serverInfo; 079 this.pollIntervalMillis = pollIntervalMillis; 080 this.kernel = kernel; 081 this.configManager = configManager; 082 } 083 084 public void deployed(Artifact id) { 085 // no action when something is deployed 086 } 087 088 public void undeployed(Artifact id) { 089 // check to see whether the artifact was hot deployed, and if so, delete it 090 monitor.removeModuleId(id); 091 } 092 093 public String getPath() { 094 return path; 095 } 096 097 public void setPath(String path) { 098 this.path = path; 099 } 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 }