之前写的文章
Wicket如何通过OSGi框架注入Jetty 中关于wicket如何在OSGi下工作比较繁琐,经过不断摸索,发现一种简化的方式,而且可以在wicket的程序类修改时不重新启动整个OSGi框架,只通过update就可以更新wicket程序,很方便。
首先看一下jetty如何整合到OSGi中,建一个Bundle(wanged_core_jetty)写一个Jetty的类:
java 代码
- package wanged.core.jetty;
-
- import java.util.List;
-
- import org.mortbay.jetty.Handler;
- import org.mortbay.jetty.Server;
- import org.mortbay.jetty.handler.HandlerCollection;
-
- public class Jetty {
-
- private Server server = new Server(8080);
-
- private HandlerCollection handlerCollection;
-
- private List<Handler> handlers;
-
- public void start() {
-
- if (handlers != null && handlers.size() > 0) {
- this.handlerCollection.setHandlers(handlers.toArray(new Handler[handlers.size()]));
- }
-
- this.server.setHandler(handlerCollection);
- try {
- this.server.start();
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
-
- public void stop() {
- try {
- this.server.stop();
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
-
- public void setHandlerCollection(HandlerCollection handlerCollection) {
- this.handlerCollection = handlerCollection;
- }
-
-
-
-
-
-
- public void setHandlers(List<Handler> handlers) {
- this.handlers = handlers;
- }
-
- }
这个Bundle中就一个类,下面看配置文件:
xml 代码
- <osgi:service interface="org.mortbay.jetty.HandlerContainer" ref="handlerCollection" />
-
-
- <bean name="requestLog" class="org.mortbay.jetty.NCSARequestLog">
- <property name="filename" value="F:/TMP/yyyy_mm_dd.request.log" />
- <property name="filenameDateFormat" value="yyyy_MM_dd" />
- <property name="logDateFormat" value="yyyy-MM-dd HH:mm:ss" />
- <property name="logLatency" value="true" />
- <property name="extended" value="false" />
- </bean>
-
-
- <bean name="requestLogHandler" class="org.mortbay.jetty.handler.RequestLogHandler">
- <property name="requestLog" ref="requestLog" />
- </bean>
-
-
- <bean name="handlerCollection" class="org.mortbay.jetty.handler.HandlerCollection" />
-
-
- <bean name="jettyServer" class="wanged.core.jetty.Jetty" init-method="start" destroy-method="stop">
- <property name="handlerCollection" ref="handlerCollection" />
- <property name="handlers">
- <list>
- <ref bean="requestLogHandler" />
- </list>
- </property>
- </bean>
好了,这个已经搞定,目前Jetty可以单独启动了。
下面来看wicket的bundle,中间涉及到4个主要类:
java 代码
- package wanged.web.wicket;
-
- import org.mortbay.jetty.HandlerContainer;
- import org.mortbay.jetty.webapp.WebAppContext;
- import org.springframework.beans.BeansException;
- import org.springframework.context.ApplicationContext;
- import org.springframework.context.ApplicationContextAware;
-
- public class WicketApplication implements ApplicationContextAware {
-
- private WebAppContext ctx;
-
- public WicketApplication(String webApp, String contextPath) {
- ctx = new WebAppContext(webApp, contextPath);
- }
-
- public void start() throws Exception {
- ctx.start();
- }
-
- public void stop() throws Exception {
- ctx.stop();
- }
-
-
-
-
-
-
- public void setHandlerContainer(HandlerContainer handlerContainer) {
- handlerContainer.addHandler(ctx);
- }
-
- public void setApplicationContext(ApplicationContext context) throws BeansException {
-
- ctx.setAttribute(ApplicationContext.class.getName(), context);
- }
- }
这个用来启动wicket的web应用,具体配置请看后面的配置文件。
Wicket本身和Spring的整合很简单,这里借鉴了官方实现写了两个简单的类来处理:
java 代码
- package wanged.web.wicket;
-
- import java.lang.annotation.ElementType;
- import java.lang.annotation.Retention;
- import java.lang.annotation.RetentionPolicy;
- import java.lang.annotation.Target;
-
- @Retention(RetentionPolicy.RUNTIME)
- @Target( { ElementType.FIELD })
- public @interface SpringBean {
-
-
-
-
-
-
- String name() default "";
- }
这个声明了一个原数据,你的Wicket组件中使用方法如下:
java 代码
- private static final class LoginForm extends Form {
- @SpringBean
- private transient RoleService roleService;
- ......
- }
这样就可以通过下面这个类,自动将Bean注入。
java 代码
- package wanged.web.wicket;
-
- import java.lang.reflect.Field;
- import java.util.Collections;
- import java.util.HashMap;
- import java.util.Map;
-
- import org.apache.wicket.Component;
- import org.apache.wicket.application.IComponentInstantiationListener;
- import org.springframework.context.ApplicationContext;
-
- public class SpringComponentInjector implements IComponentInstantiationListener {
-
- public final static String WICKET_PACKAGE = "org.apache.wicket";
-
- private HashMap<Class<Component>, Map<Field, SpringBean>> AnnotationCacheMap = new HashMap<Class<Component>, Map<Field, SpringBean>>();
-
- private ApplicationContext context;
-
-
-
-
-
-
- public SpringComponentInjector(ApplicationContext context) {
- this.context = context;
- }
-
- @SuppressWarnings("unchecked")
- @Override
- public void onInstantiation(Component component) {
- Map<Field, SpringBean> fieldMap = getFieldMap((Class<Component>) component.getClass());
- if (fieldMap != null) {
-
- for (Map.Entry<Field, SpringBean> entry : fieldMap.entrySet()) {
- this.inject(entry.getKey(), component, entry.getValue());
- }
- }
- }
-
-
-
-
-
-
- private final Map<Field, SpringBean> getFieldMap(Class<Component> clazz) {
-
-
- if (clazz.getName().startsWith(WICKET_PACKAGE)) {
- return null;
- }
-
-
- synchronized (AnnotationCacheMap) {
- Map<Field, SpringBean> map = AnnotationCacheMap.get(clazz);
-
- if (map == null) {
- map = createFieldMap(clazz);
- AnnotationCacheMap.put(clazz, map);
- }
- return map;
- }
-
- }
-
- private Map<Field, SpringBean> createFieldMap(Class<Component> clazz) {
- Map<Field, SpringBean> map = new HashMap<Field, SpringBean>();
-
- for (Field field : clazz.getDeclaredFields()) {
- SpringBean beanAnon = field.getAnnotation(SpringBean.class);
- if (beanAnon != null) {
- map.put(field, beanAnon);
- }
- }
-
-
- if (map.size() == 0) {
- return null;
- }
-
-
- return Collections.unmodifiableMap(map);
- }
-
- private final void inject(Field field, Object instance, SpringBean beanAnon) {
- Object o = this.getBean(field, beanAnon);
- if (o != null) {
- try {
- if (field.isAccessible()) {
- field.set(instance, o);
- } else {
- field.setAccessible(true);
- field.set(instance, o);
- field.setAccessible(false);
- }
- } catch (IllegalArgumentException e) {
- e.printStackTrace();
- } catch (IllegalAccessException e) {
- e.printStackTrace();
- }
- }
- }
-
- private final Object getBean(Field field, SpringBean beanAnon) {
- String beanName = beanAnon.name();
-
- if ("".equals(beanName)) {
- beanName = field.getName();
- }
- return this.context.getBean(beanName);
- }
-
- }
主要的类都已经有了,现在就缺每个wicket都需要的WebApplication类的子类了:
java 代码
- package wanged.web.wicket;
-
- import java.io.File;
-
- import org.apache.wicket.protocol.http.WebApplication;
- import org.apache.wicket.session.ISessionStore;
- import org.apache.wicket.util.lang.PackageName;
- import org.springframework.context.ApplicationContext;
-
- import wanged.web.wicket.page.help.ErrorPage;
- import wanged.web.wicket.page.login.Login;
-
- public class DefaultApplication extends WebApplication {
-
- public static final String DISK_STORE_PATH = "DISK_STORE_PATH";
-
- @SuppressWarnings("unchecked")
- @Override
- public Class getHomePage() {
- return Login.class;
- }
-
- @Override
- protected void init() {
- ApplicationContext ac = (ApplicationContext) getServletContext().getAttribute(ApplicationContext.class.getName());
- this.addComponentInstantiationListener(new SpringComponentInjector(ac));
- this.mount("/login", PackageName.forClass(Login.class));
- this.mount("/help", PackageName.forClass(ErrorPage.class));
- }
-
- @Override
- protected ISessionStore newSessionStore() {
- getServletContext().setAttribute("javax.servlet.context.tempdir", new File(getInitParameter("pageStorePath")));
- return super.newSessionStore();
- }
-
- }
有了这些类,还需要配置文件将他们组合起来才能使用:
xml 代码
- <osgi:reference interface="org.mortbay.jetty.HandlerContainer" id="handlerContainer" />
-
- <bean id="wicketApp" class="wanged.web.wicket.WicketApplication" init-method="start" destroy-method="stop">
- <constructor-arg value="d:/Workspace/wanged_wicket_app/webapp" />
- <constructor-arg value="/" />
- <property name="handlerContainer" ref="handlerContainer" />
- </bean>
有了上面的配置还需要一点,就是我们的web应用的根目录
d:/Workspace/wanged_wicket_app/webapp,在这里我们放置静态文件和web.xml:
xml 代码
- <?xml version="1.0" encoding="UTF-8"?>
- <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee"
- xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
- xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
- version="2.5">
-
- <filter>
- <filter-name>gzipFilter</filter-name>
- <filter-class>org.mortbay.servlet.GzipFilter</filter-class>
- </filter>
-
- <filter>
- <filter-name>wicketFilter</filter-name>
- <filter-class>org.apache.wicket.protocol.http.WicketFilter</filter-class>
- <init-param>
- <param-name>applicationClassName</param-name>
- <param-value>wanged.web.wicket.DefaultApplication</param-value>
- </init-param>
- <init-param>
- <param-name>pageStorePath</param-name>
- <param-value>F:/TMP</param-value>
- </init-param>
- <init-param>
- <param-name>configuration</param-name>
- <param-value>development</param-value>
- </init-param>
- </filter>
-
- <filter-mapping>
- <filter-name>gzipFilter</filter-name>
- <url-pattern>/*</url-pattern>
- </filter-mapping>
-
- <filter-mapping>
- <filter-name>wicketFilter</filter-name>
- <url-pattern>/wkt/*</url-pattern>
- </filter-mapping>
-
- </web-app>
好了,现在就自己写一个Wicket页面尝试一下吧^_^