View Javadoc

1   /**
2    *
3    * Copyright 2005 The Apache Software Foundation
4    *
5    *  Licensed under the Apache License, Version 2.0 (the "License");
6    *  you may not use this file except in compliance with the License.
7    *  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  package org.apache.geronimo.deployment;
18  
19  import java.io.File;
20  import java.io.IOException;
21  import java.util.ArrayList;
22  import java.util.Arrays;
23  import java.util.Collection;
24  import java.util.Iterator;
25  import java.util.List;
26  import java.util.LinkedList;
27  import java.util.jar.JarFile;
28  
29  import org.apache.commons.logging.Log;
30  import org.apache.commons.logging.LogFactory;
31  import org.apache.geronimo.common.DeploymentException;
32  import org.apache.geronimo.deployment.util.DeploymentUtil;
33  import org.apache.geronimo.kernel.config.ConfigurationData;
34  import org.apache.geronimo.kernel.config.ConfigurationInfo;
35  import org.apache.geronimo.kernel.config.ConfigurationManager;
36  import org.apache.geronimo.kernel.config.ConfigurationStore;
37  import org.apache.geronimo.kernel.config.InvalidConfigException;
38  import org.apache.geronimo.kernel.config.NoSuchConfigException;
39  import org.apache.geronimo.kernel.repository.Artifact;
40  import org.apache.geronimo.kernel.repository.ArtifactResolver;
41  import org.apache.geronimo.kernel.repository.Version;
42  import org.apache.geronimo.system.serverinfo.ServerInfo;
43  import org.apache.geronimo.gbean.GBeanInfo;
44  import org.apache.geronimo.gbean.GBeanInfoBuilder;
45  
46  /**
47   * @version $Rev: 410741 $ $Date: 2006-05-31 21:35:48 -0700 (Wed, 31 May 2006) $
48   */
49  public class SingleFileHotDeployer {
50      private static final Log log = LogFactory.getLog(SingleFileHotDeployer.class);
51      private static final String LINE_SEP = System.getProperty("line.separator");
52      private final File dir;
53      private final String[] watchPaths;
54      private final Collection builders;
55      private final ConfigurationStore store;
56      private final ConfigurationManager configurationManager;
57      private final boolean forceDeploy;
58      private final Artifact configurationId;
59      private boolean wasDeployed;
60  
61      public SingleFileHotDeployer(String path, ServerInfo serverInfo, String[] watchPaths, Collection builders, ConfigurationStore store, ConfigurationManager configurationManager, boolean forceDeploy) throws DeploymentException {
62          this(serverInfo.resolve(path), watchPaths, builders, store, configurationManager, forceDeploy);
63      }
64  
65      public SingleFileHotDeployer(File dir, String[] watchPaths, Collection builders, ConfigurationStore store, ConfigurationManager configurationManager, boolean forceDeploy) throws DeploymentException {
66          this.dir = dir;
67          this.watchPaths = watchPaths;
68          this.builders = builders;
69          this.store = store;
70          this.configurationManager = configurationManager;
71          this.forceDeploy = forceDeploy;
72  
73          configurationId = start(dir);
74      }
75  
76      private Artifact start(File dir) throws DeploymentException {
77          if (!dir.exists()) {
78              throw new IllegalArgumentException("Directory does not exist " + dir.getAbsolutePath());
79          }
80          if (!dir.isDirectory()) {
81              throw new IllegalArgumentException("Directory is not a directory " + dir.getAbsolutePath());
82          }
83  
84          // take no action if there is nothing in the directory to deploy.   Perhaps we should
85          // consider doing an undeploy in this case if the application is already deployed. Howevr 
86          // for now this is to handle the case where the application is not already laid down at the 
87          // time of the initial deploy of this gbean.
88          if (dir.list().length == 0) {
89              return null;
90          }
91  
92          // get the existing inplace configuration if there is one
93          ConfigurationInfo existingConfiguration = null;
94          List list = configurationManager.listConfigurations();
95          for (Iterator iterator = list.iterator(); iterator.hasNext();) {
96              ConfigurationInfo configurationInfo = (ConfigurationInfo) iterator.next();
97              if (dir.equals(configurationInfo.getInPlaceLocation())) {
98                  existingConfiguration = configurationInfo;
99              }
100         }
101         Artifact existingConfigurationId = (existingConfiguration == null) ? null : existingConfiguration.getConfigID();
102 
103         if (!forceDeploy && existingConfiguration != null && !isModifiedSince(existingConfiguration.getCreated())) {
104             try {
105                 configurationManager.loadConfiguration(existingConfigurationId);
106                 configurationManager.startConfiguration(existingConfigurationId);
107             } catch (Exception e) {
108                 throw new DeploymentException("Unable to load and start " + dir, e);
109             }
110             return existingConfigurationId;
111         }
112 
113         // if the current id and the new id only differ by version, we can reload, otherwise we need to load and start
114         if (existingConfigurationId != null && configurationManager.isLoaded(existingConfigurationId)) {
115             try {
116                 configurationManager.unloadConfiguration(existingConfigurationId);
117             } catch (NoSuchConfigException e) {
118                 throw new DeploymentException("Unable to unload existing configuration " + existingConfigurationId);
119             }
120         }
121 
122         ModuleIDBuilder idBuilder = new ModuleIDBuilder();
123 
124         JarFile module = null;
125         try {
126             module = DeploymentUtil.createJarFile(dir);
127         } catch (IOException e) {
128             throw new DeploymentException("Cound not open module file: " + dir.getAbsolutePath(), e);
129         }
130 
131         try {
132             // get the builder and plan
133             Object plan = null;
134             ConfigurationBuilder builder = null;
135             for (Iterator i = builders.iterator(); i.hasNext();) {
136                 ConfigurationBuilder candidate = (ConfigurationBuilder) i.next();
137                 plan = candidate.getDeploymentPlan(null, module, idBuilder);
138                 if (plan != null) {
139                     builder = candidate;
140                     break;
141                 }
142             }
143             if (builder == null) {
144                 throw new DeploymentException("Cannot deploy the requested application module because no builder is able to handle it (dir=" + dir.getAbsolutePath() + ")");
145             }
146 
147             // determine the new configuration id
148             Artifact configurationId = builder.getConfigurationID(plan, module, idBuilder);
149 
150             // if the new configuration id isn't fully resolved, populate it with defaults
151             if (!configurationId.isResolved()) {
152                 configurationId = resolve(configurationId);
153             }
154 
155             // If we didn't find a previous configuration based upon the path then check one more time to
156             // see if one exists by the same configurationID.   This will catch situations where the target
157             // path was renamed or moved such that the path associated with the previous configuration no longer
158             // matches the patch associated with the new configuration.
159             if ((existingConfigurationId == null) && configurationManager.isInstalled(configurationId)) {
160                 log.info("Existing Module found by moduleId");
161                 existingConfigurationId = configurationId;
162             }
163 
164             // if we are deploying over the exisitng version we need to uninstall first
165             if(configurationId.equals(existingConfigurationId)) {
166                 log.info("Undeploying " + existingConfigurationId);
167                 configurationManager.uninstallConfiguration(existingConfigurationId);
168             }
169 
170             // deploy it
171             deployConfiguration(builder, store, configurationId, plan, module, Arrays.asList(configurationManager.getStores()), configurationManager.getArtifactResolver());
172             wasDeployed = true;
173 
174             configurationManager.loadConfiguration(configurationId);
175             configurationManager.startConfiguration(configurationId);
176 
177             log.info("Successfully deployed and started " + configurationId + " in location " + dir);
178 
179             return configurationId;
180         } catch (Exception e) {
181             throw new DeploymentException("Unable to deploy " + dir, e);
182         } finally {
183             DeploymentUtil.close(module);
184         }
185 
186     }
187 
188     private boolean isModifiedSince(long created) {
189         for (int i = 0; i < watchPaths.length; i++) {
190             String path = watchPaths[i];
191             File file = new File(dir, path);
192             if (!file.exists()) {
193                 log.warn("Watched file does not exist " + file);
194             }
195             if (file.isFile() && file.lastModified() > created) {
196                 log.info("Redeploying " + dir + " because file " + file + " was modified;");
197                 return true;
198             }
199         }
200         return false;
201     }
202 
203     private Artifact resolve(Artifact configID) throws DeploymentException {
204         String group = configID.getGroupId();
205         if (group == null) {
206             group = Artifact.DEFAULT_GROUP_ID;
207         }
208         String artifactId = configID.getArtifactId();
209         if (artifactId == null) {
210             throw new DeploymentException("Every configuration to deploy must have a ConfigID with an ArtifactID (not " + configID + ")");
211         }
212         Version version = configID.getVersion();
213         if (version == null) {
214             version = new Version(Long.toString(System.currentTimeMillis()));
215         }
216         String type = configID.getType();
217         if (type == null) {
218             type = "car";
219         }
220         return new Artifact(group, artifactId, version, type);
221     }
222 
223     private List deployConfiguration(ConfigurationBuilder builder, ConfigurationStore store, Artifact configurationId, Object plan, JarFile module, Collection stores, ArtifactResolver artifactResolver) throws DeploymentException {
224         try {
225             // It's our responsibility to close this context, once we're done with it...
226             DeploymentContext context = builder.buildConfiguration(true, configurationId, plan, module, stores, artifactResolver, store);
227 
228             List configurations = new ArrayList();
229             try {
230                 configurations.add(context.getConfigurationData());
231                 configurations.addAll(context.getAdditionalDeployment());
232 
233                 if (configurations.isEmpty()) {
234                     throw new DeploymentException("Deployer did not create any configurations");
235                 }
236                 List deployedURIs = new ArrayList();
237                 for (Iterator iterator = configurations.iterator(); iterator.hasNext();) {
238                     ConfigurationData configurationData = (ConfigurationData) iterator.next();
239                     configurationData.setAutoStart(false);
240                     store.install(configurationData);
241                     deployedURIs.add(configurationData.getId().toString());
242                 }
243                 return deployedURIs;
244             } catch (IOException e) {
245                 cleanupConfigurations(configurations);
246                 throw e;
247             } catch (InvalidConfigException e) {
248                 cleanupConfigurations(configurations);
249                 // unlikely as we just built this
250                 throw new DeploymentException(e);
251             } finally {
252                 if (context != null) {
253                     context.close();
254                 }
255             }
256         } catch (Throwable e) {
257             if (e instanceof Error) {
258                 log.error("Deployment failed due to ", e);
259                 throw (Error) e;
260             } else if (e instanceof DeploymentException) {
261                 throw (DeploymentException) e;
262             } else if (e instanceof Exception) {
263                 log.error("Deployment failed due to ", e);
264                 throw new DeploymentException(e);
265             }
266             throw new Error(e);
267         } finally {
268             DeploymentUtil.close(module);
269         }
270     }
271 
272     private void cleanupConfigurations(List configurations) {
273         LinkedList cannotBeDeletedList = new LinkedList();
274         for (Iterator iterator = configurations.iterator(); iterator.hasNext();) {
275             ConfigurationData configurationData = (ConfigurationData) iterator.next();
276             File dir = configurationData.getConfigurationDir();
277             cannotBeDeletedList.clear();
278             if (!DeploymentUtil.recursiveDelete(dir,cannotBeDeletedList)) {
279                 // Output a message to help user track down file problem
280                 log.warn("Unable to delete " + cannotBeDeletedList.size() + 
281                         " files while recursively deleting directory " 
282                         + dir + LINE_SEP +
283                         "The first file that could not be deleted was:" + LINE_SEP + "  "+
284                         ( !cannotBeDeletedList.isEmpty() ? cannotBeDeletedList.getFirst() : "") );
285             }
286         }
287     }
288 
289     public File getDir() {
290         return dir;
291     }
292 
293     public Artifact getConfigurationId() {
294         return configurationId;
295     }
296 
297     public boolean isForceDeploy() {
298         return forceDeploy;
299     }
300 
301     public boolean wasDeployed() {
302         return wasDeployed;
303     }
304 
305     public static final GBeanInfo GBEAN_INFO;
306 
307     static {
308         GBeanInfoBuilder infoFactory = GBeanInfoBuilder.createStatic(SingleFileHotDeployer.class);
309 
310         infoFactory.addAttribute("path", String.class, true);
311         infoFactory.addReference("ServerInfo", ServerInfo.class);
312         infoFactory.addAttribute("watchPaths", String[].class, true);
313         infoFactory.addReference("Builders", ConfigurationBuilder.class);
314         infoFactory.addReference("Store", ConfigurationStore.class);
315         infoFactory.addReference("ConfigurationManager", ConfigurationManager.class);
316         infoFactory.addAttribute("forceDeploy", boolean.class, true);
317 
318         infoFactory.setConstructor(new String[]{"path", "ServerInfo", "watchPaths", "Builders", "Store", "ConfigurationManager", "forceDeploy"});
319 
320         GBEAN_INFO = infoFactory.getBeanInfo();
321     }
322 
323     public static GBeanInfo getGBeanInfo() {
324         return GBEAN_INFO;
325     }
326 }