1 package com.atlassian.plugin.osgi.spring;
2
3 import java.lang.reflect.Field;
4 import java.lang.reflect.Method;
5 import java.util.Map;
6
7 import javax.annotation.Nullable;
8
9 import com.atlassian.plugin.event.PluginEventListener;
10 import com.atlassian.plugin.event.PluginEventManager;
11 import com.atlassian.plugin.event.events.PluginFrameworkStartedEvent;
12
13 import com.google.common.base.Throwables;
14
15 import org.osgi.framework.Bundle;
16 import org.osgi.framework.BundleContext;
17 import org.osgi.framework.BundleEvent;
18 import org.osgi.framework.BundleListener;
19 import org.slf4j.Logger;
20 import org.slf4j.LoggerFactory;
21 import org.springframework.beans.CachedIntrospectionResults;
22 import org.springframework.beans.factory.DisposableBean;
23 import org.springframework.beans.factory.InitializingBean;
24
25
26
27
28
29
30
31 public class MarkBundleClassesCacheableListener implements BundleListener, InitializingBean, DisposableBean
32 {
33 private static final Logger log = LoggerFactory.getLogger(MarkBundleClassesCacheableListener.class);
34
35 private final BundleContext bundleContext;
36 private final PluginEventManager pluginEventManager;
37
38 private final Class<?> bundleImplClass;
39 private final Method getModule;
40 private final Class<?> moduleImplClass;
41 private final Method getClassLoader;
42
43 private final Object lock = new Object();
44 private boolean active;
45
46 public MarkBundleClassesCacheableListener(BundleContext bundleContext, PluginEventManager pluginEventManager)
47 {
48 this.bundleContext = bundleContext;
49 this.pluginEventManager = pluginEventManager;
50
51 Class<?> bundleImplClass = null;
52 Method getModule = null;
53 Class<?> moduleImplClass = null;
54 Method getClassLoader = null;
55 try
56 {
57 bundleImplClass = Bundle.class.getClassLoader().loadClass("org.apache.felix.framework.BundleImpl");
58 getModule = bundleImplClass.getDeclaredMethod("getCurrentModule");
59 getModule.setAccessible(true);
60
61 moduleImplClass = Bundle.class.getClassLoader().loadClass("org.apache.felix.framework.ModuleImpl");
62 getClassLoader = moduleImplClass.getDeclaredMethod("getClassLoader");
63 getClassLoader.setAccessible(true);
64
65 active = Boolean.getBoolean("atlassian.enable.spring.strong.cache.bean.metadata");
66 }
67 catch (Exception e)
68 {
69 log.warn("Reflection failed. Performance may suffer.", e);
70 }
71 this.bundleImplClass = bundleImplClass;
72 this.getModule = getModule;
73 this.moduleImplClass = moduleImplClass;
74 this.getClassLoader = getClassLoader;
75 }
76
77 @Override
78 public void afterPropertiesSet()
79 {
80 bundleContext.addBundleListener(this);
81 pluginEventManager.register(this);
82 for (Bundle bundle : bundleContext.getBundles())
83 {
84 if (bundle.getState() == Bundle.ACTIVE)
85 {
86 maybeAcceptClassLoader(bundle);
87 }
88 }
89 }
90
91 @Override
92 public void destroy()
93 {
94 synchronized (lock)
95 {
96 for (Bundle bundle : bundleContext.getBundles())
97 {
98 if (bundle.getState() == Bundle.ACTIVE)
99 {
100 maybeClearClassLoader(bundle);
101 }
102 }
103 active = false;
104 }
105
106 pluginEventManager.unregister(this);
107 bundleContext.removeBundleListener(this);
108 }
109
110 @Override
111 public void bundleChanged(BundleEvent event)
112 {
113 switch (event.getType()) {
114 case BundleEvent.STARTED: {
115 maybeAcceptClassLoader(event.getBundle());
116 break;
117 }
118 case BundleEvent.STOPPED: {
119 maybeClearClassLoader(event.getBundle());
120 break;
121 }
122 default:
123 break;
124 }
125 }
126
127 private void maybeAcceptClassLoader(Bundle bundle)
128 {
129 synchronized (lock)
130 {
131 ClassLoader bundleClassLoader = getBundleClassLoader(bundle);
132 if (bundleClassLoader != null)
133 {
134 CachedIntrospectionResults.acceptClassLoader(bundleClassLoader);
135 }
136 }
137
138 }
139
140 private void maybeClearClassLoader(Bundle bundle)
141 {
142 synchronized (lock)
143 {
144 ClassLoader bundleClassLoader = getBundleClassLoader(bundle);
145 if (bundleClassLoader != null)
146 {
147 CachedIntrospectionResults.clearClassLoader(bundleClassLoader);
148 }
149 }
150 }
151
152 @Nullable
153 private ClassLoader getBundleClassLoader(Bundle bundle)
154 {
155 if (active)
156 {
157 try
158 {
159
160
161 if (bundleImplClass == bundle.getClass())
162 {
163 final Object module = getModule.invoke(bundle);
164 if (moduleImplClass == module.getClass())
165 {
166 return (ClassLoader) getClassLoader.invoke(module);
167 }
168 }
169 }
170 catch (Exception e)
171 {
172 log.warn("Failed to retrieve class loader for bundle '{}'", bundle.getSymbolicName(), e);
173 }
174 }
175 return null;
176 }
177
178 @PluginEventListener
179 public void onPluginEnabled(PluginFrameworkStartedEvent event)
180 {
181 if(Boolean.getBoolean("atlassian.enable.spring.strong.cache.bean.metadata.flush"))
182 {
183 try
184 {
185 final Field classCacheField = CachedIntrospectionResults.class.getDeclaredField("classCache");
186 classCacheField.setAccessible(true);
187 Map classCache = (Map) classCacheField.get(null);
188
189
190
191 classCache.clear();
192 }
193 catch (Exception e)
194 {
195 Throwables.propagate(e);
196 }
197 }
198 }
199 }