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 }