1
2
3
4
5
6
7
8
9
10
11 package org.mule.module.launcher.application;
12
13 import org.mule.MuleServer;
14 import org.mule.api.MuleContext;
15 import org.mule.api.MuleException;
16 import org.mule.api.config.ConfigurationBuilder;
17 import org.mule.api.config.MuleProperties;
18 import org.mule.api.context.notification.MuleContextNotificationListener;
19 import org.mule.config.builders.AutoConfigurationBuilder;
20 import org.mule.config.builders.SimpleConfigurationBuilder;
21 import org.mule.config.i18n.CoreMessages;
22 import org.mule.config.i18n.MessageFactory;
23 import org.mule.context.DefaultMuleContextFactory;
24 import org.mule.context.notification.MuleContextNotification;
25 import org.mule.context.notification.NotificationException;
26 import org.mule.module.launcher.AbstractFileWatcher;
27 import org.mule.module.launcher.AppBloodhound;
28 import org.mule.module.launcher.ApplicationMuleContextBuilder;
29 import org.mule.module.launcher.ConfigChangeMonitorThreadFactory;
30 import org.mule.module.launcher.DefaultAppBloodhound;
31 import org.mule.module.launcher.DefaultMuleSharedDomainClassLoader;
32 import org.mule.module.launcher.DeploymentInitException;
33 import org.mule.module.launcher.DeploymentStartException;
34 import org.mule.module.launcher.DeploymentStopException;
35 import org.mule.module.launcher.InstallException;
36 import org.mule.module.launcher.MuleApplicationClassLoader;
37 import org.mule.module.launcher.MuleSharedDomainClassLoader;
38 import org.mule.module.launcher.descriptor.ApplicationDescriptor;
39 import org.mule.module.reboot.MuleContainerBootstrapUtils;
40 import org.mule.util.ClassUtils;
41 import org.mule.util.FileUtils;
42 import org.mule.util.StringUtils;
43
44 import java.io.File;
45 import java.io.IOException;
46 import java.util.ArrayList;
47 import java.util.List;
48 import java.util.Map;
49 import java.util.concurrent.Executors;
50 import java.util.concurrent.ScheduledExecutorService;
51 import java.util.concurrent.TimeUnit;
52
53 import org.apache.commons.logging.Log;
54 import org.apache.commons.logging.LogFactory;
55
56 public class DefaultMuleApplication implements Application
57 {
58
59 protected static final int DEFAULT_RELOAD_CHECK_INTERVAL_MS = 3000;
60 protected static final String ANCHOR_FILE_BLURB = "Delete this file while Mule is running to undeploy this app in a clean way.";
61
62 protected transient final Log logger = LogFactory.getLog(getClass());
63
64 protected ScheduledExecutorService watchTimer;
65
66 private String appName;
67 private MuleContext muleContext;
68 private ClassLoader deploymentClassLoader;
69 private ApplicationDescriptor descriptor;
70
71 protected String[] absoluteResourcePaths;
72
73 protected DefaultMuleApplication(String appName)
74 {
75 this.appName = appName;
76 }
77
78 public void install()
79 {
80 if (logger.isInfoEnabled())
81 {
82 logger.info("Installing application: " + appName);
83 }
84
85 AppBloodhound bh = new DefaultAppBloodhound();
86 try
87 {
88 descriptor = bh.fetch(getAppName());
89 }
90 catch (IOException e)
91 {
92 throw new InstallException(MessageFactory.createStaticMessage("Failed to parse the application deployment descriptor"), e);
93 }
94
95
96 final String[] configResources = descriptor.getConfigResources();
97 absoluteResourcePaths = new String[configResources.length];
98 for (int i = 0; i < configResources.length; i++)
99 {
100 String resource = configResources[i];
101 final File file = toAbsoluteFile(resource);
102 if (!file.exists())
103 {
104 throw new InstallException(
105 MessageFactory.createStaticMessage(String.format("Config for app '%s' not found: %s", getAppName(), file))
106 );
107 }
108
109 absoluteResourcePaths[i] = file.getAbsolutePath();
110 }
111
112 createDeploymentClassLoader();
113 }
114
115 public String getAppName()
116 {
117 return appName;
118 }
119
120 public ApplicationDescriptor getDescriptor()
121 {
122 return descriptor;
123 }
124
125 public void setAppName(String appName)
126 {
127 this.appName = appName;
128 }
129
130 public void start()
131 {
132 if (logger.isInfoEnabled())
133 {
134 logger.info("Starting application: " + appName);
135 }
136
137 try
138 {
139 this.muleContext.start();
140
141 File marker = new File(MuleContainerBootstrapUtils.getMuleAppsDir(), String.format("%s-anchor.txt", getAppName()));
142 FileUtils.writeStringToFile(marker, ANCHOR_FILE_BLURB);
143 }
144 catch (MuleException e)
145 {
146
147 throw new DeploymentStartException(MessageFactory.createStaticMessage(appName), e);
148 }
149 catch (IOException e)
150 {
151
152 throw new DeploymentStartException(MessageFactory.createStaticMessage(appName), e);
153 }
154 }
155
156 public void init()
157 {
158 if (logger.isInfoEnabled())
159 {
160 logger.info("Initializing application: " + appName);
161 }
162
163 String configBuilderClassName = null;
164 try
165 {
166
167
168 final String builderFromDesc = descriptor.getConfigurationBuilder();
169 if ("spring".equalsIgnoreCase(builderFromDesc))
170 {
171 configBuilderClassName = ApplicationDescriptor.CLASSNAME_SPRING_CONFIG_BUILDER;
172 }
173 else if (builderFromDesc == null)
174 {
175 configBuilderClassName = AutoConfigurationBuilder.class.getName();
176 }
177 else
178 {
179 configBuilderClassName = builderFromDesc;
180 }
181
182 ConfigurationBuilder cfgBuilder = (ConfigurationBuilder) ClassUtils.instanciateClass(
183 configBuilderClassName, new Object[] {absoluteResourcePaths}, getDeploymentClassLoader());
184
185 if (!cfgBuilder.isConfigured())
186 {
187
188 List<ConfigurationBuilder> builders = new ArrayList<ConfigurationBuilder>(2);
189
190 final Map<String,String> appProperties = descriptor.getAppProperties();
191
192
193 appProperties.put(MuleProperties.APP_HOME_DIRECTORY_PROPERTY,
194 new File(MuleContainerBootstrapUtils.getMuleAppsDir(), getAppName()).getAbsolutePath());
195
196 builders.add(new SimpleConfigurationBuilder(appProperties));
197
198
199
200
201 if (ClassUtils.isClassOnPath(MuleServer.CLASSNAME_ANNOTATIONS_CONFIG_BUILDER, getClass()))
202 {
203 Object configBuilder = ClassUtils.instanciateClass(
204 MuleServer.CLASSNAME_ANNOTATIONS_CONFIG_BUILDER, ClassUtils.NO_ARGS, getClass());
205 builders.add((ConfigurationBuilder) configBuilder);
206 }
207
208 builders.add(cfgBuilder);
209
210 DefaultMuleContextFactory muleContextFactory = new DefaultMuleContextFactory();
211 this.muleContext = muleContextFactory.createMuleContext(builders, new ApplicationMuleContextBuilder(descriptor));
212
213 if (descriptor.isRedeploymentEnabled())
214 {
215 createRedeployMonitor();
216 }
217 }
218 }
219 catch (Exception e)
220 {
221 throw new DeploymentInitException(CoreMessages.failedToLoad(configBuilderClassName), e);
222 }
223 }
224
225 public MuleContext getMuleContext()
226 {
227 return muleContext;
228 }
229
230 public ClassLoader getDeploymentClassLoader()
231 {
232 return this.deploymentClassLoader;
233 }
234
235 public void dispose()
236 {
237 if (muleContext == null)
238 {
239 if (logger.isInfoEnabled())
240 {
241 logger.info("MuleContext not created, nothing to dispose of");
242 }
243 return;
244 }
245
246 if (muleContext.isStarted() && !muleContext.isDisposed())
247 {
248 stop();
249 }
250 if (logger.isInfoEnabled())
251 {
252 logger.info("Disposing application: " + appName);
253 }
254
255 muleContext.dispose();
256 muleContext = null;
257
258 Thread.currentThread().setContextClassLoader(null);
259 }
260
261 public void redeploy()
262 {
263 if (logger.isInfoEnabled())
264 {
265 logger.info("Redeploying application: " + appName);
266 }
267 dispose();
268 install();
269
270
271 final ClassLoader cl = getDeploymentClassLoader();
272 Thread.currentThread().setContextClassLoader(cl);
273
274 init();
275 start();
276
277
278 Thread.currentThread().setContextClassLoader(null);
279 }
280
281 public void stop()
282 {
283 if (this.muleContext == null)
284 {
285
286 return;
287 }
288 if (logger.isInfoEnabled())
289 {
290 logger.info("Stopping application: " + appName);
291 }
292 try
293 {
294 this.muleContext.stop();
295 }
296 catch (MuleException e)
297 {
298
299 throw new DeploymentStopException(MessageFactory.createStaticMessage(appName), e);
300 }
301 }
302
303 @Override
304 public String toString()
305 {
306 return String.format("%s[%s]@%s", getClass().getName(),
307 appName,
308 Integer.toHexString(System.identityHashCode(this)));
309 }
310
311 protected void createDeploymentClassLoader()
312 {
313 final String domain = descriptor.getDomain();
314 ClassLoader parent;
315
316 if (StringUtils.isBlank(domain) || DefaultMuleSharedDomainClassLoader.DEFAULT_DOMAIN_NAME.equals(domain))
317 {
318 parent = new DefaultMuleSharedDomainClassLoader(getClass().getClassLoader());
319 }
320 else
321 {
322
323 parent = new MuleSharedDomainClassLoader(domain, getClass().getClassLoader());
324 }
325
326 this.deploymentClassLoader = new MuleApplicationClassLoader(appName, parent);
327 }
328
329 protected void createRedeployMonitor() throws NotificationException
330 {
331 if (logger.isInfoEnabled())
332 {
333 logger.info("Monitoring for hot-deployment: " + new File(absoluteResourcePaths [0]));
334 }
335
336 final AbstractFileWatcher watcher = new ConfigFileWatcher(new File(absoluteResourcePaths [0]));
337
338
339 muleContext.registerListener(new MuleContextNotificationListener<MuleContextNotification>()
340 {
341
342 public void onNotification(MuleContextNotification notification)
343 {
344 final int action = notification.getAction();
345 switch (action)
346 {
347 case MuleContextNotification.CONTEXT_STARTED:
348 scheduleConfigMonitor(watcher);
349 break;
350 case MuleContextNotification.CONTEXT_STOPPING:
351 watchTimer.shutdownNow();
352 muleContext.unregisterListener(this);
353 break;
354 }
355 }
356 });
357 }
358
359 protected void scheduleConfigMonitor(AbstractFileWatcher watcher)
360 {
361 final int reloadIntervalMs = DEFAULT_RELOAD_CHECK_INTERVAL_MS;
362 watchTimer = Executors.newSingleThreadScheduledExecutor(new ConfigChangeMonitorThreadFactory(appName));
363
364 watchTimer.scheduleWithFixedDelay(watcher, reloadIntervalMs, reloadIntervalMs, TimeUnit.MILLISECONDS);
365
366 if (logger.isInfoEnabled())
367 {
368 logger.info("Reload interval: " + reloadIntervalMs);
369 }
370 }
371
372
373
374
375
376
377 protected File toAbsoluteFile(String path)
378 {
379 final String muleHome = System.getProperty(MuleProperties.MULE_HOME_DIRECTORY_PROPERTY);
380 String configPath = String.format("%s/apps/%s/%s", muleHome, getAppName(), path);
381 return new File(configPath);
382 }
383
384 protected class ConfigFileWatcher extends AbstractFileWatcher
385 {
386 public ConfigFileWatcher(File watchedResource)
387 {
388 super(watchedResource);
389 }
390
391 @Override
392 protected synchronized void onChange(File file)
393 {
394 if (logger.isInfoEnabled())
395 {
396 logger.info("================== Reloading " + file);
397 }
398
399
400 final ClassLoader cl = getDeploymentClassLoader();
401 Thread.currentThread().setContextClassLoader(cl);
402 redeploy();
403 }
404 }
405 }