KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > geronimo > deployment > Deployer


1 /**
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements. See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License. You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */

17
18 package org.apache.geronimo.deployment;
19
20 import java.io.File JavaDoc;
21 import java.io.IOException JavaDoc;
22 import java.net.URI JavaDoc;
23 import java.util.ArrayList JavaDoc;
24 import java.util.Collection JavaDoc;
25 import java.util.Collections JavaDoc;
26 import java.util.Enumeration JavaDoc;
27 import java.util.Hashtable JavaDoc;
28 import java.util.Iterator JavaDoc;
29 import java.util.List JavaDoc;
30 import java.util.Properties JavaDoc;
31 import java.util.Set JavaDoc;
32 import java.util.jar.Attributes JavaDoc;
33 import java.util.jar.JarFile JavaDoc;
34 import java.util.jar.Manifest JavaDoc;
35
36 import javax.management.ObjectName JavaDoc;
37
38 import org.apache.commons.logging.Log;
39 import org.apache.commons.logging.LogFactory;
40 import org.apache.geronimo.common.DeploymentException;
41 import org.apache.geronimo.deployment.util.DeploymentUtil;
42 import org.apache.geronimo.gbean.AbstractName;
43 import org.apache.geronimo.gbean.AbstractNameQuery;
44 import org.apache.geronimo.gbean.GBeanInfo;
45 import org.apache.geronimo.gbean.GBeanInfoBuilder;
46 import org.apache.geronimo.kernel.GBeanNotFoundException;
47 import org.apache.geronimo.kernel.Kernel;
48 import org.apache.geronimo.kernel.config.Configuration;
49 import org.apache.geronimo.kernel.config.ConfigurationData;
50 import org.apache.geronimo.kernel.config.ConfigurationManager;
51 import org.apache.geronimo.kernel.config.ConfigurationStore;
52 import org.apache.geronimo.kernel.config.ConfigurationUtil;
53 import org.apache.geronimo.kernel.config.InvalidConfigException;
54 import org.apache.geronimo.kernel.config.DeploymentWatcher;
55 import org.apache.geronimo.kernel.repository.Artifact;
56 import org.apache.geronimo.kernel.repository.ArtifactResolver;
57 import org.apache.geronimo.system.configuration.ExecutableConfigurationUtil;
58 import org.apache.geronimo.system.main.CommandLineManifest;
59
60 /**
61  * GBean that knows how to deploy modules (by consulting available module builders)
62  *
63  * @version $Rev: 486195 $ $Date: 2006-12-12 10:42:02 -0500 (Tue, 12 Dec 2006) $
64  */

65 public class Deployer {
66     private static final Log log = LogFactory.getLog(Deployer.class);
67     private final int REAPER_INTERVAL = 60 * 1000;
68     private final Properties JavaDoc pendingDeletionIndex = new Properties JavaDoc();
69     private DeployerReaper reaper;
70     private final Collection JavaDoc builders;
71     private final Collection JavaDoc stores;
72     private final Collection JavaDoc watchers;
73     private final ArtifactResolver artifactResolver;
74     private final Kernel kernel;
75
76     public Deployer(Collection JavaDoc builders, Collection JavaDoc stores, Collection JavaDoc watchers, Kernel kernel) {
77         this(builders, stores, watchers, getArtifactResolver(kernel), kernel);
78     }
79
80     private static ArtifactResolver getArtifactResolver(Kernel kernel) {
81         ConfigurationManager configurationManager = ConfigurationUtil.getConfigurationManager(kernel);
82         return configurationManager.getArtifactResolver();
83     }
84
85     public Deployer(Collection JavaDoc builders, Collection JavaDoc stores, Collection JavaDoc watchers, ArtifactResolver artifactResolver, Kernel kernel) {
86         this.builders = builders;
87         this.stores = stores;
88         this.watchers = watchers;
89         this.artifactResolver = artifactResolver;
90         this.kernel = kernel;
91
92         // Create and start the reaper...
93
this.reaper = new DeployerReaper(REAPER_INTERVAL);
94         Thread JavaDoc t = new Thread JavaDoc(reaper, "Geronimo Config Store Reaper");
95         t.setDaemon(true);
96         t.start();
97     }
98
99     public List JavaDoc deploy(boolean inPlace, File JavaDoc moduleFile, File JavaDoc planFile) throws DeploymentException {
100         return deploy(inPlace, moduleFile, planFile, null);
101     }
102
103     public List JavaDoc deploy(boolean inPlace, File JavaDoc moduleFile, File JavaDoc planFile, String JavaDoc targetConfigStore) throws DeploymentException {
104         File JavaDoc originalModuleFile = moduleFile;
105         File JavaDoc tmpDir = null;
106         if (moduleFile != null && !moduleFile.isDirectory()) {
107             // todo jar url handling with Sun's VM on Windows leaves a lock on the module file preventing rebuilds
108
// to address this we use a gross hack and copy the file to a temporary directory
109
// the lock on the file will prevent that being deleted properly until the URLJarFile has
110
// been GC'ed.
111
try {
112                 tmpDir = File.createTempFile("geronimo-deployer", ".tmpdir");
113                 tmpDir.delete();
114                 tmpDir.mkdir();
115                 File JavaDoc tmpFile = new File JavaDoc(tmpDir, moduleFile.getName());
116                 DeploymentUtil.copyFile(moduleFile, tmpFile);
117                 moduleFile = tmpFile;
118             } catch (IOException JavaDoc e) {
119                 throw new DeploymentException(e);
120             }
121         }
122
123         try {
124             return deploy(inPlace, planFile, moduleFile, null, true, null, null, null, null, null, null, null, targetConfigStore);
125         } catch (DeploymentException e) {
126             log.debug("Deployment failed: plan=" + planFile + ", module=" + originalModuleFile, e);
127             throw e.cleanse();
128         } finally {
129             if (tmpDir != null) {
130                 if (!DeploymentUtil.recursiveDelete(tmpDir)) {
131                     pendingDeletionIndex.setProperty(tmpDir.getName(), "delete");
132                 }
133             }
134         }
135     }
136
137     /**
138      * Gets a URL that a remote deploy client can use to upload files to the
139      * server. Looks up a remote deploy web application by searching for a
140      * particular GBean and figuring out a reference to the web application
141      * based on that. Then constructs a URL pointing to that web application
142      * based on available connectors for the web container and the context
143      * root for the web application.
144      *
145      * @return The URL that clients should use for deployment file uploads.
146      */

147     public String JavaDoc getRemoteDeployUploadURL() {
148         // Get the token GBean from the remote deployment configuration
149
Set JavaDoc set = kernel.listGBeans(new AbstractNameQuery("org.apache.geronimo.deployment.remote.RemoteDeployToken"));
150         if (set.size() == 0) {
151             return null;
152         }
153         AbstractName token = (AbstractName) set.iterator().next();
154         // Identify the parent configuration for that GBean
155
set = kernel.getDependencyManager().getParents(token);
156         ObjectName JavaDoc config = null;
157         for (Iterator JavaDoc it = set.iterator(); it.hasNext();) {
158             AbstractName name = (AbstractName) it.next();
159             if (Configuration.isConfigurationObjectName(name.getObjectName())) {
160                 config = name.getObjectName();
161                 break;
162             }
163         }
164         if (config == null) {
165             log.warn("Unable to find remote deployment configuration; is the remote deploy web application running?");
166             return null;
167         }
168         // Generate the URL based on the remote deployment configuration
169
Hashtable JavaDoc hash = new Hashtable JavaDoc();
170         hash.put("J2EEApplication", token.getObjectName().getKeyProperty("J2EEApplication"));
171         hash.put("j2eeType", "WebModule");
172         try {
173             hash.put("name", Configuration.getConfigurationID(config).toString());
174             Set JavaDoc names = kernel.listGBeans(new AbstractNameQuery(null, hash));
175             if (names.size() != 1) {
176                 log.error("Unable to look up remote deploy upload URL");
177                 return null;
178             }
179             AbstractName module = (AbstractName) names.iterator().next();
180             return kernel.getAttribute(module, "URLFor") + "/upload";
181         } catch (Exception JavaDoc e) {
182             log.error("Unable to look up remote deploy upload URL", e);
183             return null;
184         }
185     }
186
187     public List JavaDoc deploy(boolean inPlace,
188             File JavaDoc planFile,
189             File JavaDoc moduleFile,
190             File JavaDoc targetFile,
191             boolean install,
192             String JavaDoc mainClass,
193             String JavaDoc mainGBean, String JavaDoc mainMethod, String JavaDoc manifestConfigurations, String JavaDoc classPath,
194             String JavaDoc endorsedDirs,
195             String JavaDoc extensionDirs,
196             String JavaDoc targetConfigurationStore) throws DeploymentException {
197         if (planFile == null && moduleFile == null) {
198             throw new DeploymentException("No plan or module specified");
199         }
200
201         if (planFile != null) {
202             if (!planFile.exists()) {
203                 throw new DeploymentException("Plan file does not exist: " + planFile.getAbsolutePath());
204             }
205             if (!planFile.isFile()) {
206                 throw new DeploymentException("Plan file is not a regular file: " + planFile.getAbsolutePath());
207             }
208         }
209
210         JarFile JavaDoc module = null;
211         if (moduleFile != null) {
212             if (inPlace && !moduleFile.isDirectory()) {
213                 throw new DeploymentException("In place deployment is not allowed for packed module");
214             }
215             if (!moduleFile.exists()) {
216                 throw new DeploymentException("Module file does not exist: " + moduleFile.getAbsolutePath());
217             }
218             try {
219                 module = DeploymentUtil.createJarFile(moduleFile);
220             } catch (IOException JavaDoc e) {
221                 throw new DeploymentException("Cound not open module file: " + moduleFile.getAbsolutePath(), e);
222             }
223         }
224
225 // File configurationDir = null;
226
ModuleIDBuilder idBuilder = new ModuleIDBuilder();
227         try {
228             Object JavaDoc plan = null;
229             ConfigurationBuilder builder = null;
230             for (Iterator JavaDoc i = builders.iterator(); i.hasNext();) {
231                 ConfigurationBuilder candidate = (ConfigurationBuilder) i.next();
232                 plan = candidate.getDeploymentPlan(planFile, module, idBuilder);
233                 if (plan != null) {
234                     builder = candidate;
235                     break;
236                 }
237             }
238             if (builder == null) {
239                 throw new DeploymentException("Cannot deploy the requested application module because no deployer is able to handle it. " +
240                         " This can happen if you have omitted the J2EE deployment descriptor, disabled a deployer module, or if, for example, you are trying to deploy an" +
241                         " EJB module on a minimal Geronimo server that does not have EJB support installed. (" +
242                         (planFile == null ? "" : "planFile=" + planFile.getAbsolutePath()) +
243                         (moduleFile == null ? "" : (planFile == null ? "" : ", ") + "moduleFile=" + moduleFile.getAbsolutePath()) + ")");
244             }
245
246             Artifact configID = builder.getConfigurationID(plan, module, idBuilder);
247             // If the Config ID isn't fully resolved, populate it with defaults
248
if (!configID.isResolved()) {
249                 configID = idBuilder.resolve(configID, "car");
250             }
251             // Make sure this configuration doesn't already exist
252
try {
253                 kernel.getGBeanState(Configuration.getConfigurationAbstractName(configID));
254                 throw new DeploymentException("Module " + configID + " already exists in the server. Try to undeploy it first or use the redeploy command.");
255             } catch (GBeanNotFoundException e) {
256                 // this is good
257
}
258
259             // create the manifest
260
Manifest JavaDoc manifest;
261             if (mainClass != null) {
262                 manifest = new Manifest JavaDoc();
263                 Attributes JavaDoc mainAttributes = manifest.getMainAttributes();
264                 mainAttributes.putValue(Attributes.Name.MANIFEST_VERSION.toString(), "1.0");
265                 if (mainClass != null) {
266                     mainAttributes.putValue(Attributes.Name.MAIN_CLASS.toString(), mainClass);
267                 }
268                 if (mainGBean != null) {
269                     mainAttributes.putValue(CommandLineManifest.MAIN_GBEAN.toString(), mainGBean);
270                 }
271                 if (mainMethod != null) {
272                     mainAttributes.putValue(CommandLineManifest.MAIN_METHOD.toString(), mainMethod);
273                 }
274                 if (manifestConfigurations != null) {
275                     mainAttributes.putValue(CommandLineManifest.CONFIGURATIONS.toString(), manifestConfigurations);
276                 }
277                 if (classPath != null) {
278                     mainAttributes.putValue(Attributes.Name.CLASS_PATH.toString(), classPath);
279                 }
280                 if (endorsedDirs != null) {
281                     mainAttributes.putValue(CommandLineManifest.ENDORSED_DIRS.toString(), endorsedDirs);
282                 }
283                 if (extensionDirs != null) {
284                     mainAttributes.putValue(CommandLineManifest.EXTENSION_DIRS.toString(), extensionDirs);
285                 }
286             } else {
287                 manifest = null;
288             }
289
290             if (stores.isEmpty()) {
291                 throw new DeploymentException("No ConfigurationStores!");
292             }
293             ConfigurationStore store;
294             if (targetConfigurationStore != null) {
295                 AbstractName targetStoreName = new AbstractName(new URI JavaDoc(targetConfigurationStore));
296                 store = (ConfigurationStore) kernel.getGBean(targetStoreName);
297             } else {
298                 store = (ConfigurationStore) stores.iterator().next();
299             }
300
301             // It's our responsibility to close this context, once we're done with it...
302
DeploymentContext context = builder.buildConfiguration(inPlace, configID, plan, module, stores, artifactResolver, store);
303             List JavaDoc configurations = new ArrayList JavaDoc();
304             boolean configsCleanupRequired = false;
305             configurations.add(context.getConfigurationData());
306             configurations.addAll(context.getAdditionalDeployment());
307
308             if (configurations.isEmpty()) {
309                 throw new DeploymentException("Deployer did not create any configurations");
310             }
311
312             // Set TCCL to the classloader for the configuration being deployed
313
// so that any static blocks invoked during the loading of classes
314
// during serialization of the configuration have the correct TCCL
315
// ( a TCCL that is consistent with what is set when the same
316
// classes are loaded when the configuration is started.
317
Thread JavaDoc thread = Thread.currentThread();
318             ClassLoader JavaDoc oldCl = thread.getContextClassLoader();
319             thread.setContextClassLoader( context.getConfiguration().getConfigurationClassLoader());
320             try {
321                 if (targetFile != null) {
322                     if (configurations.size() > 1) {
323                         throw new DeploymentException("Deployer created more than one configuration");
324                     }
325                     ConfigurationData configurationData = (ConfigurationData) configurations.get(0);
326                     ExecutableConfigurationUtil.createExecutableConfiguration(configurationData, manifest, targetFile);
327                 }
328                 if (install) {
329                     List JavaDoc deployedURIs = new ArrayList JavaDoc();
330                     for (Iterator JavaDoc iterator = configurations.iterator(); iterator.hasNext();) {
331                         ConfigurationData configurationData = (ConfigurationData) iterator.next();
332                         store.install(configurationData);
333                         deployedURIs.add(configurationData.getId().toString());
334                     }
335                     notifyWatchers(deployedURIs);
336                     return deployedURIs;
337                 } else {
338                     configsCleanupRequired = true;
339                     return Collections.EMPTY_LIST;
340                 }
341             } catch (DeploymentException e) {
342                 configsCleanupRequired = true;
343                 throw e;
344             } catch (IOException JavaDoc e) {
345                 configsCleanupRequired = true;
346                 throw e;
347             } catch (InvalidConfigException e) {
348                 configsCleanupRequired = true;
349                 // unlikely as we just built this
350
throw new DeploymentException(e);
351             } catch (Throwable JavaDoc e) {
352                 // Could get here if serialization of the configuration failed (GERONIMO-1996)
353
configsCleanupRequired = true;
354                 throw e;
355             } finally {
356                 thread.setContextClassLoader(oldCl);
357                 if (context != null) {
358                     context.close();
359                 }
360                 if (configsCleanupRequired) {
361                     // We do this after context is closed so the module jar isn't open
362
cleanupConfigurations(configurations);
363                 }
364             }
365         } catch (Throwable JavaDoc e) {
366             //TODO not clear all errors will result in total cleanup
367
// File configurationDir = configurationData.getConfigurationDir();
368
// if (!DeploymentUtil.recursiveDelete(configurationDir)) {
369
// pendingDeletionIndex.setProperty(configurationDir.getName(), new String("delete"));
370
// log.debug("Queued deployment directory to be reaped " + configurationDir);
371
// }
372
// if (targetFile != null) {
373
// targetFile.delete();
374
// }
375

376             if (e instanceof Error JavaDoc) {
377                 log.error("Deployment failed due to ", e);
378                 throw (Error JavaDoc) e;
379             } else if (e instanceof DeploymentException) {
380                 throw (DeploymentException) e;
381             } else if (e instanceof Exception JavaDoc) {
382                 log.error("Deployment failed due to ", e);
383                 throw new DeploymentException(e);
384             }
385             throw new Error JavaDoc(e);
386         } finally {
387             DeploymentUtil.close(module);
388         }
389     }
390
391     private void notifyWatchers(List JavaDoc list) {
392         Artifact[] arts = new Artifact[list.size()];
393         for (int i = 0; i < list.size(); i++) {
394             String JavaDoc s = (String JavaDoc) list.get(i);
395             arts[i] = Artifact.create(s);
396         }
397         for (Iterator JavaDoc it = watchers.iterator(); it.hasNext();) {
398             DeploymentWatcher watcher = (DeploymentWatcher) it.next();
399             for (int i = 0; i < arts.length; i++) {
400                 Artifact art = arts[i];
401                 watcher.deployed(art);
402             }
403         }
404     }
405
406     private void cleanupConfigurations(List JavaDoc configurations) {
407         for (Iterator JavaDoc iterator = configurations.iterator(); iterator.hasNext();) {
408             ConfigurationData configurationData = (ConfigurationData) iterator.next();
409             File JavaDoc configurationDir = configurationData.getConfigurationDir();
410             if (!DeploymentUtil.recursiveDelete(configurationDir)) {
411                 pendingDeletionIndex.setProperty(configurationDir.getName(), "delete");
412                 log.debug("Queued deployment directory to be reaped " + configurationDir);
413             }
414         }
415     }
416
417     /**
418      * Thread to cleanup unused temporary Deployer directories (and files).
419      * On Windows, open files can't be deleted. Until MultiParentClassLoaders
420      * are GC'ed, we won't be able to delete Config Store directories/files.
421      */

422     class DeployerReaper implements Runnable JavaDoc {
423         private final int reaperInterval;
424         private volatile boolean done = false;
425
426         public DeployerReaper(int reaperInterval) {
427             this.reaperInterval = reaperInterval;
428         }
429
430         public void close() {
431             this.done = true;
432         }
433
434         public void run() {
435             log.debug("ConfigStoreReaper started");
436             while (!done) {
437                 try {
438                     Thread.sleep(reaperInterval);
439                 } catch (InterruptedException JavaDoc e) {
440                     continue;
441                 }
442                 reap();
443             }
444         }
445
446         /**
447          * For every directory in the pendingDeletionIndex, attempt to delete all
448          * sub-directories and files.
449          */

450         public void reap() {
451             // return, if there's nothing to do
452
if (pendingDeletionIndex.size() == 0)
453                 return;
454             // Otherwise, attempt to delete all of the directories
455
Enumeration JavaDoc list = pendingDeletionIndex.propertyNames();
456             while (list.hasMoreElements()) {
457                 String JavaDoc dirName = (String JavaDoc) list.nextElement();
458                 File JavaDoc deleteDir = new File JavaDoc(dirName);
459
460                 if (!DeploymentUtil.recursiveDelete(deleteDir)) {
461                     pendingDeletionIndex.remove(deleteDir);
462                     log.debug("Reaped deployment directory " + deleteDir);
463                 }
464             }
465         }
466     }
467
468     public static final GBeanInfo GBEAN_INFO;
469
470     private static final String JavaDoc DEPLOYER = "Deployer";
471
472     static {
473         GBeanInfoBuilder infoFactory = GBeanInfoBuilder.createStatic(Deployer.class, DEPLOYER);
474
475         infoFactory.addAttribute("kernel", Kernel.class, false);
476         infoFactory.addAttribute("remoteDeployUploadURL", String JavaDoc.class, false);
477         infoFactory.addOperation("deploy", new Class JavaDoc[]{boolean.class, File JavaDoc.class, File JavaDoc.class});
478         infoFactory.addOperation("deploy", new Class JavaDoc[]{boolean.class, File JavaDoc.class, File JavaDoc.class, String JavaDoc.class});
479         infoFactory.addOperation("deploy", new Class JavaDoc[]{boolean.class, File JavaDoc.class, File JavaDoc.class, File JavaDoc.class, boolean.class, String JavaDoc.class, String JavaDoc.class, String JavaDoc.class, String JavaDoc.class, String JavaDoc.class, String JavaDoc.class, String JavaDoc.class, String JavaDoc.class});
480
481         infoFactory.addReference("Builders", ConfigurationBuilder.class, "ConfigBuilder");
482         infoFactory.addReference("Store", ConfigurationStore.class, "ConfigurationStore");
483         infoFactory.addReference("Watchers", DeploymentWatcher.class);
484
485         infoFactory.setConstructor(new String JavaDoc[]{"Builders", "Store", "Watchers", "kernel"});
486
487         GBEAN_INFO = infoFactory.getBeanInfo();
488     }
489
490     public static GBeanInfo getGBeanInfo() {
491         return GBEAN_INFO;
492     }
493 }
494
Popular Tags