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: 545480 $ $Date: 2007-06-08 07:30:20 -0400 (Fri, 08 Jun 2007) $
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 ProgressObject po;
267 if (DeployUtils.isJarFile(file) || file.isDirectory()) {
268 po = mgr.distribute(targets, file, null);
269 } else {
270 po = mgr.distribute(targets, null, file);
271 }
272 waitForProgress(po);
273 if (po.getDeploymentStatus().isCompleted()) {
274 modules = po.getResultTargetModuleIDs();
275 po = mgr.start(modules);
276 waitForProgress(po);
277 if (po.getDeploymentStatus().isCompleted()) {
278 completed = true;
279 } else {
280 log.warn("Unable to start some modules for " + file.getAbsolutePath());
281 }
282 modules = po.getResultTargetModuleIDs();
283 for (int i = 0; i < modules.length; i++) {
284 TargetModuleID result = modules[i];
285 log.info(DeployUtils.reformat("Deployed " + result.getModuleID() + (targets.length > 1 ? " to " + result.getTarget().getName() : "") + (result.getWebURL() == null ? "" : " @ " + result.getWebURL()), 4, 72));
286 if (result.getChildTargetModuleID() != null) {
287 for (int j = 0; j < result.getChildTargetModuleID().length; j++) {
288 TargetModuleID child = result.getChildTargetModuleID()[j];
289 log.info(DeployUtils.reformat(" `-> " + child.getModuleID() + (child.getWebURL() == null ? "" : " @ " + child.getWebURL()), 4, 72));
290 }
291 }
292 }
293 } else {
294 //Try to delete the module , that failed to successfully hot-deploy
295 log.error("Unable to deploy: " + po.getDeploymentStatus().getMessage());
296 String delfile=file.getAbsolutePath();
297 File fd = new File(delfile);
298 if(fd.isDirectory()){
299 log.info("Deleting the Directory: "+delfile);
300 if(DeploymentUtil.recursiveDelete(fd))
301 log.debug("Successfully deleted the Directory: "+delfile);
302 else
303 log.error("Couldn't delete the hot deployed directory"+delfile);
304 }else if(fd.isFile()){
305 log.info("Deleting the File: "+delfile);
306 if(fd.delete()){
307 log.debug("Successfully deleted the File: "+delfile);
308 }else
309 log.error("Couldn't delete the hot deployed directory"+delfile);
310 }
311
312 return null;
313 }
314 } catch (DeploymentManagerCreationException e) {
315 log.error("Unable to open deployer", e);
316 return null;
317 } catch (DeploymentException e) {
318 log.error("Unable to determine if file is a jar", e);
319 } finally {
320 if (mgr != null) mgr.release();
321 }
322 if (completed && modules != null) {
323 if (modules.length == 1) {
324 return modules[0].getModuleID();
325 } else {
326 return "";
327 }
328 } else if (modules != null) { //distribute completed but not start or something like that
329 return "";
330 } else {
331 return null;
332 }
333 }
334
335 private DeploymentManager getDeploymentManager() throws DeploymentManagerCreationException {
336 DeploymentManager manager = factory.getDeploymentManager(deploymentURI, deploymentUser, deploymentPassword);
337 if (manager instanceof JMXDeploymentManager) {
338 ((JMXDeploymentManager) manager).setLogConfiguration(false, true);
339 }
340 return manager;
341 }
342
343 public boolean fileRemoved(File file, String configId) {
344 log.info("Undeploying " + file.getName());
345 DeploymentManager mgr = null;
346 try {
347 mgr = getDeploymentManager();
348 Target[] targets = mgr.getTargets();
349 TargetModuleID[] ids = mgr.getAvailableModules(null, targets);
350 ids = (TargetModuleID[]) DeployUtils.identifyTargetModuleIDs(ids, configId, true).toArray(new TargetModuleID[0]);
351 ProgressObject po = mgr.undeploy(ids);
352 waitForProgress(po);
353 if (po.getDeploymentStatus().isCompleted()) {
354 TargetModuleID[] modules = po.getResultTargetModuleIDs();
355 for (int i = 0; i < modules.length; i++) {
356 TargetModuleID result = modules[i];
357 log.info(DeployUtils.reformat("Undeployed " + result.getModuleID() + (targets.length > 1 ? " to " + result.getTarget().getName() : ""), 4, 72));
358 }
359 } else {
360 log.error("Unable to undeploy " + file.getAbsolutePath() + "(" + configId + ")" + po.getDeploymentStatus().getMessage());
361 return false;
362 }
363 } catch (DeploymentManagerCreationException e) {
364 log.error("Unable to open deployer", e);
365 return false;
366 } catch (Exception e) {
367 log.error("Unable to undeploy", e);
368 return false;
369 } finally {
370 if (mgr != null) mgr.release();
371 }
372 return true;
373 }
374
375 public String getModuleId(String config) {
376 DeploymentManager mgr = null;
377 TargetModuleID[] modules = null;
378 try {
379 mgr = getDeploymentManager();
380 Target[] targets = mgr.getTargets();
381 TargetModuleID[] ids = mgr.getAvailableModules(null, targets);
382 for(int j=0;j<ids.length;j++) {
383 String moduleId=ids[j].getModuleID();
384 String[] parts = moduleId.split("/", -1);
385 if (parts.length != 4) {
386 continue;
387 }
388 if(parts[1] != null && parts[1].equals(config))
389 return ids[j].getModuleID();
390 }
391 } catch(Exception ex){
392 log.error("Unable to getModuleId",ex);
393 }
394 return config;
395 }
396
397 public String fileUpdated(File file, String configId) {
398 log.info("Redeploying " + file.getName());
399 DeploymentManager mgr = null;
400 TargetModuleID[] modules = null;
401 try {
402 mgr = getDeploymentManager();
403 Target[] targets = mgr.getTargets();
404 TargetModuleID[] ids = mgr.getAvailableModules(null, targets);
405 ids = (TargetModuleID[]) DeployUtils.identifyTargetModuleIDs(ids, configId, true).toArray(new TargetModuleID[0]);
406 ProgressObject po;
407 if (DeployUtils.isJarFile(file) || file.isDirectory()) {
408 po = mgr.redeploy(ids, file, null);
409 } else {
410 po = mgr.redeploy(ids, null, file);
411 }
412 waitForProgress(po);
413 if (po.getDeploymentStatus().isCompleted()) {
414 modules = po.getResultTargetModuleIDs();
415 for (int i = 0; i < modules.length; i++) {
416 TargetModuleID result = modules[i];
417 log.info(DeployUtils.reformat("Redeployed " + result.getModuleID() + (targets.length > 1 ? " to " + result.getTarget().getName() : "") + (result.getWebURL() == null ? "" : " @ " + result.getWebURL()), 4, 72));
418 if (result.getChildTargetModuleID() != null) {
419 for (int j = 0; j < result.getChildTargetModuleID().length; j++) {
420 TargetModuleID child = result.getChildTargetModuleID()[j];
421 log.info(DeployUtils.reformat(" `-> " + child.getModuleID() + (child.getWebURL() == null ? "" : " @ " + child.getWebURL()), 4, 72));
422 }
423 }
424 }
425 } else {
426 log.error("Unable to undeploy " + file.getAbsolutePath() + "(" + configId + ")" + po.getDeploymentStatus().getMessage());
427 }
428 } catch (DeploymentManagerCreationException e) {
429 log.error("Unable to open deployer", e);
430 } catch (Exception e) {
431 log.error("Unable to undeploy", e);
432 } finally {
433 if (mgr != null) mgr.release();
434 }
435 if (modules != null) {
436 if (modules.length == 1) {
437 return modules[0].getModuleID();
438 } else {
439 return "";
440 }
441 } else {
442 return null;
443 }
444 }
445
446 private void waitForProgress(ProgressObject po) {
447 while (po.getDeploymentStatus().isRunning()) {
448 try {
449 Thread.sleep(100);
450 } catch (InterruptedException e) {
451 log.error(e.getMessage(), e);
452 }
453 }
454 }
455
456 public static final GBeanInfo GBEAN_INFO;
457
458 static {
459 GBeanInfoBuilder infoFactory = GBeanInfoBuilder.createStatic(DirectoryHotDeployer.class);
460
461 infoFactory.addAttribute("path", String.class, true, true);
462 infoFactory.addAttribute("pollIntervalMillis", int.class, true, true);
463
464 // The next 3 args can be used to configure the hot deployer for a remote (out of VM) server
465 infoFactory.addAttribute("deploymentURI", String.class, true, true);
466 infoFactory.addAttribute("deploymentUser", String.class, true, true);
467 infoFactory.addAttribute("deploymentPassword", String.class, true, true);
468
469 infoFactory.addReference("ConfigManager", ConfigurationManager.class, "ConfigurationManager");
470 infoFactory.addReference("ServerInfo", ServerInfo.class, "GBean");
471 infoFactory.addAttribute("kernel", Kernel.class, false, false);
472 infoFactory.addInterface(HotDeployer.class);
473
474 infoFactory.setConstructor(new String[]{"path", "pollIntervalMillis", "ServerInfo", "ConfigManager", "kernel"});
475
476 GBEAN_INFO = infoFactory.getBeanInfo();
477 }
478
479 public static GBeanInfo getGBeanInfo() {
480 return GBEAN_INFO;
481 }
482 }