随笔 - 19, 文章 - 1, 评论 - 21, 引用 - 0
数据加载中……

打造一个基于OSGi的Web Application——使用Tomcat原生API动态管理Listener

本文介绍在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 < 0return 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 }








posted on 2010-04-02 18:47 dbstar 阅读(3642) 评论(2)  编辑  收藏 所属分类: OSGi

评论

# re: 打造一个基于OSGi的Web Application——使用Tomcat原生API动态管理Listener  回复  更多评论   

实现WEB容器的原生API的方案工作量有点大啊,不知道有没有下文。。。
2010-05-26 11:55 | hesy

# re: 打造一个基于OSGi的Web Application——使用Tomcat原生API动态管理Listener[未登录]  回复  更多评论   

OSGi最成熟、最NB的开源开发平台JXADF。
欢迎访问:http://osgi.jxtech.net
2015-11-05 14:09 | Java Fans

只有注册用户登录后才能发表评论。


网站导航: