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: */package org.apache.geronimo.deployment.hot;
017:
018: import java.io.File;
019: import java.util.Iterator;
020: import java.util.Set;
021:
022: import javax.enterprise.deploy.spi.DeploymentManager;
023: import javax.enterprise.deploy.spi.Target;
024: import javax.enterprise.deploy.spi.TargetModuleID;
025: import javax.enterprise.deploy.spi.exceptions.DeploymentManagerCreationException;
026: import javax.enterprise.deploy.spi.factories.DeploymentFactory;
027: import javax.enterprise.deploy.spi.status.ProgressObject;
028:
029: import org.apache.commons.logging.Log;
030: import org.apache.commons.logging.LogFactory;
031: import org.apache.geronimo.common.DeploymentException;
032: import org.apache.geronimo.deployment.cli.DeployUtils;
033: import org.apache.geronimo.deployment.plugin.factories.DeploymentFactoryWithKernel;
034: import org.apache.geronimo.deployment.plugin.jmx.JMXDeploymentManager;
035: import org.apache.geronimo.deployment.util.DeploymentUtil;
036: import org.apache.geronimo.gbean.AbstractName;
037: import org.apache.geronimo.gbean.AbstractNameQuery;
038: import org.apache.geronimo.gbean.GBeanInfo;
039: import org.apache.geronimo.gbean.GBeanInfoBuilder;
040: import org.apache.geronimo.gbean.GBeanLifecycle;
041: import org.apache.geronimo.kernel.Kernel;
042: import org.apache.geronimo.kernel.config.Configuration;
043: import org.apache.geronimo.kernel.config.ConfigurationManager;
044: import org.apache.geronimo.kernel.config.DeploymentWatcher;
045: import org.apache.geronimo.kernel.config.PersistentConfigurationList;
046: import org.apache.geronimo.kernel.repository.Artifact;
047: import org.apache.geronimo.kernel.repository.MissingDependencyException;
048: import org.apache.geronimo.system.serverinfo.ServerInfo;
049:
050: /**
051: * A directory-scanning hot deployer
052: *
053: * @version $Rev: 596987 $ $Date: 2007-11-21 00:39:04 -0800 (Wed, 21 Nov 2007) $
054: */
055: public class DirectoryHotDeployer implements HotDeployer,
056: DeploymentWatcher, GBeanLifecycle { //todo: write unit tests
057: private static final Log log = LogFactory
058: .getLog(DirectoryHotDeployer.class);
059:
060: // Try to make this stand out as the user is likely to get a ton of errors if this comes up
061: private static final String BAD_LAYOUT_MESSAGE = "CANNOT DEPLOY: It looks like you unpacked an application or module "
062: + "directly into the hot deployment directory. THIS DOES NOT WORK. You need to unpack into a "
063: + "subdirectory directly under the hot deploy directory. For example, if the hot deploy directory "
064: + "is 'deploy/' and your file is 'webapp.war' then you could unpack it into a directory 'deploy/webapp.war/'";
065: private DirectoryMonitor monitor;
066: private String path;
067: private ServerInfo serverInfo;
068: private ConfigurationManager configManager;
069: private int pollIntervalMillis;
070: private String deploymentURI = "deployer:geronimo:inVM";
071: private String deploymentUser;
072: private String deploymentPassword;
073: private transient Kernel kernel;
074: private transient DeploymentFactory factory;
075: private transient TargetModuleID[] startupModules = null;
076: private transient boolean serverRunning = false;
077:
078: public DirectoryHotDeployer(String path, int pollIntervalMillis,
079: ServerInfo serverInfo, ConfigurationManager configManager,
080: Kernel kernel) {
081: this .path = path;
082: this .serverInfo = serverInfo;
083: this .pollIntervalMillis = pollIntervalMillis;
084: this .kernel = kernel;
085: this .configManager = configManager;
086: }
087:
088: public void deployed(Artifact id) {
089: // no action when something is deployed
090: }
091:
092: public void undeployed(Artifact id) {
093: // check to see whether the artifact was hot deployed, and if so, delete it
094: monitor.removeModuleId(id);
095: }
096:
097: public String getPath() {
098: return path;
099: }
100:
101: public void setPath(String path) {
102: this .path = path;
103: }
104:
105: public ServerInfo getServerInfo() {
106: return serverInfo;
107: }
108:
109: public void setServerInfo(ServerInfo serverInfo) {
110: this .serverInfo = serverInfo;
111: }
112:
113: public int getPollIntervalMillis() {
114: return pollIntervalMillis;
115: }
116:
117: public void setPollIntervalMillis(int pollIntervalMillis) {
118: this .pollIntervalMillis = pollIntervalMillis;
119: }
120:
121: public String getDeploymentURI() {
122: return deploymentURI;
123: }
124:
125: public void setDeploymentURI(String deploymentURI) {
126: if (deploymentURI != null && !deploymentURI.trim().equals("")) {
127: this .deploymentURI = deploymentURI.trim();
128: }
129: }
130:
131: public String getDeploymentUser() {
132: return deploymentUser;
133: }
134:
135: public void setDeploymentUser(String deploymentUser) {
136: this .deploymentUser = deploymentUser;
137: }
138:
139: public String getDeploymentPassword() {
140: return deploymentPassword;
141: }
142:
143: public void setDeploymentPassword(String deploymentPassword) {
144: this .deploymentPassword = deploymentPassword;
145: }
146:
147: public void doStart() throws Exception {
148: if (factory == null) {
149: factory = new DeploymentFactoryWithKernel(kernel);
150: }
151: File dir = serverInfo.resolveServer(path);
152: if (!dir.exists()) {
153: if (!dir.mkdirs()) {
154: throw new IllegalStateException("Hot deploy directory "
155: + dir.getAbsolutePath()
156: + " does not exist and cannot be created!");
157: }
158: } else if (!dir.canRead() || !dir.isDirectory()) {
159: throw new IllegalStateException("Hot deploy directory "
160: + dir.getAbsolutePath()
161: + " is not a readable directory!");
162: }
163: DeploymentManager mgr = null;
164: try {
165: mgr = getDeploymentManager();
166: Target[] targets = mgr.getTargets();
167: startupModules = mgr.getAvailableModules(null, targets);
168: mgr.release();
169: mgr = null;
170: monitor = new DirectoryMonitor(dir, this ,
171: pollIntervalMillis);
172: log
173: .debug("Hot deploy scanner intialized; starting main loop.");
174: Thread t = new Thread(monitor,
175: "Geronimo hot deploy scanner");
176: t.setDaemon(true);
177: t.start();
178: } finally {
179: if (mgr != null)
180: mgr.release();
181: }
182: }
183:
184: public void doStop() throws Exception {
185: monitor.close();
186: }
187:
188: public void doFail() {
189: if (monitor != null) {
190: monitor.close();
191: }
192: }
193:
194: public boolean isFileDeployed(File file, String configId) {
195: DeploymentManager mgr = null;
196: try {
197: if (startupModules != null) {
198: DeployUtils.identifyTargetModuleIDs(startupModules,
199: configId, true).toArray(new TargetModuleID[0]);
200: } else {
201: mgr = getDeploymentManager();
202: Target[] targets = mgr.getTargets();
203: TargetModuleID[] ids = mgr.getAvailableModules(null,
204: targets);
205: DeployUtils
206: .identifyTargetModuleIDs(ids, configId, true)
207: .toArray(new TargetModuleID[0]);
208: mgr.release();
209: mgr = null;
210: }
211: return true;
212: } catch (DeploymentException e) {
213: log
214: .debug("Found new file in deploy directory on startup with ID "
215: + configId);
216: } catch (Exception e) {
217: log.error("Unable to check status", e);
218: } finally {
219: if (mgr != null) {
220: mgr.release();
221: mgr = null;
222: }
223: }
224: return false;
225: }
226:
227: public boolean isServerRunning() {
228: if (serverRunning) {
229: return true;
230: }
231:
232: // a bit of a hack, but the PersistentConfigurationList is the only thing that knows whether the server is full started!
233: Set configLists = kernel.listGBeans(new AbstractNameQuery(
234: PersistentConfigurationList.class.getName()));
235: for (Iterator i = configLists.iterator(); i.hasNext();) {
236: AbstractName configListName = (AbstractName) i.next();
237: try {
238: Boolean result = (Boolean) kernel.getAttribute(
239: configListName, "kernelFullyStarted");
240: if (!result.booleanValue()) {
241: return false;
242: }
243: } catch (Exception e) {
244: log
245: .warn(
246: "Hot deployer unable to determine whether kernel is started",
247: e);
248: }
249: }
250: serverRunning = true;
251: return true;
252: }
253:
254: public long getDeploymentTime(File file, String configId) {
255: try {
256: Artifact art = configManager.getArtifactResolver()
257: .resolveInClassLoader(Artifact.create(configId));
258: Configuration config = configManager.getConfiguration(art);
259: return config.getCreated();
260: } catch (MissingDependencyException e) {
261: log.error("Unknown configuration " + configId);
262: return -1;
263: }
264: }
265:
266: public void started() {
267: startupModules = null;
268: log
269: .debug("Initialization complete; directory scanner entering normal scan mode");
270: }
271:
272: public boolean validateFile(File file, String configId) {
273: //todo: some more detailed evaluation
274: if (file.isDirectory()
275: && (file.getName().equals("WEB-INF") || file.getName()
276: .equals("META-INF"))) {
277: log.error("(" + file.getName() + ") " + BAD_LAYOUT_MESSAGE);
278: return false;
279: }
280: return true;
281: }
282:
283: public String fileAdded(File file) {
284: log.info("Deploying " + file.getName());
285: DeploymentManager mgr = null;
286: TargetModuleID[] modules = null;
287: boolean completed = false;
288: try {
289: mgr = getDeploymentManager();
290: Target[] targets = mgr.getTargets();
291: if (null == targets) {
292: throw new IllegalStateException(
293: "No target to distribute to");
294: }
295: targets = new Target[] { targets[0] };
296:
297: ProgressObject po;
298: if (DeployUtils.isJarFile(file) || file.isDirectory()) {
299: po = mgr.distribute(targets, file, null);
300: } else {
301: po = mgr.distribute(targets, null, file);
302: }
303: waitForProgress(po);
304: if (po.getDeploymentStatus().isCompleted()) {
305: modules = po.getResultTargetModuleIDs();
306: po = mgr.start(modules);
307: waitForProgress(po);
308: if (po.getDeploymentStatus().isCompleted()) {
309: completed = true;
310: } else {
311: log.warn("Unable to start some modules for "
312: + file.getAbsolutePath());
313: }
314: modules = po.getResultTargetModuleIDs();
315: for (int i = 0; i < modules.length; i++) {
316: TargetModuleID result = modules[i];
317: log
318: .info(DeployUtils
319: .reformat(
320: "Deployed "
321: + result
322: .getModuleID()
323: + (targets.length > 1 ? " to "
324: + result
325: .getTarget()
326: .getName()
327: : "")
328: + (result
329: .getWebURL() == null ? ""
330: : " @ "
331: + result
332: .getWebURL()),
333: 4, 72));
334: if (result.getChildTargetModuleID() != null) {
335: for (int j = 0; j < result
336: .getChildTargetModuleID().length; j++) {
337: TargetModuleID child = result
338: .getChildTargetModuleID()[j];
339: log
340: .info(DeployUtils
341: .reformat(
342: " `-> "
343: + child
344: .getModuleID()
345: + (child
346: .getWebURL() == null ? ""
347: : " @ "
348: + child
349: .getWebURL()),
350: 4, 72));
351: }
352: }
353: }
354: } else {
355: //Try to delete the module , that failed to successfully hot-deploy
356: log.error("Unable to deploy: "
357: + po.getDeploymentStatus().getMessage());
358: String delfile = file.getAbsolutePath();
359: File fd = new File(delfile);
360: if (fd.isDirectory()) {
361: log.info("Deleting the Directory: " + delfile);
362: if (DeploymentUtil.recursiveDelete(fd))
363: log
364: .debug("Successfully deleted the Directory: "
365: + delfile);
366: else
367: log
368: .error("Couldn't delete the hot deployed directory"
369: + delfile);
370: } else if (fd.isFile()) {
371: log.info("Deleting the File: " + delfile);
372: if (fd.delete()) {
373: log.debug("Successfully deleted the File: "
374: + delfile);
375: } else
376: log
377: .error("Couldn't delete the hot deployed directory"
378: + delfile);
379: }
380:
381: return null;
382: }
383: } catch (DeploymentManagerCreationException e) {
384: log.error("Unable to open deployer", e);
385: return null;
386: } catch (DeploymentException e) {
387: log.error("Unable to determine if file is a jar", e);
388: } finally {
389: if (mgr != null)
390: mgr.release();
391: }
392: if (completed && modules != null) {
393: if (modules.length == 1) {
394: return modules[0].getModuleID();
395: } else {
396: return "";
397: }
398: } else if (modules != null) { //distribute completed but not start or something like that
399: return "";
400: } else {
401: return null;
402: }
403: }
404:
405: private DeploymentManager getDeploymentManager()
406: throws DeploymentManagerCreationException {
407: DeploymentManager manager = factory.getDeploymentManager(
408: deploymentURI, deploymentUser, deploymentPassword);
409: if (manager instanceof JMXDeploymentManager) {
410: ((JMXDeploymentManager) manager).setLogConfiguration(false,
411: true);
412: }
413: return manager;
414: }
415:
416: public boolean fileRemoved(File file, String configId) {
417: log.info("Undeploying " + file.getName());
418: DeploymentManager mgr = null;
419: try {
420: mgr = getDeploymentManager();
421: Target[] targets = mgr.getTargets();
422: TargetModuleID[] ids = mgr.getAvailableModules(null,
423: targets);
424: ids = (TargetModuleID[]) DeployUtils
425: .identifyTargetModuleIDs(ids, configId, true)
426: .toArray(new TargetModuleID[0]);
427: ProgressObject po = mgr.undeploy(ids);
428: waitForProgress(po);
429: if (po.getDeploymentStatus().isCompleted()) {
430: TargetModuleID[] modules = po
431: .getResultTargetModuleIDs();
432: for (int i = 0; i < modules.length; i++) {
433: TargetModuleID result = modules[i];
434: log.info(DeployUtils.reformat(
435: "Undeployed "
436: + result.getModuleID()
437: + (targets.length > 1 ? " to "
438: + result.getTarget()
439: .getName() : ""),
440: 4, 72));
441: }
442: } else {
443: log.error("Unable to undeploy "
444: + file.getAbsolutePath() + "(" + configId + ")"
445: + po.getDeploymentStatus().getMessage());
446: return false;
447: }
448: } catch (DeploymentManagerCreationException e) {
449: log.error("Unable to open deployer", e);
450: return false;
451: } catch (Exception e) {
452: log.error("Unable to undeploy", e);
453: return false;
454: } finally {
455: if (mgr != null)
456: mgr.release();
457: }
458: return true;
459: }
460:
461: public String getModuleId(String config) {
462: DeploymentManager mgr = null;
463: TargetModuleID[] modules = null;
464: try {
465: mgr = getDeploymentManager();
466: Target[] targets = mgr.getTargets();
467: TargetModuleID[] ids = mgr.getAvailableModules(null,
468: targets);
469: for (int j = 0; j < ids.length; j++) {
470: String moduleId = ids[j].getModuleID();
471: String[] parts = moduleId.split("/", -1);
472: if (parts.length != 4) {
473: continue;
474: }
475: if (parts[1] != null && parts[1].equals(config))
476: return ids[j].getModuleID();
477: }
478: } catch (Exception ex) {
479: log.error("Unable to getModuleId", ex);
480: }
481: return config;
482: }
483:
484: public String fileUpdated(File file, String configId) {
485: log.info("Redeploying " + file.getName());
486: DeploymentManager mgr = null;
487: TargetModuleID[] modules = null;
488: try {
489: mgr = getDeploymentManager();
490: Target[] targets = mgr.getTargets();
491: TargetModuleID[] ids = mgr.getAvailableModules(null,
492: targets);
493: ids = (TargetModuleID[]) DeployUtils
494: .identifyTargetModuleIDs(ids, configId, true)
495: .toArray(new TargetModuleID[0]);
496: ProgressObject po;
497: if (DeployUtils.isJarFile(file) || file.isDirectory()) {
498: po = mgr.redeploy(ids, file, null);
499: } else {
500: po = mgr.redeploy(ids, null, file);
501: }
502: waitForProgress(po);
503: if (po.getDeploymentStatus().isCompleted()) {
504: modules = po.getResultTargetModuleIDs();
505: for (int i = 0; i < modules.length; i++) {
506: TargetModuleID result = modules[i];
507: log
508: .info(DeployUtils
509: .reformat(
510: "Redeployed "
511: + result
512: .getModuleID()
513: + (targets.length > 1 ? " to "
514: + result
515: .getTarget()
516: .getName()
517: : "")
518: + (result
519: .getWebURL() == null ? ""
520: : " @ "
521: + result
522: .getWebURL()),
523: 4, 72));
524: if (result.getChildTargetModuleID() != null) {
525: for (int j = 0; j < result
526: .getChildTargetModuleID().length; j++) {
527: TargetModuleID child = result
528: .getChildTargetModuleID()[j];
529: log
530: .info(DeployUtils
531: .reformat(
532: " `-> "
533: + child
534: .getModuleID()
535: + (child
536: .getWebURL() == null ? ""
537: : " @ "
538: + child
539: .getWebURL()),
540: 4, 72));
541: }
542: }
543: }
544: } else {
545: log.error("Unable to undeploy "
546: + file.getAbsolutePath() + "(" + configId + ")"
547: + po.getDeploymentStatus().getMessage());
548: }
549: } catch (DeploymentManagerCreationException e) {
550: log.error("Unable to open deployer", e);
551: } catch (Exception e) {
552: log.error("Unable to undeploy", e);
553: } finally {
554: if (mgr != null)
555: mgr.release();
556: }
557: if (modules != null) {
558: if (modules.length == 1) {
559: return modules[0].getModuleID();
560: } else {
561: return "";
562: }
563: } else {
564: return null;
565: }
566: }
567:
568: private void waitForProgress(ProgressObject po) {
569: while (po.getDeploymentStatus().isRunning()) {
570: try {
571: Thread.sleep(100);
572: } catch (InterruptedException e) {
573: log.error(e.getMessage(), e);
574: }
575: }
576: }
577:
578: public static final GBeanInfo GBEAN_INFO;
579:
580: static {
581: GBeanInfoBuilder infoFactory = GBeanInfoBuilder
582: .createStatic(DirectoryHotDeployer.class);
583:
584: infoFactory.addAttribute("path", String.class, true, true);
585: infoFactory.addAttribute("pollIntervalMillis", int.class, true,
586: true);
587:
588: // The next 3 args can be used to configure the hot deployer for a remote (out of VM) server
589: infoFactory.addAttribute("deploymentURI", String.class, true,
590: true);
591: infoFactory.addAttribute("deploymentUser", String.class, true,
592: true);
593: infoFactory.addAttribute("deploymentPassword", String.class,
594: true, true);
595:
596: infoFactory.addReference("ConfigManager",
597: ConfigurationManager.class, "ConfigurationManager");
598: infoFactory.addReference("ServerInfo", ServerInfo.class,
599: "GBean");
600: infoFactory.addAttribute("kernel", Kernel.class, false, false);
601: infoFactory.addInterface(HotDeployer.class);
602:
603: infoFactory.setConstructor(new String[] { "path",
604: "pollIntervalMillis", "ServerInfo", "ConfigManager",
605: "kernel" });
606:
607: GBEAN_INFO = infoFactory.getBeanInfo();
608: }
609:
610: public static GBeanInfo getGBeanInfo() {
611: return GBEAN_INFO;
612: }
613: }
|