设为首页 收藏本站
查看: 505|回复: 0

[经验分享] Wicket如何通过OSGi框架注入Jetty

[复制链接]

尚未签到

发表于 2017-2-26 13:29:46 | 显示全部楼层 |阅读模式
       Wicket框架的优点就不多说了,总之第一次看到她就让人耳目一新。我的框架就是采用OSGi、Spring、Hibernate、Wicket、Jetty等开源技术搭建的,所以这篇文章就是我在将Wicket通过OSGi框架注入Jetty中的一点心得体会。目前wicket刚刚推出1.3Beta3版,由于1.3版和2.0版比较接近,所以采用1.3版来测试。
        首先介绍一下我的整体思路,将Jetty和wicket分别作为OSGi的Bundle,对外提供服务;自己的基于wicket的web应用也作为一个Bundle。另外因为web应用中要用到其它Bundle提供的服务,当然采用IOC方式最方便,所以需要wicket和spring整合一下。
        先说Jetty和Wicket这两个Bundle,其实Jetty6.1.5已经兼容OSGi,它的jar包已经可以直接作为Bundle使用,我们目前涉及到的就是其安装目录下的lib/jetty-6.1.5.jar,lib/jetty-util-6.1.5.jar和lib/servlet-api-2.5.6.1.5.jar这三个jar包。前两个是jetty作为嵌入式服务器所必需的,最后一个是jetty的servlet实现。
       因为我们采用编程式处理web应用,所以需要将web应用的信息注入到Jetty中。那么为什么不使用配置文件如web.xml呢?因为如果使用配置文件就要解决两个问题:
       第一是绝对路径问题,OSGi的Bundle并没有自己固定部署的位置,所以部署时绝对路径可以任选,这样如果采用配置文件声明web应用,那么配置文件就要随着Bundle位置变更而变更,一般Bundle都是jar包的形式保存,势必导致修改的困难。
       第二是类加载问题,因为我们要使用IOC和其他Bundle声明的服务,如果采用配置文件方式,类的初始化都由ClassLoader直接处理,因此上面所说的很难办到。   
        采用编程式处理web应用,就需要建一个Bundle(wanged_core_jetty),用来处理其他Bundle提供的Handler(这个jetty中的主要部件,可以参看Jetty 6.1.5的配置一文)。先看配置文件bean.xml:
xml 代码
 

  • <bean id="jettyHandlerManager" class="wanged.core.jetty.JettyHandlerManager">  
  •   <property name="handlers" ref="handlers" />  
  • </bean>  
  •   
  • <bean name="jettyServer" class="wanged.core.jetty.JettyServer" init-method="start" destroy-method="stop" />  

以及osgi-service.xml:
xml 代码
 

  • <!-- 服务 和 引用 -->  
  • <osgi:reference id="handlers" interface="org.mortbay.jetty.Handler" cardinality="1..n">  
  •   <osgi:listener ref="jettyServer" />  
  • </osgi:reference>  

这里引用了其他Bundle提供的接口为org.mortbay.jetty.Handler的服务,并注册一个监听器jettyServer来处理Handler的动态绑定与删除问题,以便可以动态添加/删除web应用。下面看看具体代码:
java 代码
 

  • package wanged.core.jetty;  
  •   
  • import java.util.Dictionary;  
  •   
  • import org.mortbay.jetty.Handler;  
  • import org.mortbay.jetty.Server;  
  • import org.mortbay.jetty.handler.HandlerCollection;  
  • import org.springframework.osgi.service.TargetSourceLifecycleListener;  
  •   
  • /** 
  •  * 启动Jetty服务器,监听Handler的添加与删除,并动态更新服务器的Handler 
  •  * @author wanged 
  •  * 
  •  */  
  •   
  • @SuppressWarnings("unchecked")  
  • public class JettyServer implements TargetSourceLifecycleListener{  
  •       
  •     private Server server = new Server(8080);  
  •       
  •     private HandlerCollection handlers = new HandlerCollection();  
  •       
  •     public void start(){  
  •         this.server.setHandler(this.handlers);  
  •         try {  
  •             this.server.start();  
  •         } catch (Exception e) {  
  •             e.printStackTrace();  
  •         }  
  •     }  
  •       
  •     /** 
  •      * 绑定新的Handler,并启动之 
  •      */  
  •     public void bind(Object obj, Dictionary dict) throws Exception {  
  •         Handler h = (Handler)obj;  
  •         this.handlers.addHandler(h);  
  •         h.start();  
  •     }  
  •       
  •     /** 
  •      * 删除Handler,并停止之 
  •      */  
  •     public void unbind(Object obj, Dictionary dict) throws Exception {  
  •         Handler h = (Handler)obj;  
  •         this.handlers.removeHandler(h);  
  •         h.stop();  
  •     }  
  •       
  •     public void stop(){  
  •         try {  
  •             this.server.stop();  
  •         } catch (Exception e) {  
  •             e.printStackTrace();  
  •         }  
  •     }  
  •   
  • }  

实现接口TargetSourceLifecycleListener,监听服务的绑定与删除。另一个类如下:
java 代码

  • package wanged.core.jetty;  
  •   
  • import java.util.Collection;  
  •   
  • import org.mortbay.jetty.Handler;  
  •   
  • /** 
  •  * Jetty服务器Handler的管理器,用来动态管理作为服务注册的Handler 
  •  * @author wanged 
  •  * 
  •  */  
  • public class JettyHandlerManager {  
  •     private Collection<Handler> handlers;  
  •   
  •     public void setHandlers(Collection<Handler> handlers) {  
  •         this.handlers = handlers;  
  •     }  
  •   
  • }  

这个类只是简单声明了一个setHandlers方法,功能可以扩展。到此为止Jetty的相关工作已经处理完了。
        下面该说说web应用了,我的web应用放在一个叫wanged_wicket_app的Bundle中。涉及到的类比较多,所以现将类列出再一一解说。首先看SpringWicketFilter:
java 代码

  • package wanged.web.wicket;  
  •   
  • import org.apache.wicket.protocol.http.IWebApplicationFactory;  
  • import org.apache.wicket.protocol.http.WicketFilter;  
  •   
  • public class SpringWicketFilter extends WicketFilter{  
  •     private IWebApplicationFactory factory ;  
  •       
  •     public IWebApplicationFactory getApplicationFactory(){  
  •         return factory;  
  •     }  
  •           
  •     public void setApplicationFactory(IWebApplicationFactory factory){  
  •         this.factory = factory;  
  •     }  
  •       
  • }  

为什么不直接用WicketFilter呢,因为WicketFilter需要有一个applicationClass的参数需要定义在init-param中,需要解析web.xml才能得到,而且生成的WebApplication不能使用IOC功能。熟悉Wicket的朋友会问,Wicket不是已经和Spring整合了么?是的,确实整合了,但这种整合无法用在OSGi环境下,所以我们需要自定义一个WicketFilter的子类来声明一个注入了Spring上下文的IWebApplicationFactory的实现SpringWebApplicationFactory:
java 代码
 

  • package wanged.web.wicket;  
  •   
  • import org.apache.wicket.protocol.http.IWebApplicationFactory;  
  • import org.apache.wicket.protocol.http.WebApplication;  
  • import org.apache.wicket.protocol.http.WicketFilter;  
  • import org.springframework.beans.BeansException;  
  • import org.springframework.context.ApplicationContext;  
  • import org.springframework.context.ApplicationContextAware;  
  •   
  • public class SpringWebApplicationFactory implements IWebApplicationFactory, ApplicationContextAware {  
  •     private WebApplication webApplication;  
  •   
  •     private ApplicationContext context;  
  •   
  •     private String diskStorePath;  
  •   
  •     public void init() {  
  •         DefaultApplication app = new DefaultApplication();  
  •         app.setApplicationContext(context);  
  •         app.setDiskStorePath(this.diskStorePath);  
  •         this.webApplication = app;  
  •     }  
  •   
  •     public WebApplication createApplication(WicketFilter filter) {  
  •         return webApplication;  
  •     }  
  •   
  •     public void setApplicationContext(ApplicationContext context) throws BeansException {  
  •         this.context = context;  
  •     }  
  •       
  •     public void setDiskStorePath(String path){  
  •         this.diskStorePath = path;  
  •     }  
  • }  

这里引用了一个DefaultApplication,这是WebApplication的子类,在Wicket应用中有至关重要的作用,每一个wicket的web应用都需要自定义一个:
java 代码
 

  • package wanged.web.wicket;  
  •   
  • import java.io.File;  
  •   
  • import org.apache.wicket.protocol.http.SecondLevelCacheSessionStore;  
  • import org.apache.wicket.protocol.http.WebApplication;  
  • import org.apache.wicket.protocol.http.pagestore.DiskPageStore;  
  • import org.apache.wicket.session.ISessionStore;  
  • import org.apache.wicket.spring.injection.annot.SpringComponentInjector;  
  • import org.apache.wicket.util.lang.Bytes;  
  • import org.springframework.beans.BeansException;  
  • import org.springframework.context.ApplicationContext;  
  • import org.springframework.context.ApplicationContextAware;  
  •   
  • import wanged.web.wicket.login.LoginPage;  
  •   
  • public class DefaultApplication extends WebApplication implements ApplicationContextAware {  
  •     private ApplicationContext context;  
  •     private String diskStorePath;  
  •   
  •       
  •     @SuppressWarnings("unchecked")  
  •     @Override  
  •     public Class getHomePage() {  
  •         return LoginPage.class;  
  •     }  
  •   
  •     @Override  
  •     public void init() {  
  •         super.init();  
  •         addComponentInstantiationListener(new SpringComponentInjector(this, this.context));  
  •     }  
  •   
  •     public void setApplicationContext(ApplicationContext context) throws BeansException {  
  •         this.context = context;  
  •     }  
  •   
  •     protected ISessionStore newSessionStore() {  
  •         return new SecondLevelCacheSessionStore(this, new DiskPageStore(new File(this.diskStorePath), (int) Bytes.megabytes(10).bytes(), (int) Bytes.megabytes(  
  •                         100).bytes(), 25));  
  •     }  
  •       
  •     public void setDiskStorePath(String path){  
  •         this.diskStorePath = path;  
  •     }  
  • }  

由于Wicket1.3开始采用Filter处理Request,不再使用Servlet,所以我们的环境中只需要配置Filter的相关信息即可。为了能采用IOC方式配置Filter及其映射,引入一个新类FilterMapping:
java 代码
 

  • package wanged.web.wicket;  
  •   
  • import javax.servlet.Filter;  
  •   
  • public class FilterMapping {  
  •     private Class<Filter> filterClass;  
  •   
  •     private Filter filter;  
  •   
  •     private String urlPattern = "/*";  
  •   
  •     public Class<Filter> getFilterClass() {  
  •         return filterClass;  
  •     }  
  •   
  •     public void setFilterClass(Class<Filter> filterClass) {  
  •         this.filterClass = filterClass;  
  •     }  
  •   
  •     public Filter getFilter() {  
  •         return filter;  
  •     }  
  •   
  •     public void setFilter(Filter filter) {  
  •         this.filter = filter;  
  •     }  
  •   
  •     public String getUrlPattern() {  
  •         return urlPattern;  
  •     }  
  •   
  •     public void setUrlPattern(String urlPattern) {  
  •         this.urlPattern = urlPattern;  
  •     }  
  •   
  • }  

内容很简单,不多废话了。到目前为止,我们只是定义了Wicket相关的类文件,如何注入到Jetty中呢?接下来就是最主要的了:
java 代码

  • package wanged.web;  
  •   
  • import java.util.HashMap;  
  • import java.util.List;  
  • import java.util.Map;  
  •   
  • import org.mortbay.jetty.Handler;  
  • import org.mortbay.jetty.servlet.Context;  
  • import org.mortbay.jetty.servlet.DefaultServlet;  
  • import org.mortbay.jetty.servlet.FilterHolder;  
  • import org.mortbay.jetty.servlet.SessionHandler;  
  •   
  • import wanged.web.wicket.FilterMapping;  
  •   
  • public class ContextFactory {  
  •   
  •     private HashMap<String, String> attributes;  
  •   
  •     private List<FilterMapping> filterMappings;  
  •   
  •     private Context ctx = new Context();;  
  •   
  •     public Context newInstance() {  
  •         return ctx;  
  •     }  
  •   
  •     public void init() {  
  •         if (this.attributes != null) {  
  •             for (Map.Entry<String, String> entry : this.attributes.entrySet()) {  
  •                 ctx.setAttribute(entry.getKey(), entry.getValue());  
  •             }  
  •         }  
  •         ctx.setSessionHandler(new SessionHandler());  
  •         ctx.addServlet(DefaultServlet.class, "/");  
  •   
  •         for (FilterMapping fm : filterMappings) {  
  •             ctx.addFilter(new FilterHolder(fm.getFilter()), fm.getUrlPattern(), Handler.REQUEST);  
  •         }  
  •     }  
  •   
  •     public void setFilterMappings(List<FilterMapping> filterMappings) {  
  •         this.filterMappings = filterMappings;  
  •     }  
  •     public void setAttributes(HashMap<String, String> attributes) {  
  •         this.attributes = attributes;  
  •     }  
  •   
  •     public void setContextPath(String contextPath) {  
  •         this.ctx.setContextPath(contextPath);  
  •     }  
  •   
  • }  

这个类负责收集我们配置文件中的bean,然后打包成一个Context作为一个对外声明的Handler服务(注意org.mortbay.jetty.servlet.Context实现了org.mortbay.jetty.Handler接口)。
下面看配置文件web-bean.xml:
xml 代码

  • <?xml version="1.0" encoding="UTF-8"?>  
  • <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  •   xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">  
  •   
  •   <!-- 定义服务实现 -->  
  •   <bean id="handler" factory-bean="contextFactory" factory-method="newInstance" />  
  •   
  •   <!-- Jetty的处理器工厂,用来向Jetty添加处理器 -->  
  •   <bean id="contextFactory" class="wanged.web.ContextFactory" init-method="init">  
  •     <property name="contextPath" value="/" />  
  •     <property name="filterMappings">  
  •       <!-- Filter执行顺序预定顺序相关 -->  
  •       <list>  
  •         <ref bean="gzipFilterMapping" />  
  •         <ref bean="wicketFilterMapping" />  
  •       </list>  
  •     </property>  
  •   </bean>  
  •   
  •   <bean id="gzipFilter" class="org.mortbay.servlet.GzipFilter" />  
  •   
  •   <bean id="wicketFilter" class="wanged.web.wicket.SpringWicketFilter">  
  •     <property name="applicationFactory" ref="applicationFactory" />  
  •   </bean>  
  •   
  •   <bean id="wicketFilterMapping" class="wanged.web.wicket.FilterMapping">  
  •     <property name="filter" ref="wicketFilter" />  
  •   </bean>  
  •   
  •   <bean id="gzipFilterMapping" class="wanged.web.wicket.FilterMapping">  
  •     <property name="filter" ref="gzipFilter" />  
  •   </bean>  
  •   
  •   <bean id="applicationFactory" class="wanged.web.wicket.SpringWebApplicationFactory" init-method="init" >  
  •     <property name="diskStorePath" value="F:/TMP" />  
  •   </bean>  
  •   
  • </beans>  

这里定义了我们所使用到的各种类,同时SpringWebApplicationFactory在此获得Spring上下文信息。
另一个声明服务的配置文件osgi-service.xml:
xml 代码
 

  • <osgi:service interface="org.mortbay.jetty.Handler" ref="handler" />  
  •   
  • <osgi:reference id="roleService" interface="wanged.security.service.RoleService" />  

这里的roleService是引用了外部的一个服务,用来测试Sping注入的(可以用一个在web-bean.xml中定义的bean来代替测试)。
以上已经完成了wicket的web应用注入jetty的工作,最后就是写一个LoginPage.java完成整个Bundle:
java 代码
 

  • package wanged.web.wicket.login;  
  •   
  • import org.apache.wicket.ajax.AjaxRequestTarget;  
  • import org.apache.wicket.ajax.markup.html.form.AjaxButton;  
  • import org.apache.wicket.markup.html.WebPage;  
  • import org.apache.wicket.markup.html.form.Form;  
  • import org.apache.wicket.markup.html.form.PasswordTextField;  
  • import org.apache.wicket.markup.html.form.TextField;  
  • import org.apache.wicket.model.CompoundPropertyModel;  
  • import org.apache.wicket.model.IModel;  
  •   
  • import wanged.security.entity.Role;  
  • import wanged.security.service.RoleService;  
  • import org.apache.wicket.spring.injection.annot.SpringBean;  
  • import org.mortbay.jetty.Handler;  
  •   
  • public class LoginPage extends WebPage {  
  •   
  •     private LoginUser user;  
  •   
  •     public LoginPage() {  
  •         super();  
  •         this.user = new LoginUser();  
  •   
  •         Form form = new AutoSaveForm("loginForm", new CompoundPropertyModel(user));  
  •         add(form);  
  •         form.add(new TextField("username"));  
  •         form.add(new PasswordTextField("password"));  
  •     }  
  •   
  • }  
  •   
  • class AutoSaveForm extends Form {  
  •   
  •     /* 这里就是使用IOC的地方, 
  •         试想一下如果没有IOC,我们如何让自定义的类来获得其他Bean呢 
  •     */  
  •     @SpringBean  
  •     RoleService roleService;  
  •       
  •     AutoSaveForm(String id, IModel m) {  
  •         super(id, m);  
  •         AjaxButton button = new AjaxButton("button", this) {  
  •             @Override  
  •             protected void onSubmit(AjaxRequestTarget target, Form form) {  
  •                 LoginUser user = (LoginUser)form.getModelObject();  
  •                 System.out.println("un: " + user.getUsername());  
  •                 System.out.println("pd: " + user.getPassword());  
  •             }  
  •         };  
  •         this.add(button);  
  •     }  
  • }  


        运行一下一切OK。感觉好繁琐呀!确实如此,OSGi带给我们的是基于组件的面向服务的开发,用来做小项目确实有点大炮打蚊子的感觉,但如果是做大型项目,就能充分体现出组件和SOA的优势。

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-347507-1-1.html 上篇帖子: Java学习——Jetty编程使用HttpClient 下篇帖子: Jetty in GWT-DEV Form too large
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表