1 package com.atlassian.plugin.refimpl;
2
3 import com.atlassian.beehive.ClusterLockService;
4 import com.atlassian.beehive.SimpleClusterLockService;
5 import com.atlassian.event.api.EventPublisher;
6 import com.atlassian.healthcheck.spi.HealthCheckWhitelist;
7 import com.atlassian.healthcheck.spi.impl.ClasspathFileHealthCheckWhitelist;
8 import com.atlassian.plugin.Application;
9 import com.atlassian.plugin.DefaultModuleDescriptorFactory;
10 import com.atlassian.plugin.ModuleDescriptorFactory;
11 import com.atlassian.plugin.PluginAccessor;
12 import com.atlassian.plugin.PluginController;
13 import com.atlassian.plugin.PluginParseException;
14 import com.atlassian.plugin.SplitStartupPluginSystemLifecycle;
15 import com.atlassian.plugin.event.PluginEventManager;
16 import com.atlassian.plugin.eventlistener.descriptors.EventListenerModuleDescriptor;
17 import com.atlassian.plugin.hostcontainer.HostContainer;
18 import com.atlassian.plugin.hostcontainer.SimpleConstructorHostContainer;
19 import com.atlassian.plugin.main.AtlassianPlugins;
20 import com.atlassian.plugin.main.PluginsConfiguration;
21 import com.atlassian.plugin.main.PluginsConfigurationBuilder;
22 import com.atlassian.plugin.metadata.DefaultPluginMetadataManager;
23 import com.atlassian.plugin.metadata.PluginMetadataManager;
24 import com.atlassian.plugin.module.ClassPrefixModuleFactory;
25 import com.atlassian.plugin.module.ModuleFactory;
26 import com.atlassian.plugin.module.PrefixDelegatingModuleFactory;
27 import com.atlassian.plugin.module.PrefixModuleFactory;
28 import com.atlassian.plugin.osgi.container.OsgiContainerManager;
29 import com.atlassian.plugin.osgi.container.impl.DefaultPackageScannerConfiguration;
30 import com.atlassian.plugin.osgi.external.ListableModuleDescriptorFactory;
31 import com.atlassian.plugin.osgi.hostcomponents.ComponentRegistrar;
32 import com.atlassian.plugin.osgi.hostcomponents.HostComponentProvider;
33 import com.atlassian.plugin.osgi.module.BeanPrefixModuleFactory;
34 import com.atlassian.plugin.refimpl.cache.VCacheFactoryBean;
35 import com.atlassian.plugin.refimpl.db.ConnectionProviderImpl;
36 import com.atlassian.plugin.refimpl.saldeps.CookieBasedScopeManager;
37 import com.atlassian.plugin.refimpl.saldeps.CoreRefimplI18nResolver;
38 import com.atlassian.plugin.refimpl.saldeps.CoreRefimplLocaleResolver;
39 import com.atlassian.plugin.refimpl.servlet.SimpleContentTypeResolver;
40 import com.atlassian.plugin.refimpl.servlet.SimpleServletContextFactory;
41 import com.atlassian.plugin.refimpl.tenant.RefappLandlordRequests;
42 import com.atlassian.plugin.refimpl.tenant.RefappTenancyManager;
43 import com.atlassian.plugin.refimpl.tenant.RefappTenantRegistry;
44 import com.atlassian.plugin.refimpl.webresource.RefAppResourceBatchingConfiguration;
45 import com.atlassian.plugin.refimpl.webresource.SimpleWebResourceIntegration;
46 import com.atlassian.plugin.schema.descriptor.DescribedModuleDescriptorFactory;
47 import com.atlassian.plugin.schema.impl.DefaultDescribedModuleDescriptorFactory;
48 import com.atlassian.plugin.servlet.DefaultServletModuleManager;
49 import com.atlassian.plugin.servlet.DownloadStrategy;
50 import com.atlassian.plugin.servlet.ServletContextFactory;
51 import com.atlassian.plugin.servlet.ServletModuleManager;
52 import com.atlassian.plugin.servlet.descriptors.ServletContextListenerModuleDescriptor;
53 import com.atlassian.plugin.servlet.descriptors.ServletContextParamModuleDescriptor;
54 import com.atlassian.plugin.servlet.descriptors.ServletFilterModuleDescriptor;
55 import com.atlassian.plugin.servlet.descriptors.ServletModuleDescriptor;
56 import com.atlassian.plugin.util.Assertions;
57 import com.atlassian.plugin.webresource.PluginResourceLocator;
58 import com.atlassian.plugin.webresource.PluginResourceLocatorImpl;
59 import com.atlassian.plugin.webresource.ResourceBatchingConfiguration;
60 import com.atlassian.plugin.webresource.WebResourceIntegration;
61 import com.atlassian.plugin.webresource.WebResourceManager;
62 import com.atlassian.plugin.webresource.WebResourceManagerImpl;
63 import com.atlassian.plugin.webresource.WebResourceModuleDescriptor;
64 import com.atlassian.plugin.webresource.WebResourceUrlProvider;
65 import com.atlassian.plugin.webresource.WebResourceUrlProviderImpl;
66 import com.atlassian.plugin.webresource.assembler.DefaultPageBuilderService;
67 import com.atlassian.plugin.webresource.assembler.DefaultWebResourceAssemblerFactory;
68 import com.atlassian.plugin.webresource.prebake.PrebakeWebResourceAssemblerFactory;
69 import com.atlassian.plugin.webresource.servlet.PluginResourceDownload;
70 import com.atlassian.plugin.webresource.transformer.StaticTransformersSupplier;
71 import com.atlassian.plugin.webresource.transformer.UrlReadingWebResourceTransformerModuleDescriptor;
72 import com.atlassian.plugin.webresource.transformer.WebResourceTransformerModuleDescriptor;
73 import com.atlassian.plugins.landlord.spi.LandlordRequests;
74 import com.atlassian.refapp.api.ConnectionProvider;
75 import com.atlassian.tenancy.api.TenantAccessor;
76 import com.atlassian.tenancy.api.TenantContext;
77 import com.atlassian.vcache.VCacheFactory;
78 import com.atlassian.webresource.api.assembler.PageBuilderService;
79 import com.atlassian.webresource.api.assembler.WebResourceAssemblerFactory;
80 import com.google.common.collect.ImmutableSet;
81 import com.google.common.collect.Maps;
82 import com.google.common.util.concurrent.FutureCallback;
83 import com.google.common.util.concurrent.Futures;
84 import com.google.common.util.concurrent.ListenableFuture;
85 import com.google.common.util.concurrent.SettableFuture;
86 import org.apache.commons.io.FileUtils;
87 import org.apache.commons.io.IOUtils;
88 import org.apache.commons.lang.StringUtils;
89
90 import javax.servlet.ServletContext;
91 import java.io.File;
92 import java.io.IOException;
93 import java.io.InputStream;
94 import java.net.MalformedURLException;
95 import java.net.URL;
96 import java.util.ArrayList;
97 import java.util.HashMap;
98 import java.util.List;
99 import java.util.Map;
100 import java.util.Properties;
101
102
103
104
105 public class ContainerManager {
106
107
108
109 public static final String TENANTACCESSOR = "tenantAccessor";
110
111
112
113
114 private static final String PACKAGE_VERSION_EXPORT_OVERRIDES = "refapp.packageExport.overrides";
115
116
117
118
119 private static final String BUNDLED_PLUGINS_DIR = "/WEB-INF/atlassian-bundled-plugins";
120
121 private final ServletModuleManager servletModuleManager;
122 private final SimpleWebResourceIntegration webResourceIntegration;
123 private final OsgiContainerManager osgiContainerManager;
124 private final PluginAccessor pluginAccessor;
125 private final HostComponentProvider hostComponentProvider;
126 private final DefaultModuleDescriptorFactory moduleDescriptorFactory;
127 private final Map<Class<?>, Object> publicContainer;
128 private final AtlassianPlugins plugins;
129 private final SplitStartupPluginSystemLifecycle pluginLifecycle;
130 private final RefappTenantRegistry tenantAccessor;
131 private final RefappLandlordRequests landlordImpl;
132 private final RefappTenancyManager tenancyManager;
133 private final ClusterLockService clusterLockService;
134 private final ConnectionProviderImpl connectionProvider;
135
136 private final PluginPhaseTriggers triggers = new PluginPhaseTriggers();
137
138 private final HostContainer hostContainer;
139 private static ContainerManager instance;
140 private final List<DownloadStrategy> downloadStrategies;
141 private final DefaultPageBuilderService pageBuilderService;
142
143 public ContainerManager(final ServletContext servletContext) {
144 instance = this;
145 try {
146 Class factoryFinder = Class.forName("javax.xml.transform.FactoryFinder");
147 } catch (ClassNotFoundException e) {
148 throw new RuntimeException(e);
149 }
150
151 final HostContainer delegatingHostContainer = new HostContainer() {
152 public <T> T create(final Class<T> moduleClass) throws IllegalArgumentException {
153 return hostContainer.create(moduleClass);
154 }
155 };
156
157 try {
158 connectionProvider = new ConnectionProviderImpl();
159 } catch (Exception e) {
160
161 throw new RuntimeException(e);
162 }
163
164 moduleDescriptorFactory = new DefaultDescribedModuleDescriptorFactory(delegatingHostContainer);
165
166 moduleDescriptorFactory.addModuleDescriptor("servlet", ServletModuleDescriptor.class);
167 moduleDescriptorFactory.addModuleDescriptor("servlet-filter", ServletFilterModuleDescriptor.class);
168 moduleDescriptorFactory.addModuleDescriptor("servlet-context-param", ServletContextParamModuleDescriptor.class);
169 moduleDescriptorFactory.addModuleDescriptor("servlet-context-listener", ServletContextListenerModuleDescriptor.class);
170 moduleDescriptorFactory.addModuleDescriptor("web-resource", WebResourceModuleDescriptor.class);
171 moduleDescriptorFactory.addModuleDescriptor("web-resource-transformer", WebResourceTransformerModuleDescriptor.class);
172 moduleDescriptorFactory.addModuleDescriptor("url-reading-web-resource-transformer", UrlReadingWebResourceTransformerModuleDescriptor.class);
173 moduleDescriptorFactory.addModuleDescriptor("listener", EventListenerModuleDescriptor.class);
174
175 final DefaultPackageScannerConfiguration scannerConfig = new DefaultPackageScannerConfiguration(determineVersion());
176 scannerConfig.setServletContext(servletContext);
177
178 final List<String> packageIncludes = new ArrayList<String>(scannerConfig.getPackageIncludes());
179 packageIncludes.add("org.bouncycastle*");
180 packageIncludes.add("org.dom4j*");
181 packageIncludes.add("javax.servlet*");
182 packageIncludes.add("com.google.common.*");
183 packageIncludes.add("com.atlassian.sal*");
184 packageIncludes.add("com.opensymphony.module.sitemesh*");
185 packageIncludes.add("org.apache.commons.logging*");
186 scannerConfig.setPackageIncludes(packageIncludes);
187
188 final List<String> packageExcludes = new ArrayList<String>(scannerConfig.getPackageExcludes());
189 packageExcludes.add("com.atlassian.healthcheck.spi.impl");
190 scannerConfig.setPackageExcludes(packageExcludes);
191
192 final Map<String, String> packageVersions = Maps.<String, String>newHashMap();
193 packageVersions.put("com.google.common.*", SystemExportVersionUtils.getGoogleGuavaVersion());
194
195
196 packageVersions.put("org.w3c.*", "");
197
198
199
200
201 packageVersions.put("javax.annotation", "");
202 final String packageVersionExportOverrides = System.getProperty(PACKAGE_VERSION_EXPORT_OVERRIDES);
203 if (packageVersionExportOverrides != null) {
204 packageVersions.putAll(ConfigParser.parseMap(packageVersionExportOverrides));
205 }
206 scannerConfig.setPackageVersions(packageVersions);
207
208 hostComponentProvider = new SimpleHostComponentProvider();
209
210 File osgiCache = findAndCreateDirectory(servletContext, "osgi.cache", "/WEB-INF/osgi-cache");
211 File frameworkBundlesDir = findAndCreateDirectory(servletContext, "framework.bundles", "/WEB-INF/framework-bundles");
212 final URL bundledPluginDir = getBundledPluginDir(servletContext);
213
214
215 final File legacyBundledPluginCacheDirectory = FileUtils.toFile(bundledPluginDir);
216
217 final CookieBasedScopeManager scopeManager = new CookieBasedScopeManager();
218
219 final PluginsConfiguration config = new PluginsConfigurationBuilder()
220 .useLegacyDynamicPluginDeployer(true)
221 .frameworkBundleDirectory(frameworkBundlesDir)
222 .bundledPluginUrl(bundledPluginDir)
223 .bundledPluginCacheDirectory(legacyBundledPluginCacheDirectory)
224 .pluginDirectory(makeSureDirectoryExists(servletContext, "/WEB-INF/plugins"))
225 .moduleDescriptorFactory(moduleDescriptorFactory)
226 .packageScannerConfiguration(scannerConfig)
227 .hostComponentProvider(hostComponentProvider)
228 .osgiPersistentCache(osgiCache)
229 .pluginStateStore(new DefaultPluginPersistentStateStore(osgiCache))
230 .application(refapp())
231 .scopeManager(scopeManager)
232 .build();
233
234 PrefixDelegatingModuleFactory moduleFactory = new PrefixDelegatingModuleFactory(ImmutableSet.<PrefixModuleFactory>of(new BeanPrefixModuleFactory()));
235 plugins = new AtlassianPlugins(config);
236
237 final PluginEventManager pluginEventManager = plugins.getPluginEventManager();
238 osgiContainerManager = plugins.getOsgiContainerManager();
239
240 servletModuleManager = new DefaultServletModuleManager(servletContext, pluginEventManager, scopeManager);
241 pluginAccessor = plugins.getPluginAccessor();
242
243 pluginLifecycle = plugins.getPluginSystemLifecycle();
244
245 webResourceIntegration = new SimpleWebResourceIntegration(
246 servletContext,
247 new CoreRefimplLocaleResolver(),
248 new CoreRefimplI18nResolver(pluginAccessor, pluginEventManager),
249 pluginEventManager,
250 determineVersion()
251 );
252 WebResourceUrlProvider webResourceUrlProvider = new WebResourceUrlProviderImpl(webResourceIntegration);
253
254
255 final RefAppResourceBatchingConfiguration refappBatchconfig = new RefAppResourceBatchingConfiguration();
256
257 final ServletContextFactory simpleServletContextFactory = new SimpleServletContextFactory(servletContext);
258 final PluginResourceLocator pluginResourceLocator = new PluginResourceLocatorImpl(webResourceIntegration, simpleServletContextFactory, webResourceUrlProvider, refappBatchconfig, pluginEventManager);
259 final PluginResourceDownload pluginDownloadStrategy = new PluginResourceDownload(pluginResourceLocator, new SimpleContentTypeResolver(), "UTF-8");
260
261 PrebakeWebResourceAssemblerFactory prebakeWebResourceAssemblerFactory = new DefaultWebResourceAssemblerFactory(pluginResourceLocator);
262
263 pageBuilderService = new DefaultPageBuilderService(webResourceIntegration, prebakeWebResourceAssemblerFactory);
264
265 EventPublisher eventPublisher = plugins.getEventPublisher();
266
267
268 WebResourceManager webResourceManager = new WebResourceManagerImpl(pluginResourceLocator, webResourceIntegration, webResourceUrlProvider, refappBatchconfig);
269 tenantAccessor = new RefappTenantRegistry();
270 landlordImpl = new RefappLandlordRequests(triggers.getTenantTrigger(), tenantAccessor);
271 tenancyManager = new RefappTenancyManager(landlordImpl);
272 servletContext.setAttribute(TENANTACCESSOR, tenantAccessor);
273
274
275 clusterLockService = new SimpleClusterLockService();
276
277
278 VCacheFactory vCacheFactory = new VCacheFactoryBean().getObject();
279
280 publicContainer = new HashMap<Class<?>, Object>();
281 publicContainer.put(VCacheFactory.class, vCacheFactory);
282 publicContainer.put(PluginController.class, plugins.getPluginController());
283
284 publicContainer.put(ClusterLockService.class, clusterLockService);
285 publicContainer.put(SplitStartupPluginSystemLifecycle.class, pluginLifecycle);
286 publicContainer.put(EventPublisher.class, eventPublisher);
287 publicContainer.put(TenantAccessor.class, tenantAccessor);
288 publicContainer.put(TenantContext.class, tenantAccessor);
289 publicContainer.put(LandlordRequests.class, landlordImpl);
290 publicContainer.put(ServletModuleManager.class, servletModuleManager);
291 publicContainer.put(WebResourceManager.class, webResourceManager);
292 publicContainer.put(WebResourceUrlProvider.class, webResourceUrlProvider);
293 publicContainer.put(ResourceBatchingConfiguration.class, refappBatchconfig);
294 publicContainer.put(WebResourceIntegration.class, webResourceIntegration);
295 publicContainer.put(WebResourceAssemblerFactory.class, prebakeWebResourceAssemblerFactory);
296 publicContainer.put(PrebakeWebResourceAssemblerFactory.class, prebakeWebResourceAssemblerFactory);
297 publicContainer.put(PluginResourceLocator.class, pluginResourceLocator);
298 publicContainer.put(PageBuilderService.class, pageBuilderService);
299 publicContainer.put(WebResourceUrlProvider.class, webResourceUrlProvider);
300
301 publicContainer.put(Map.class, publicContainer);
302 publicContainer.put(ModuleFactory.class, moduleFactory);
303 publicContainer.put(PluginMetadataManager.class, new DefaultPluginMetadataManager());
304 publicContainer.put(ServletContextFactory.class, simpleServletContextFactory);
305
306 publicContainer.put(ModuleDescriptorFactory.class, moduleDescriptorFactory);
307 publicContainer.put(ListableModuleDescriptorFactory.class, moduleDescriptorFactory);
308 publicContainer.put(DescribedModuleDescriptorFactory.class, moduleDescriptorFactory);
309 publicContainer.put(TenantAccessor.class, tenantAccessor);
310
311 publicContainer.put(StaticTransformersSupplier.class, webResourceUrlProvider);
312
313 publicContainer.put(ConnectionProvider.class, connectionProvider);
314
315 publicContainer.put(HealthCheckWhitelist.class, new ClasspathFileHealthCheckWhitelist());
316
317 hostContainer = new SimpleConstructorHostContainer(publicContainer);
318 moduleFactory.addPrefixModuleFactory(new ClassPrefixModuleFactory(hostContainer));
319
320 tenancyManager.enableTenancy();
321 try {
322 pluginLifecycle.earlyStartup();
323 triggers.getPhase1Trigger().set(null);
324 } catch (final PluginParseException e) {
325 throw new RuntimeException(e);
326 }
327 downloadStrategies = new ArrayList<DownloadStrategy>();
328 downloadStrategies.add(pluginDownloadStrategy);
329 }
330
331 private URL getBundledPluginDir(final ServletContext servletContext) {
332 final File bundledPluginDir = makeSureDirectoryExists(servletContext, BUNDLED_PLUGINS_DIR);
333 final URL bundledPluginUrl;
334 try {
335 bundledPluginUrl = bundledPluginDir.toURI().toURL();
336 } catch (MalformedURLException e) {
337
338 throw new IllegalStateException("Can't form url to bundled plugins directory at: " + BUNDLED_PLUGINS_DIR, e);
339 }
340 return bundledPluginUrl;
341 }
342
343
344
345
346
347
348 private class PluginPhaseTriggers {
349 private SettableFuture<Void> triggerPhase1 = SettableFuture.create();
350 private SettableFuture<Void> triggerTenant = SettableFuture.create();
351 private ListenableFuture<List<Void>> phase2ready = Futures.allAsList(triggerPhase1, triggerTenant);
352
353 public PluginPhaseTriggers() {
354 Futures.addCallback(phase2ready, new FutureCallback<List<Void>>() {
355 @Override
356 public void onSuccess(List<Void> results) {
357 pluginLifecycle.lateStartup();
358 }
359
360 @Override
361 public void onFailure(Throwable throwable) {
362 }
363 });
364 }
365
366 public SettableFuture<Void> getPhase1Trigger() {
367 return triggerPhase1;
368 }
369
370 public SettableFuture<Void> getTenantTrigger() {
371 return triggerTenant;
372 }
373 }
374
375 private Application refapp() {
376 return new Application() {
377 @Override
378 public String getKey() {
379 return "refapp";
380 }
381
382 @Override
383 public String getVersion() {
384 return "1.0";
385 }
386
387 @Override
388 public String getBuildNumber() {
389 return "123";
390 }
391 };
392 }
393
394 private File findAndCreateDirectory(ServletContext servletContext, String sysPropName, String defaultPath) {
395 File dir;
396 if (System.getProperty(sysPropName) != null) {
397 dir = makeSureDirectoryExists(System.getProperty(sysPropName));
398 } else {
399 dir = makeSureDirectoryExists(servletContext, defaultPath);
400 }
401 return dir;
402 }
403
404 private String determineVersion() {
405 InputStream in = null;
406 final Properties props = new Properties();
407 try {
408 in = getClass().getClassLoader().getResourceAsStream("META-INF/maven/com.atlassian.plugins/atlassian-plugins-core/pom.properties");
409 if (in != null) {
410 props.load(in);
411 return props.getProperty("version");
412 }
413 } catch (final IOException e) {
414 e.printStackTrace();
415 return null;
416 } finally {
417 IOUtils.closeQuietly(in);
418 }
419 return null;
420 }
421
422 private File makeSureDirectoryExists(final ServletContext servletContext, final String relativePath) {
423 return makeSureDirectoryExists(servletContext.getRealPath(relativePath));
424 }
425
426 private File makeSureDirectoryExists(final String path) {
427 final File dir = new File(path);
428 if (!dir.exists() && !dir.mkdirs()) {
429 throw new RuntimeException("Could not create directory <" + dir + ">");
430 }
431 return dir;
432 }
433
434 public static synchronized void setInstance(final ContainerManager mgr) {
435 instance = mgr;
436 }
437
438 public static synchronized ContainerManager getInstance() {
439 return instance;
440 }
441
442 public ServletModuleManager getServletModuleManager() {
443 return servletModuleManager;
444 }
445
446 public OsgiContainerManager getOsgiContainerManager() {
447 return osgiContainerManager;
448 }
449
450 public PluginAccessor getPluginAccessor() {
451 return pluginAccessor;
452 }
453
454 public HostComponentProvider getHostComponentProvider() {
455 return hostComponentProvider;
456 }
457
458 public ModuleDescriptorFactory getModuleDescriptorFactory() {
459 return moduleDescriptorFactory;
460 }
461
462 public List<DownloadStrategy> getDownloadStrategies() {
463 return downloadStrategies;
464 }
465
466 public PageBuilderService getPageBuilderService() {
467 return pageBuilderService;
468 }
469
470 public WebResourceIntegration getWebResourceIntegration() {
471 return webResourceIntegration;
472 }
473
474 void shutdown() {
475 plugins.stop();
476 connectionProvider.terminate();
477 }
478
479 private class SimpleHostComponentProvider implements HostComponentProvider {
480 public void provide(final ComponentRegistrar componentRegistrar) {
481 Assertions.notNull("publicContainer", publicContainer);
482 for (final Map.Entry<Class<?>, Object> entry : publicContainer.entrySet()) {
483 final String name = StringUtils.uncapitalize(entry.getKey().getSimpleName());
484 componentRegistrar.register(entry.getKey()).forInstance(entry.getValue()).withName(name);
485 }
486
487 }
488 }
489 }