本文介绍在Tomcat中,如何通过Tomcat原生API实现OSGi容器中动态管理Listener。
首先建个接口类,用于描述注册Listener所使用的Service:
1 package org.dbstar.osgi.web.register;
2
3 import java.util.EventListener;
4
5 import org.osgi.framework.Bundle;
6
7 public interface ListenerService {
8 /**
9 * 判断当前ListenerService实现是否支持使用Listener实例。Service的使用者可以根据此参数来选择适合的调用方式。
10 *
11 * <pre>
12 * 1.如果支持使用Listener实例,那么在addApplicationListener(Bundle, ListenerRegistration, EventListener)方法中的
13 * EventListener实例将被直接注册到Web Context中去,从而使得Listener实例中的预设环境得以保留。
14 * 2.如果不支持使用Listener实例,那么不管调用哪个addApplicationListener方法,新的实例总是会根据提供的ListenerRegistration
15 * 创建出来,而方法中的EventListener参数将被忽略。
16 * </pre>
17 *
18 * @return 当前ListenerService实现是否支持使用Listener实例。
19 */
20 public boolean supportsListenerInstance();
21
22 /**
23 * 根据提供的ListenerRegistration来注册一个新的Listener到Web Context中。
24 *
25 * @param bundle 注册listener的bundle
26 * @param registration Listener注册信息
27 * @return 新增加的Listener的系统名称,这个名称用于调用removeApplicationListener方法
28 * @exception IllegalArgumentException 如果根据提供的ListenerRegistration不能创建Listener实例
29 * @exception IllegalArgumentException 如果待增加的Listener在Web Context中不唯一
30 */
31 public String addApplicationListener(Bundle bundle, ListenerRegistration registration);
32
33 /**
34 * 根据提供的ListenerRegistration和Listener实例来注册一个新的Listener到Web Context中。
35 *
36 * <pre>
37 * 根据supportsListenerInstance返回的结果不同,此方法有不同的处理方式:
38 * 1.如果supportsListenerInstance返回true,那么方法中的Listener实例参数将被直接注册到Web Context中去。
39 * 2.如果supportsListenerInstance返回false,那么方法中的Listener实例参数将被忽略,Listener实例将根据ListenerRegistration来创建。
40 * </pre>
41 *
42 * @param bundle 注册listener的bundle
43 * @param registration Listener注册信息
44 * @param listener Listener实例
45 * @return 新增加的Listener的系统名称,这个名称用于调用removeApplicationListener方法
46 * @exception IllegalArgumentException 如果根据提供的ListenerRegistration不能创建Listener实例
47 * @exception IllegalArgumentException 如果待增加的Listener在Web Context中不唯一
48 */
49 public String addApplicationListener(Bundle bundle, ListenerRegistration registration, EventListener listener);
50
51 /**
52 * 从Web Context中删除指定的Listener。
53 *
54 * @param listenerName 待删除Listener对应的系统名称,这个名称通过addApplicationListener方法返回值获得
55 */
56 public void removeApplicationListener(String listenerName);
57 }
然后是
ListenerRegistration,用于记载注册Listener时所提供的信息:
1 package org.dbstar.osgi.web.register;
2
3 import java.util.EventListener;
4
5 public final class ListenerRegistration extends GeneralInfo<ListenerRegistration> {
6 private static final long serialVersionUID = -6201323709943780698L;
7
8 private Class<? extends EventListener> listenerClass;
9
10 public ListenerRegistration(Class<? extends EventListener> listenerClass) {
11 this.listenerClass = listenerClass;
12 }
13
14 public Class<? extends EventListener> getListenerClass() {
15 return listenerClass;
16 }
17 }
1 package org.dbstar.osgi.web.register;
2
3 import java.io.Serializable;
4
5 public abstract class GeneralInfo<T> implements Serializable {
6 private static final long serialVersionUID = -5739502239414925563L;
7
8 private String description;
9 private String displayName;
10 private Icon icon;
11
12 public final String getDescription() {
13 return description;
14 }
15
16 @SuppressWarnings("unchecked")
17 public final T setDescription(String description) {
18 this.description = description;
19 return (T) this;
20 }
21
22 public final String getDisplayName() {
23 return displayName;
24 }
25
26 @SuppressWarnings("unchecked")
27 public final T setDisplayName(String displayName) {
28 this.displayName = displayName;
29 return (T) this;
30 }
31
32 public final Icon getIcon() {
33 return icon;
34 }
35
36 @SuppressWarnings("unchecked")
37 public final T setIcon(Icon icon) {
38 this.icon = icon;
39 return (T) this;
40 }
41 }
1 package org.dbstar.osgi.web.register;
2
3 import java.io.Serializable;
4
5 public final class Icon implements Serializable {
6 private static final long serialVersionUID = 5232737399172945853L;
7
8 private String smallIcon;
9 private String largeIcon;
10
11 public String getSmallIcon() {
12 return smallIcon;
13 }
14
15 public Icon setSmallIcon(String smallIcon) {
16 this.smallIcon = smallIcon;
17 return this;
18 }
19
20 public String getLargeIcon() {
21 return largeIcon;
22 }
23
24 public Icon setLargeIcon(String largeIcon) {
25 this.largeIcon = largeIcon;
26 return this;
27 }
28 }
细心的同学可能已经发现了,ListenerRegistration类定义的属性与Servlet 2.4的web.xml中,<listener>标签的描述是一致的。
好了,Service接口定义好了,下面我们来写一个实现类,还是使用DS方式:
ListenerService.xml:
1 <?xml version="1.0" encoding="UTF-8"?>
2 <scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="TomcatListenerService"
3 xsi:schemaLocation="http://www.osgi.org/xmlns/scr/v1.1.0 http://www.osgi.org/xmlns/scr/v1.1.0/scr.xsd">
4 <implementation class="org.dbstar.osgi.web.register.tomcat.ListenerServiceImpl" />
5 <reference cardinality="1..1" interface="org.apache.catalina.Context" name="Context" policy="static" />
6 <reference bind="bindLog" cardinality="0..1" interface="org.osgi.service.log.LogService" name="LogService"
7 policy="static" unbind="unbindLog" />
8 <service>
9 <provide interface="org.dbstar.osgi.web.register.ListenerService" />
10 </service>
11 </scr:component>
接下来是ListenerServiceImpl,当然了,它必须实现ListenerService接口:
1 package org.dbstar.osgi.web.register.tomcat;
2
3 import java.util.ArrayList;
4 import java.util.EventListener;
5 import java.util.HashMap;
6 import java.util.List;
7 import java.util.Map;
8 import java.util.Set;
9
10 import javax.servlet.ServletContextAttributeListener;
11 import javax.servlet.ServletContextEvent;
12 import javax.servlet.ServletContextListener;
13 import javax.servlet.ServletRequestAttributeListener;
14 import javax.servlet.ServletRequestListener;
15 import javax.servlet.http.HttpSessionAttributeListener;
16 import javax.servlet.http.HttpSessionEvent;
17 import javax.servlet.http.HttpSessionListener;
18
19 import org.apache.catalina.Context;
20 import org.apache.catalina.Session;
21 import org.dbstar.osgi.web.register.ListenerRegistration;
22 import org.dbstar.osgi.web.register.ListenerService;
23 import org.osgi.framework.Bundle;
24 import org.osgi.framework.BundleEvent;
25 import org.osgi.framework.BundleListener;
26 import org.osgi.service.component.ComponentContext;
27 import org.osgi.service.log.LogService;
28
29 public class ListenerServiceImpl implements ListenerService, BundleListener {
30 private Context context;
31 private LogService log;
32
33 /**
34 * Bundle ID和ListenerName数组的映射,通过Bundle ID找到所有此Bundle注册的Listener的系统名称
35 */
36 private Map<Long, List<String>> bnsMap = new HashMap<Long, List<String>>();
37
38 /**
39 * ListenerName和Listener实例的映射,通过ListenerName找到对应的Listener实例
40 */
41 private Map<String, EventListener> nlMap = new HashMap<String, EventListener>();
42
43 /**
44 * ListenerName和Bundle ID的映射,通过ListenerName找到对应的注册此Listener的Bundle的ID
45 */
46 private Map<String, Long> nbMap = new HashMap<String, Long>();
47
48 protected void activate(ComponentContext context) {
49 // 获取org.apache.catalina.Context
50 this.context = (Context) context.locateService("Context");
51 context.getBundleContext().addBundleListener(this);
52 }
53
54 protected void deactivate(ComponentContext context) {
55 context.getBundleContext().removeBundleListener(this);
56 // 在destroy方法中注销所有注册的Listener
57 destroy();
58 this.context = null;
59 }
60
61 protected void bindLog(LogService log) {
62 this.log = log;
63 }
64
65 protected void unbindLog(LogService log) {
66 if (this.log == log) this.log = null;
67 }
68
69 public void bundleChanged(BundleEvent event) {
70 if (event.getType() == BundleEvent.STOPPED) {
71 // 在Bundle Stop之后注销所有此Bundle注册的Listener
72 Long bundleId = event.getBundle().getBundleId();
73 synchronized (this) {
74 if (bnsMap.containsKey(bundleId)) {
75 List<String> nameList = bnsMap.get(bundleId);
76 for (String listenerName : nameList.toArray(new String[nameList.size()])) {
77 removeApplicationListener(bundleId, listenerName);
78 }
79 }
80 }
81 }
82 }
83
84 /*
85 * (non-Javadoc)
86 *
87 * @see org.dbstar.osgi.web.register.ListenerService#supportsListenerInstance()
88 */
89 public boolean supportsListenerInstance() {
90 return true;
91 }
92
93 /*
94 * (non-Javadoc)
95 *
96 * @see org.dbstar.osgi.web.register.ListenerService#addApplicationListener(org.osgi.framework.Bundle,
97 * org.dbstar.osgi.web.register.ListenerRegistration)
98 */
99 public synchronized String addApplicationListener(Bundle bundle, ListenerRegistration registration) {
100 EventListener listener = null;
101 try {
102 listener = registration.getListenerClass().newInstance();
103 } catch (Throwable e) {
104 throw new IllegalArgumentException("Create listener instance error: " + registration.getListenerClass(), e);
105 }
106
107 return addApplicationListener(bundle, registration, listener);
108 }
109
110 /*
111 * (non-Javadoc)
112 *
113 * @see org.dbstar.osgi.web.register.ListenerService#addApplicationListener(org.osgi.framework.Bundle,
114 * org.dbstar.osgi.web.register.ListenerRegistration, java.util.EventListener)
115 */
116 public synchronized String addApplicationListener(Bundle bundle, ListenerRegistration registration,
117 EventListener listener) {
118 String listenerName = getListenerName(bundle, listener);
119
120 // 检查listener是否已经注册过
121 if (nlMap.containsValue(listener) || nlMap.containsKey(listenerName)) {
122 if (log != null) log.log(LogService.LOG_ERROR, "Listener already registered: " + listenerName);
123 throw new IllegalArgumentException("Listener already registered: " + listenerName);
124 }
125
126 addApplicationListener(bundle.getBundleId(), listener, listenerName);
127 return listenerName;
128 }
129
130 private void addApplicationListener(Long bundleId, EventListener listener, String listenerName) {
131 boolean isApplicationListener = false;
132
133 if (isEventListener(listener)) {
134 Object[] listeners = context.getApplicationEventListeners();
135 if (!inArray(listeners, listener)) {
136 context.setApplicationEventListeners(addToArray(listeners, listener));
137 isApplicationListener = true;
138 }
139 }
140
141 if (isLifecycleListener(listener)) {
142 Object[] listeners = context.getApplicationLifecycleListeners();
143 if (!inArray(listeners, listener)) {
144 context.setApplicationLifecycleListeners(addToArray(listeners, listener));
145 isApplicationListener = true;
146 }
147 }
148
149 if (isApplicationListener) {
150 context.addApplicationListener(listenerName);
151
152 List<String> nameList = bnsMap.get(bundleId);
153 if (nameList == null) {
154 nameList = new ArrayList<String>();
155 bnsMap.put(bundleId, nameList);
156 }
157 nameList.add(listenerName);
158 nlMap.put(listenerName, listener);
159 nbMap.put(listenerName, bundleId);
160
161 if (log != null) log.log(LogService.LOG_INFO, "Add ApplicationListener success: " + listenerName);
162
163 // 必要的初始化
164 if (listener instanceof ServletContextListener) {
165 try {
166 ServletContextEvent event = new ServletContextEvent(context.getServletContext());
167 ((ServletContextListener) listener).contextInitialized(event);
168 } catch (Throwable e) {
169 if (log != null) log.log(LogService.LOG_WARNING, "contextInitialized() error: " + listenerName, e);
170 }
171 }
172 if (listener instanceof HttpSessionListener) {
173 for (Session session : context.getManager().findSessions()) {
174 try {
175 HttpSessionEvent event = new HttpSessionEvent(session.getSession());
176 ((HttpSessionListener) listener).sessionCreated(event);
177 } catch (Throwable e) {
178 if (log != null) log.log(LogService.LOG_WARNING, "sessionCreated() error: " + listenerName, e);
179 }
180 }
181 }
182 }
183 }
184
185 /*
186 * (non-Javadoc)
187 *
188 * @see org.dbstar.osgi.web.register.ListenerService#removeApplicationListener(java.lang.String)
189 */
190 public synchronized void removeApplicationListener(String listenerName) {
191 // 检查listener是否已经注册过
192 if (!nlMap.containsKey(listenerName)) {
193 if (log != null) log.log(LogService.LOG_WARNING, "Listener not register: " + listenerName);
194 return;
195 }
196
197 removeApplicationListener(nbMap.get(listenerName), listenerName);
198 }
199
200 private void removeApplicationListener(Long bundleId, String listenerName) {
201 boolean isApplicationListener = false;
202
203 EventListener listener = nlMap.get(listenerName);
204
205 if (isEventListener(listener)) {
206 Object[] listeners = context.getApplicationEventListeners();
207 if (inArray(listeners, listener)) {
208 context.setApplicationEventListeners(removeFromArray(listeners, listener));
209 isApplicationListener = true;
210 }
211 }
212
213 if (isLifecycleListener(listener)) {
214 Object[] listeners = context.getApplicationLifecycleListeners();
215 if (inArray(listeners, listener)) {
216 context.setApplicationLifecycleListeners(removeFromArray(listeners, listener));
217 isApplicationListener = true;
218 }
219 }
220
221 if (isApplicationListener) {
222 context.removeApplicationListener(listenerName);
223
224 List<String> nameList = bnsMap.get(bundleId);
225 if (nameList != null) {
226 nameList.remove(listenerName);
227 if (nameList.isEmpty()) bnsMap.remove(bundleId);
228 }
229 nlMap.remove(listenerName);
230 nbMap.remove(listenerName);
231
232 if (log != null) log.log(LogService.LOG_INFO, "Remove ApplicationListener success: " + listenerName);
233
234 // destroy
235 if (listener instanceof ServletContextListener) {
236 try {
237 ServletContextEvent event = new ServletContextEvent(context.getServletContext());
238 ((ServletContextListener) listener).contextDestroyed(event);
239 } catch (Throwable e) {
240 if (log != null) log.log(LogService.LOG_WARNING, "contextDestroyed() error: " + listenerName, e);
241 }
242 }
243 if (listener instanceof HttpSessionListener) {
244 for (Session session : context.getManager().findSessions()) {
245 try {
246 HttpSessionEvent event = new HttpSessionEvent(session.getSession());
247 ((HttpSessionListener) listener).sessionDestroyed(event);
248 } catch (Throwable e) {
249 if (log != null) log
250 .log(LogService.LOG_WARNING, "sessionDestroyed() error: " + listenerName, e);
251 }
252 }
253 }
254 }
255 }
256
257 private static boolean inArray(Object[] objects, Object object) {
258 for (Object obj : objects) {
259 if (obj == object) return true;
260 }
261 return false;
262 }
263
264 private static Object[] addToArray(Object[] objects, Object object) {
265 Object[] newList = new Object[objects.length + 1];
266 System.arraycopy(objects, 0, newList, 0, objects.length);
267 newList[objects.length] = object;
268 return newList;
269 }
270
271 private static Object[] removeFromArray(Object[] objects, Object object) {
272 int n = -1;
273 for (int i = 0; i < objects.length; i++) {
274 if (objects[i] == object) {
275 n = i;
276 break;
277 }
278 }
279 if (n < 0) return objects;
280
281 // Remove the specified constraint
282 int j = 0;
283 Object results[] = new Object[objects.length - 1];
284 for (int i = 0; i < objects.length; i++) {
285 if (i != n) results[j++] = objects[i];
286 }
287 return results;
288 }
289
290 private String getListenerName(Bundle bundle, EventListener listener) {
291 Class<? extends EventListener> clazz = listener.getClass();
292 String className = clazz.getName();
293 if (bundle != null) className = "bundle@" + bundle.getBundleId() + "@" + className + "@" + listener.hashCode();
294 return className;
295 }
296
297 private boolean isEventListener(EventListener listener) {
298 return (listener instanceof ServletContextAttributeListener)
299 || (listener instanceof ServletRequestAttributeListener)
300 || (listener instanceof ServletRequestListener) || (listener instanceof HttpSessionAttributeListener);
301 }
302
303 private boolean isLifecycleListener(EventListener listener) {
304 return (listener instanceof ServletContextListener) || (listener instanceof HttpSessionListener);
305 }
306
307 private synchronized void destroy() {
308 Set<Long> bundleSet = bnsMap.keySet();
309 for (Long bundleId : bundleSet.toArray(new Long[bundleSet.size()])) {
310 List<String> nameList = bnsMap.get(bundleId);
311 for (String listenerName : nameList.toArray(new String[nameList.size()])) {
312 removeApplicationListener(bundleId, listenerName);
313 }
314 }
315 }
316 }
在这个实现类中,我还实现了一个BundleListener接口,这样就可以在某个bundle stop的时候,顺便把这个bundle所注册的所有的Listener都给注销掉,这样就不用在bundle内仔细的维护Listener的注册和注销了,也算是一个小小的便利吧,当然了,对于在Component内部注册的Listener,还是不能在Component销毁的时候自动实行反注册,需要自己维护。
最后,再给一个使用ListenerService的例子吧:
listener.xml:
1 <?xml version="1.0" encoding="UTF-8"?>
2 <scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="testListener" xsi:schemaLocation="http://www.osgi.org/xmlns/scr/v1.1.0 http://www.osgi.org/xmlns/scr/v1.1.0/scr.xsd">
3 <implementation class="testlistener.TestListener"/>
4 <reference cardinality="1..1" interface="org.dbstar.osgi.web.register.ListenerService" name="ListenerService" policy="static"/>
5 <reference bind="bindLog" cardinality="0..1" interface="org.osgi.service.log.LogService" name="LogService" policy="static" unbind="unbindLog"/>
6 </scr:component>
1 package testlistener;
2
3 import javax.servlet.ServletContextEvent;
4 import javax.servlet.ServletContextListener;
5 import javax.servlet.ServletRequestEvent;
6 import javax.servlet.ServletRequestListener;
7
8 import org.dbstar.osgi.web.register.ListenerRegistration;
9 import org.dbstar.osgi.web.register.ListenerService;
10 import org.osgi.framework.Bundle;
11 import org.osgi.service.component.ComponentContext;
12 import org.osgi.service.log.LogService;
13
14 public class TestListener implements ServletRequestListener, ServletContextListener {
15 private LogService log;
16 private String listenerName;
17
18 protected void activate(ComponentContext context) {
19 Bundle bundle = context.getBundleContext().getBundle();
20 ListenerService service = (ListenerService) context.locateService("ListenerService");
21 listenerName = service.addApplicationListener(bundle, new ListenerRegistration(TestListener.class), this);
22 }
23
24 protected void deactivate(ComponentContext context) {
25 ListenerService service = (ListenerService) context.locateService("ListenerService");
26 if (listenerName != null) service.removeApplicationListener(listenerName);
27 }
28
29 protected void bindLog(LogService log) {
30 this.log = log;
31 }
32
33 protected void unbindLog(LogService log) {
34 if (this.log == log) this.log = null;
35 }
36
37 public void requestDestroyed(ServletRequestEvent sre) {
38 if (log != null) log.log(LogService.LOG_INFO, "requestDestroyed(" + sre + ")");
39 }
40
41 public void requestInitialized(ServletRequestEvent sre) {
42 if (log != null) log.log(LogService.LOG_INFO, "requestInitialized(" + sre + ")");
43 }
44
45 public void contextDestroyed(ServletContextEvent sce) {
46 if (log != null) log.log(LogService.LOG_INFO, "contextDestroyed(" + sce + ")");
47 }
48
49 public void contextInitialized(ServletContextEvent sce) {
50 if (log != null) log.log(LogService.LOG_INFO, "contextInitialized(" + sce + ")");
51 }
52 }