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
85
86
87
88 if (dir.list().length == 0) {
89 return null;
90 }
91
92
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
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
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
148 Artifact configurationId = builder.getConfigurationID(plan, module, idBuilder);
149
150
151 if (!configurationId.isResolved()) {
152 configurationId = resolve(configurationId);
153 }
154
155
156
157
158
159 if ((existingConfigurationId == null) && configurationManager.isInstalled(configurationId)) {
160 log.info("Existing Module found by moduleId");
161 existingConfigurationId = configurationId;
162 }
163
164
165 if(configurationId.equals(existingConfigurationId)) {
166 log.info("Undeploying " + existingConfigurationId);
167 configurationManager.uninstallConfiguration(existingConfigurationId);
168 }
169
170
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
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
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
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 }