论坛首页 Java企业应用论坛

Wicket在OSGi框架下的开发

浏览 3622 次
精华帖 (1) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2007-09-08  
       之前写的文章 Wicket如何通过OSGi框架注入Jetty 中关于wicket如何在OSGi下工作比较繁琐,经过不断摸索,发现一种简化的方式,而且可以在wicket的程序类修改时不重新启动整个OSGi框架,只通过update就可以更新wicket程序,很方便。
        首先看一下jetty如何整合到OSGi中,建一个Bundle(wanged_core_jetty)写一个Jetty的类:
java 代码
 
  1. package wanged.core.jetty;  
  2.   
  3. import java.util.List;  
  4.   
  5. import org.mortbay.jetty.Handler;  
  6. import org.mortbay.jetty.Server;  
  7. import org.mortbay.jetty.handler.HandlerCollection;  
  8.   
  9. public class Jetty {  
  10.   
  11.     private Server server = new Server(8080);  
  12.   
  13.     private HandlerCollection handlerCollection;  
  14.   
  15.     private List<Handler> handlers;  
  16.   
  17.     public void start() {  
  18.   
  19.         if (handlers != null && handlers.size() > 0) {  
  20.             this.handlerCollection.setHandlers(handlers.toArray(new Handler[handlers.size()]));  
  21.         }  
  22.   
  23.         this.server.setHandler(handlerCollection);  
  24.         try {  
  25.             this.server.start();  
  26.         } catch (Exception e) {  
  27.             e.printStackTrace();  
  28.         }  
  29.     }  
  30.   
  31.     public void stop() {  
  32.         try {  
  33.             this.server.stop();  
  34.         } catch (Exception e) {  
  35.             e.printStackTrace();  
  36.         }  
  37.     }  
  38.   
  39.     public void setHandlerCollection(HandlerCollection handlerCollection) {  
  40.         this.handlerCollection = handlerCollection;  
  41.     }  
  42.   
  43.     /** 
  44.      * 设置要添加到HandlerCollection中的Handler 
  45.      *  
  46.      * @param handlers 
  47.      */  
  48.     public void setHandlers(List<Handler> handlers) {  
  49.         this.handlers = handlers;  
  50.     }  
  51.   
  52. }  
这个Bundle中就一个类,下面看配置文件:
xml 代码
 
  1. <osgi:service interface="org.mortbay.jetty.HandlerContainer" ref="handlerCollection" />  
  2.   
  3. <!-- Jetty访问日志 -->  
  4. <bean name="requestLog" class="org.mortbay.jetty.NCSARequestLog">  
  5.   <property name="filename" value="F:/TMP/yyyy_mm_dd.request.log" />  
  6.   <property name="filenameDateFormat" value="yyyy_MM_dd" />  
  7.   <property name="logDateFormat" value="yyyy-MM-dd HH:mm:ss" />  
  8.   <property name="logLatency" value="true" />  
  9.   <property name="extended" value="false" />  
  10. </bean>  
  11.   
  12. <!-- Jetty访问日志处理器 -->  
  13. <bean name="requestLogHandler" class="org.mortbay.jetty.handler.RequestLogHandler">  
  14.   <property name="requestLog" ref="requestLog" />  
  15. </bean>  
  16.   
  17. <!-- 处理器集合 -->  
  18. <bean name="handlerCollection" class="org.mortbay.jetty.handler.HandlerCollection" />  
  19.   
  20. <!-- Jetty服务器 -->  
  21. <bean name="jettyServer" class="wanged.core.jetty.Jetty" init-method="start" destroy-method="stop">  
  22.   <property name="handlerCollection" ref="handlerCollection" />  
  23.   <property name="handlers">  
  24.     <list>  
  25.       <ref bean="requestLogHandler" />  
  26.     </list>  
  27.   </property>  
  28. </bean>  
好了,这个已经搞定,目前Jetty可以单独启动了。
       下面来看wicket的bundle,中间涉及到4个主要类:
java 代码
 
  1. package wanged.web.wicket;  
  2.   
  3. import org.mortbay.jetty.HandlerContainer;  
  4. import org.mortbay.jetty.webapp.WebAppContext;  
  5. import org.springframework.beans.BeansException;  
  6. import org.springframework.context.ApplicationContext;  
  7. import org.springframework.context.ApplicationContextAware;  
  8.   
  9. public class WicketApplication implements ApplicationContextAware {  
  10.   
  11.     private WebAppContext ctx;  
  12.   
  13.     public WicketApplication(String webApp, String contextPath) {  
  14.         ctx = new WebAppContext(webApp, contextPath);  
  15.     }  
  16.   
  17.     public void start() throws Exception {  
  18.         ctx.start();  
  19.     }  
  20.   
  21.     public void stop() throws Exception {  
  22.         ctx.stop();  
  23.     }  
  24.   
  25.     /** 
  26.      * Jetty的HandlerContainer,用于注册web上下文 
  27.      *  
  28.      * @param handlerContainer 
  29.      */  
  30.     public void setHandlerContainer(HandlerContainer handlerContainer) {  
  31.         handlerContainer.addHandler(ctx);  
  32.     }  
  33.   
  34.     public void setApplicationContext(ApplicationContext context) throws BeansException {  
  35.         // 设置Spring的应用上下文参数  
  36.         ctx.setAttribute(ApplicationContext.class.getName(), context);  
  37.     }  
  38. }  
这个用来启动wicket的web应用,具体配置请看后面的配置文件。
        Wicket本身和Spring的整合很简单,这里借鉴了官方实现写了两个简单的类来处理:
java 代码
 
  1. package wanged.web.wicket;  
  2.   
  3. import java.lang.annotation.ElementType;  
  4. import java.lang.annotation.Retention;  
  5. import java.lang.annotation.RetentionPolicy;  
  6. import java.lang.annotation.Target;  
  7.   
  8. @Retention(RetentionPolicy.RUNTIME)  
  9. @Target( { ElementType.FIELD })  
  10. public @interface SpringBean {  
  11.   
  12.     /** 
  13.      * 被引用Bean的名称;如果没有指定 
  14.      *  
  15.      * @return name String 被引用Bean的名称 
  16.      */  
  17.     String name() default "";  
  18. }  
这个声明了一个原数据,你的Wicket组件中使用方法如下:
java 代码
 
  1. private static final class LoginForm extends Form {  
  2.         @SpringBean  
  3.         private transient RoleService roleService;  
  4.         ......  
  5. }  
这样就可以通过下面这个类,自动将Bean注入。
java 代码
 
  1. package wanged.web.wicket;  
  2.   
  3. import java.lang.reflect.Field;  
  4. import java.util.Collections;  
  5. import java.util.HashMap;  
  6. import java.util.Map;  
  7.   
  8. import org.apache.wicket.Component;  
  9. import org.apache.wicket.application.IComponentInstantiationListener;  
  10. import org.springframework.context.ApplicationContext;  
  11.   
  12. public class SpringComponentInjector implements IComponentInstantiationListener {  
  13.   
  14.     public final static String WICKET_PACKAGE = "org.apache.wicket";  
  15.   
  16.     private HashMap<Class<Component>, Map<Field, SpringBean>> AnnotationCacheMap = new HashMap<Class<Component>, Map<Field, SpringBean>>();  
  17.   
  18.     private ApplicationContext context;  
  19.   
  20.     /** 
  21.      * 添加Spring上下文环境,以便从配置文件中装配所需的bean 
  22.      *  
  23.      * @param context ApplicationContext Spring上下文环境 
  24.      */  
  25.     public SpringComponentInjector(ApplicationContext context) {  
  26.         this.context = context;  
  27.     }  
  28.   
  29.     @SuppressWarnings("unchecked")  
  30.     @Override  
  31.     public void onInstantiation(Component component) {  
  32.         Map<Field, SpringBean> fieldMap = getFieldMap((Class<Component>) component.getClass());  
  33.         if (fieldMap != null) {  
  34.             // 将Bean注入到对应的Field中  
  35.             for (Map.Entry<Field, SpringBean> entry : fieldMap.entrySet()) {  
  36.                 this.inject(entry.getKey(), component, entry.getValue());  
  37.             }  
  38.         }  
  39.     }  
  40.   
  41.     /** 
  42.      *  
  43.      * @param clazz 
  44.      * @return null 当clazz是wicket自身的类或者没有包含需要依赖注入的域时返回null 
  45.      */  
  46.     private final Map<Field, SpringBean> getFieldMap(Class<Component> clazz) {  
  47.   
  48.         // 判断clazz是否是wicket项目的类,即其类全称以org.apache.wicket开头  
  49.         if (clazz.getName().startsWith(WICKET_PACKAGE)) {  
  50.             return null;  
  51.         }  
  52.   
  53.         // 从缓存中获得  
  54.         synchronized (AnnotationCacheMap) {  
  55.             Map<Field, SpringBean> map = AnnotationCacheMap.get(clazz);  
  56.   
  57.             if (map == null) {  
  58.                 map = createFieldMap(clazz);  
  59.                 AnnotationCacheMap.put(clazz, map);  
  60.             }  
  61.             return map;  
  62.         }  
  63.   
  64.     }  
  65.   
  66.     private Map<Field, SpringBean> createFieldMap(Class<Component> clazz) {  
  67.         Map<Field, SpringBean> map = new HashMap<Field, SpringBean>();  
  68.   
  69.         for (Field field : clazz.getDeclaredFields()) {  
  70.             SpringBean beanAnon = field.getAnnotation(SpringBean.class);  
  71.             if (beanAnon != null) {  
  72.                 map.put(field, beanAnon);  
  73.             }  
  74.         }  
  75.   
  76.         // 该类中没有需要注入的域  
  77.         if (map.size() == 0) {  
  78.             return null;  
  79.         }  
  80.   
  81.         // 只读限制  
  82.         return Collections.unmodifiableMap(map);  
  83.     }  
  84.   
  85.     private final void inject(Field field, Object instance, SpringBean beanAnon) {  
  86.         Object o = this.getBean(field, beanAnon);  
  87.         if (o != null) {  
  88.             try {  
  89.                 if (field.isAccessible()) {  
  90.                     field.set(instance, o);  
  91.                 } else {  
  92.                     field.setAccessible(true);  
  93.                     field.set(instance, o);  
  94.                     field.setAccessible(false);  
  95.                 }  
  96.             } catch (IllegalArgumentException e) {  
  97.                 e.printStackTrace();  
  98.             } catch (IllegalAccessException e) {  
  99.                 e.printStackTrace();  
  100.             }  
  101.         }  
  102.     }  
  103.   
  104.     private final Object getBean(Field field, SpringBean beanAnon) {  
  105.         String beanName = beanAnon.name();  
  106.   
  107.         if ("".equals(beanName)) {  
  108.             beanName = field.getName();  
  109.         }  
  110.         return this.context.getBean(beanName);  
  111.     }  
  112.   
  113. }  
主要的类都已经有了,现在就缺每个wicket都需要的WebApplication类的子类了:
java 代码
 
  1. package wanged.web.wicket;  
  2.   
  3. import java.io.File;  
  4.   
  5. import org.apache.wicket.protocol.http.WebApplication;  
  6. import org.apache.wicket.session.ISessionStore;  
  7. import org.apache.wicket.util.lang.PackageName;  
  8. import org.springframework.context.ApplicationContext;  
  9.   
  10. import wanged.web.wicket.page.help.ErrorPage;  
  11. import wanged.web.wicket.page.login.Login;  
  12.   
  13. public class DefaultApplication extends WebApplication {  
  14.   
  15.     public static final String DISK_STORE_PATH = "DISK_STORE_PATH";  
  16.   
  17.     @SuppressWarnings("unchecked")  
  18.     @Override  
  19.     public Class getHomePage() {  
  20.         return Login.class;  
  21.     }  
  22.   
  23.     @Override  
  24.     protected void init() {  
  25.         ApplicationContext ac = (ApplicationContext) getServletContext().getAttribute(ApplicationContext.class.getName());  
  26.         this.addComponentInstantiationListener(new SpringComponentInjector(ac));  
  27.         this.mount("/login", PackageName.forClass(Login.class));  
  28.         this.mount("/help", PackageName.forClass(ErrorPage.class));  
  29.     }  
  30.   
  31.     @Override  
  32.     protected ISessionStore newSessionStore() {  
  33.         getServletContext().setAttribute("javax.servlet.context.tempdir"new File(getInitParameter("pageStorePath")));  
  34.         return super.newSessionStore();  
  35.     }  
  36.   
  37. }  
有了这些类,还需要配置文件将他们组合起来才能使用:
xml 代码
 
  1. <osgi:reference interface="org.mortbay.jetty.HandlerContainer" id="handlerContainer" />  
  2.   
  3. <bean id="wicketApp" class="wanged.web.wicket.WicketApplication" init-method="start" destroy-method="stop">  
  4.   <constructor-arg value="d:/Workspace/wanged_wicket_app/webapp" />  
  5.   <constructor-arg value="/" />  
  6.   <property name="handlerContainer" ref="handlerContainer" />  
  7. </bean>  
有了上面的配置还需要一点,就是我们的web应用的根目录d:/Workspace/wanged_wicket_app/webapp,在这里我们放置静态文件和web.xml:
xml 代码
 
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee"  
  3.   xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"  
  4.   xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"  
  5.   version="2.5">  
  6.   
  7.   <filter>  
  8.     <filter-name>gzipFilter</filter-name>  
  9.     <filter-class>org.mortbay.servlet.GzipFilter</filter-class>  
  10.   </filter>  
  11.   
  12.   <filter>  
  13.     <filter-name>wicketFilter</filter-name>  
  14.     <filter-class>org.apache.wicket.protocol.http.WicketFilter</filter-class>  
  15.     <init-param>  
  16.       <param-name>applicationClassName</param-name>  
  17.       <param-value>wanged.web.wicket.DefaultApplication</param-value>  
  18.     </init-param>  
  19.     <init-param>  
  20.       <param-name>pageStorePath</param-name>  
  21.       <param-value>F:/TMP</param-value>  
  22.     </init-param>  
  23.     <init-param>  
  24.       <param-name>configuration</param-name>  
  25.       <param-value>development</param-value>  
  26.     </init-param>  
  27.   </filter>  
  28.   
  29.   <filter-mapping>  
  30.     <filter-name>gzipFilter</filter-name>  
  31.     <url-pattern>/*</url-pattern>  
  32.   </filter-mapping>  
  33.   
  34.   <filter-mapping>  
  35.     <filter-name>wicketFilter</filter-name>  
  36.     <url-pattern>/wkt/*</url-pattern>  
  37.   </filter-mapping>  
  38.   
  39. </web-app>  

好了,现在就自己写一个Wicket页面尝试一下吧^_^
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics