1 package com.atlassian.plugin.osgi.spring;
2
3 import com.atlassian.plugin.event.PluginEventListener;
4 import com.atlassian.plugin.event.PluginEventManager;
5 import com.atlassian.plugin.event.events.PluginFrameworkStartedEvent;
6 import com.google.common.base.Throwables;
7 import org.osgi.framework.Bundle;
8 import org.osgi.framework.BundleContext;
9 import org.osgi.framework.BundleEvent;
10 import org.osgi.framework.BundleListener;
11 import org.osgi.framework.wiring.BundleWiring;
12 import org.springframework.beans.CachedIntrospectionResults;
13 import org.springframework.beans.factory.DisposableBean;
14 import org.springframework.beans.factory.InitializingBean;
15
16 import javax.annotation.Nonnull;
17 import javax.annotation.Nullable;
18 import java.lang.reflect.Field;
19 import java.util.Map;
20
21
22
23
24
25
26
27 public class MarkBundleClassesCacheableListener implements BundleListener, InitializingBean, DisposableBean {
28 private final BundleContext bundleContext;
29 private final PluginEventManager pluginEventManager;
30
31 private final Object lock = new Object();
32 private boolean active = Boolean.getBoolean("atlassian.enable.spring.strong.cache.bean.metadata");
33
34 public MarkBundleClassesCacheableListener(BundleContext bundleContext, PluginEventManager pluginEventManager) {
35 this.bundleContext = bundleContext;
36 this.pluginEventManager = pluginEventManager;
37 }
38
39 @Override
40 public void afterPropertiesSet() {
41 bundleContext.addBundleListener(this);
42 pluginEventManager.register(this);
43 for (Bundle bundle : bundleContext.getBundles()) {
44 if (bundle.getState() == Bundle.ACTIVE) {
45 maybeAcceptClassLoader(bundle);
46 }
47 }
48 }
49
50 @Override
51 public void destroy() {
52 synchronized (lock) {
53 for (Bundle bundle : bundleContext.getBundles()) {
54 if ((bundle.getState() & (Bundle.ACTIVE | Bundle.STOPPING)) != 0) {
55 maybeClearClassLoader(bundle);
56 }
57 }
58 active = false;
59 }
60
61 pluginEventManager.unregister(this);
62 bundleContext.removeBundleListener(this);
63 }
64
65 @Override
66 public void bundleChanged(@Nonnull BundleEvent event) {
67 switch (event.getType()) {
68 case BundleEvent.STARTED: {
69 maybeAcceptClassLoader(event.getBundle());
70 break;
71 }
72 case BundleEvent.STOPPED: {
73 maybeClearClassLoader(event.getBundle());
74 break;
75 }
76 default:
77 break;
78 }
79 }
80
81 private void maybeAcceptClassLoader(@Nonnull Bundle bundle) {
82 if (bundle.getBundleId() == 0) {
83 return;
84 }
85
86 synchronized (lock) {
87 ClassLoader bundleClassLoader = getBundleClassLoader(bundle);
88 if (bundleClassLoader != null) {
89 CachedIntrospectionResults.acceptClassLoader(bundleClassLoader);
90 }
91 }
92
93 }
94
95 private void maybeClearClassLoader(@Nonnull Bundle bundle) {
96 synchronized (lock) {
97 ClassLoader bundleClassLoader = getBundleClassLoader(bundle);
98 if (bundleClassLoader != null) {
99 CachedIntrospectionResults.clearClassLoader(bundleClassLoader);
100 }
101 }
102 }
103
104 @Nullable
105 private ClassLoader getBundleClassLoader(@Nonnull Bundle bundle) {
106 if (active) {
107 final BundleWiring bundleWiring = bundle.adapt(BundleWiring.class);
108 if (bundleWiring != null) {
109 return bundleWiring.getClassLoader();
110 }
111 }
112 return null;
113 }
114
115 @PluginEventListener
116 public void onPluginEnabled(PluginFrameworkStartedEvent event) {
117 if (Boolean.getBoolean("atlassian.enable.spring.strong.cache.bean.metadata.flush")) {
118 try {
119 final Field classCacheField = CachedIntrospectionResults.class.getDeclaredField("strongClassCache");
120 classCacheField.setAccessible(true);
121 Map classCache = (Map) classCacheField.get(null);
122
123
124
125 classCache.clear();
126 } catch (Exception e) {
127 Throwables.propagate(e);
128 }
129 }
130 }
131 }