在這邊要針對Wicket做一些處理才能讓Wicket知道Bundle class & resource
Wicket 的WebApplication.mountBookmarkablePage("/home",home.class)
會mount一個page , 在localhost:8080/home
但home page在OSgi Bundle裡時,wicket是沒辦法認得的
這邊要做一些處理才能達成
首先建立一個Project OSGiWebApp2.Interface
MANIFEST.mf 檔
Manifest-Version: 1.0 Export-Package: com.osgiweb.apps2.OSGiWebApp2.Interface;version="1.0.0 ";uses:="org.apache.wicket.markup.html.panel,org.apache.wicket.reques t.target.coding,org.apache.wicket.util.lang" Unversioned-Imports: * Built-By: momo Tool: Bnd-1.15.0 Bundle-Name: Spring OSGi Bundle Created-By: Apache Maven Bundle Plugin Bundle-Version: 1.0.0 Build-Jdk: 1.6.0_24 Bnd-LastModified: 1304487240640 Bundle-ManifestVersion: 2 Import-Package: org.apache.wicket.markup.html.panel,org.apache.wicket. request.target.coding,org.apache.wicket.util.lang Bundle-SymbolicName: com.osgiweb.apps2.OSGiWebApp2.Interface
IWicketPageService.java
package com.osgiweb.apps2.OSGiWebApp2.Interface; import org.apache.wicket.Page; import org.apache.wicket.request.target.coding.IRequestTargetUrlCodingStrategy; import org.apache.wicket.util.lang.PackageName; public interface IWicketPageService { /** * Mounts an encoder at the given path. * * @param encoder * the encoder that will be used for this mount */ void mount(IRequestTargetUrlCodingStrategy encoder); /** * Mounts all bookmarkable pages at the given path. * * @param path * the path to mount the bookmarkable page class on * @param packageName * the name of the package for which all bookmarkable pages or * sharedresources should be mounted */ void mount(final String path, final PackageName packageName); /** * Mounts a bookmarkable page class to the given path. * * @param <T> * type of page * * @param path * the path to mount the bookmarkable page class on * @param bookmarkablePageClass * the bookmarkable page class to mount */ <T extends Page> void mountBookmarkablePage(final String path, final Class<T> bookmarkablePageClass); /** * Mounts a bookmarkable page class to the given pagemap and path. * * @param <T> * type of page * * @param path * the path to mount the bookmarkable page class on * @param pageMapName * name of the pagemap this mount is for * @param bookmarkablePageClass * the bookmarkable page class to mount */ <T extends Page> void mountBookmarkablePage(final String path, final String pageMapName, final Class<T> bookmarkablePageClass); /** * Mounts a shared resource class to the given path. * * @param path * the path to mount the resource class on * @param resourceKey * the shared key of the resource being mounted */ void mountSharedResource(final String path, final String resourceKey); /** * Partly unmounts/ignores a path that normally would map to another mount * path. Like mount("/mypage", MyPage.class); and then "/mypage/arealdir" * should be ignored. This can be done by calling * unMount("/mypage/arealdir"); * * @param path * the path that should be ignored. * */ void addIgnoreMountPath(String path); /** * Unmounts whatever encoder is mounted at a given path. * * @param path * the path of the encoder to unmount */ void unmount(String path); }
然後再來建立一個Wicket Page Bundle Project, OSGiWebApp2.HomePages,
主要是存放一個Wicket Page
Activator.java , 這邊要參考OSGi Service by Interface IWicketPageService
透過這個service 通知WebApplication Bundle 我們mount 了一個bookmarkpage
package com.osgiweb.apps2.OSGiWebApp2.HomePages; import org.osgi.framework.BundleContext; import org.springframework.osgi.context.BundleContextAware; import com.osgiweb.apps2.OSGiWebApp2.Interface.IWicketPageService; public class Activator implements BundleContextAware { private BundleContext bundleCtx; IWicketPageService wicketService; public void start(){ //ountBookmarkablePage(context, "page1", MyPage1.class); System.out.println("bundle stat "); wicketService.mountBookmarkablePage( "home2", home2.class); } public void stop(){ } public void setWicketService(IWicketPageService wicketService) { System.out.println("wicketService =" wicketService); this.wicketService = wicketService; } public void setBundleContext(BundleContext bundleContext) { System.out.println("bundleCtx =" bundleCtx); this.bundleCtx = bundleContext; } }
hom2.java
package com.osgiweb.apps2.OSGiWebApp2.HomePages; import org.apache.wicket.markup.html.WebPage; import org.apache.wicket.markup.html.basic.Label; public class home2 extends WebPage { // private IuserDaoImp dao = new IuserDaoImp(); /** * Constructor */ public home2() { System.out.println("test for remort home2"); // add(new Label("message", "Hello OSGi-land! ffff" dao.selectUserName())); add(new Label("message","Hello")); } }
home2.html
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <title>Wickety OSGi!</title> </head> <body> Replace SSS <p wicket:id="message">replace me</p> </body> </html>
bundle-context-osgi.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" xmlns:osgi="http://www.springframework.org/schema/osgi" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/osgi http://www.springframework.org/schema/osgi/spring-osgi.xsd"> <!-- definitions using elements of the osgi namespace can be included in this file. There is no requirement to keep these definitions in a separate file if you do not want to. The rationale for keeping these definitions separate is to facilitate integration testing of the bundle outside of an OSGi container --> <osgi:reference id="iwicketService" interface="com.osgiweb.apps2.OSGiWebApp2.Interface.IWicketPageService"/> </beans>
bundle-context.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"> <!-- regular spring configuration file defining the beans for this bundle. The configuration of OSGi definitions is kept in a separate configuration file so that this file can easily be used for integration testing outside of an OSGi environment --> <bean id="actavitor" class="com.osgiweb.apps2.OSGiWebApp2.HomePages.Activator" init-method="start" destroy-method="stop"> <property name="wicketService" ref="iwicketService" /> </bean> </beans>
MANIFEST.MF
Manifest-Version: 1.0 Export-Package: com.osgiweb.apps2.OSGiWebApp2.HomePages;version="1.0.0"; uses:="com.osgiweb.apps2.OSGiWebApp2.Interface, org.apache.wicket.markup.html, org.osgi.framework, org.springframework.osgi.context" Unversioned-Imports: * Built-By: momo Tool: Bnd-1.15.0 Bundle-Name: Spring OSGi Bundle Created-By: Apache Maven Bundle Plugin Bundle-Version: 1.0.0 Build-Jdk: 1.6.0_24 Bnd-LastModified: 1304478929625 Bundle-ManifestVersion: 2 Import-Package: com.osgiweb.apps2.OSGiWebApp2.Interface, org.apache.wicket, org.apache.wicket.markup.html, org.apache.wicket.markup.html.basic, org.osgi.framework, org.springframework.osgi.context Bundle-SymbolicName: com.osgiweb.apps2.OSGiWebApp2.HomePages
再來就是最主要的Web Domain Bundle ,
這邊建立一個Web Bundle projectd , OSGiWebApp2.web
首先要建立Web.xml 設定
<?xml version="1.0" encoding="ISO-8859-1"?> <web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5"> <display-name>OSGiWebApp - web</display-name> <context-param> <param-name>contextClass</param-name> <param-value>org.eclipse.virgo.web.dm.ServerOsgiBundleXmlWebApplicationContext</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <filter> <filter-name>WicketSpring</filter-name> <filter-class>org.apache.wicket.protocol.http.WicketFilter</filter-class> <init-param> <param-name>applicationFactoryClassName</param-name> <param-value>org.apache.wicket.spring.SpringWebApplicationFactory</param-value> </init-param> </filter> <filter-mapping> <filter-name>WicketSpring</filter-name> <url-pattern>/*</url-pattern> <dispatcher>REQUEST</dispatcher> <dispatcher>ERROR</dispatcher> </filter-mapping> <!-- DISPATCHER SERVLET CONFIG ,Spring MVC --> <!-- <servlet> <servlet-name>greenpages</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>greenpages</servlet-name> <url-pattern>/app/*</url-pattern> </servlet-mapping> --> </web-app>
然後是Spring XML cofnig , applicationContext.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 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/osgi http://www.springframework.org/schema/osgi/spring-osgi.xsd" xmlns:context="http://www.springframework.org/schema/context" xmlns:osgi="http://www.springframework.org/schema/osgi"> <!-- enable classpath scanning --> <context:component-scan base-package="OSGiWebApp.web" /> <bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor"/> <!-- enable anntotation-driven controllers --> <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter" /> <bean id="wicketApplication" class="com.app.WicketApplication"> </bean> </beans>
WicketApplication.java extends WebApplication, 在init的時候還要設定一下一些屬性
因為原本的Wicket 是不支援OSGi classpath & resource , 在init 的時候要設定
1: this.getApplicationSettings().setClassResolver(final IClassResolver defaultClassResolver)
2: this.getResourceSettings().setResourceStreamLocator(IResourceStreamLocator resourceStreamLocator);
3: registerPageService(this);
(從這邊init時也要把Interface IWicketPageService 註冊成OSGi Service, 實作為WicketPageServiceImpl
確保Bundle 在mountbookmarkpage時的WebApplication 都為同一個)
Update(05/11): 設定好setClassResolver,setResourceStreamLocator之後,可直接對package class做處理, Example :
... setResponsePage(com.osgiweb.apps2.OSGiWebApp2.HomePages.home2.class); .. ..WicketApplication.java
package com.app; import org.apache.wicket.Page; import org.apache.wicket.protocol.http.WebApplication; import org.apache.wicket.spring.injection.annot.SpringComponentInjector; import com.osgi.OsgiResourceStreamLocator; import org.osgi.framework.BundleContext; import org.springframework.beans.BeansException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.osgi.context.BundleContextAware; import org.springframework.stereotype.Component; import com.osgi.OsgiClassResolver; import com.osgiweb.apps2.OSGiWebApp2.Interface.IWicketExtensionService; import com.osgiweb.apps2.OSGiWebApp2.Interface.IWicketPageService; import com.page.home; import com.service.WicketExtensionServiceImpl; import com.service.WicketPageServiceImpl; @Component public class WicketApplication extends WebApplication implements ApplicationContextAware,BundleContextAware{ private static final String DEFAULT_ENCODING = "UTF-8"; private BundleContext bundleCtx; @Autowired private ApplicationContext applicationContext; @Override public Class<? extends Page> getHomePage() { return home.class; } protected void init() { this.getApplicationSettings().setClassResolver(new OsgiClassResolver()); this.getResourceSettings().setResourceStreamLocator(new OsgiResourceStreamLocator()); // this.getPageSettings().addComponentResolver(new OsgiExtensionPointResolver()); super.init(); addComponentInstantiationListener(new SpringComponentInjector(this, applicationContext, true)); getMarkupSettings().setDefaultMarkupEncoding(DEFAULT_ENCODING); registerPageService(this); } // @Override // public String getConfigurationType() { // return WebApplication.DEVELOPMENT; // } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } // public static WicketApplication get() { // return (WicketApplication) WebApplication.get(); // } @Override public void setBundleContext(BundleContext bundleContext) { this.bundleCtx = bundleContext; System.out.println("bundle ctx = " bundleCtx ); } public void registerPageService(WebApplication app){ System.out.println("register pageService"); System.out.println("class = " IWicketPageService.class.getName()); this.bundleCtx.registerService(IWicketPageService.class.getName(), new WicketPageServiceImpl(this), null); System.out.println("register pageService finished"); this.bundleCtx.registerService(IWicketExtensionService.class.getName(),new WicketExtensionServiceImpl(), null); } }
然後是OsgiClassResolver.java , 實作IClassResolver , 針對OSGi 改寫resolveClass
package com.osgi; import java.net.URL; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; import org.apache.wicket.application.DefaultClassResolver; import org.apache.wicket.application.IClassResolver; import org.apache.wicket.util.string.Strings; import org.osgi.framework.Bundle; import org.osgi.framework.BundleContext; import org.springframework.osgi.context.BundleContextAware; public class OsgiClassResolver implements IClassResolver,BundleContextAware { private DefaultClassResolver wrappedClassResolver; private BundleContext bundleCtx; public OsgiClassResolver() { this.wrappedClassResolver = new DefaultClassResolver(); } /** * {@inheritDoc} * * @see org.apache.wicket.application.IClassResolver#getResources(java.lang.String) */ public Iterator<URL> getResources(String name) { System.out.println("to get Resources = " name); return this.wrappedClassResolver.getResources(name); } /** * {@inheritDoc} * * @see org.apache.wicket.application.IClassResolver#resolveClass(java.lang.String) */ public Class<?> resolveClass(String classname) throws ClassNotFoundException { System.out.println("resolveClass = " classname); Class<?> clazz = null; try { clazz = this.wrappedClassResolver.resolveClass(classname); } catch (ClassNotFoundException e) { // not found in parent classloader? look through the bundles... System.out.println("resolveClass for bundles"); Bundle[] bundles = bundleCtx.getBundles(); if (bundles != null && bundles.length > 0) { for (Bundle bundle : bundles) { if (bundle.getState() != Bundle.ACTIVE || !this.classIsExportedByBundle(classname, bundle)) continue; try { clazz = bundle.loadClass(classname); if (clazz != null) break; } catch (ClassNotFoundException ex) { ; // ignore and try next bundle.. } } } } if (clazz == null) throw new ClassNotFoundException(classname); return clazz; } private boolean classIsExportedByBundle(String classname, Bundle bundle) { List<String> exportedPackages = this.getExportedPackages(bundle); return exportedPackages.contains(Strings.beforeLast(classname, '.')); } private List<String> getExportedPackages(Bundle bundle) { String exportedString = (String) bundle.getHeaders().get( "Export-Package"); if (Strings.isEmpty(exportedString)) return Collections.emptyList(); String[] splitted = Strings.split(exportedString, ','); if (splitted == null || splitted.length == 0) return Collections.emptyList(); List<String> packages = new ArrayList<String>(); for (String s : splitted) { String pkg = null; if (s.contains(";")) pkg = Strings.beforeFirst(s, ';').trim(); else pkg = s.trim(); if (pkg != null && pkg.length() > 0) packages.add(pkg); } return packages; } @Override public void setBundleContext(BundleContext bundleContext) { this.bundleCtx = bundleContext; } }
然後是OsgiResourceStreamLocator.java(org.apache.wicket.util.resource.locator下有自帶一個)
package com.osgi; import org.apache.wicket.util.file.IResourceFinder; import org.apache.wicket.util.resource.IResourceStream; import org.apache.wicket.util.resource.locator.ResourceStreamLocator; /** * OSGI specific resource stream factory * * @author Juergen Donnerstag */ public class OsgiResourceStreamLocator extends ResourceStreamLocator { /** * Construct. */ public OsgiResourceStreamLocator() { } /** * Construct. * * @param finder */ public OsgiResourceStreamLocator(final IResourceFinder finder) { super(finder); } /** * * @see org.apache.wicket.util.resource.locator.ResourceStreamLocator#locate(java.lang.Class, * java.lang.String) */ @Override public IResourceStream locate(final Class<?> clazz, final String path) { System.out.println("IResourceStream = " clazz ", path-" path); return super.locate(clazz, "/" path); } }
WicketPageServiceImpl.java , 這邊主要呼叫WebApplication 下的mount,mountBookmarkablePage
在Web Domain Bundle 啟動初始化同時建立一個OSGi Service , 內含WebApplication實體一個
其他Bundle 透過mountBookmarkablePage告知Web Domain Bundle 新的Page Bundle 資訊
package com.service; import org.apache.wicket.Page; import org.apache.wicket.protocol.http.WebApplication; import org.apache.wicket.request.target.coding.IRequestTargetUrlCodingStrategy; import org.apache.wicket.util.lang.PackageName; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import com.osgiweb.apps2.OSGiWebApp2.Interface.IWicketPageService; /** * Default implementation of the {@link IWicketPageService} interface. * * * */ public class WicketPageServiceImpl implements IWicketPageService { private WebApplication application; public WicketPageServiceImpl(WebApplication application) { this.application = application; } /** * {@inheritDoc} * * @see net.javaforge.wicket.osgi.service.IWicketPageService#addIgnoreMountPath(java.lang.String) */ public void addIgnoreMountPath(String path) { this.application.addIgnoreMountPath(path); } /** * {@inheritDoc} * * @see net.javaforge.wicket.osgi.service.IWicketPageService#mount(org.apache.wicket.request.target.coding.IRequestTargetUrlCodingStrategy) */ public void mount(IRequestTargetUrlCodingStrategy encoder) { this.application.mount(encoder); } /** * {@inheritDoc} * * @see net.javaforge.wicket.osgi.service.IWicketPageService#mount(java.lang.String, * org.apache.wicket.util.lang.PackageName) */ public void mount(String path, PackageName packageName) { this.application.mount(path, packageName); } /** * {@inheritDoc} * * @see net.javaforge.wicket.osgi.service.IWicketPageService#mountBookmarkablePage(java.lang.String, * java.lang.Class) */ public <T extends Page> void mountBookmarkablePage(String path, Class<T> bookmarkablePageClass) { System.out.println("mountBookmarkablePage 77"); this.application.mountBookmarkablePage(path, bookmarkablePageClass); } /** * {@inheritDoc} * * @see net.javaforge.wicket.osgi.service.IWicketPageService#mountBookmarkablePage(java.lang.String, * java.lang.String, java.lang.Class) */ public <T extends Page> void mountBookmarkablePage(String path, String pageMapName, Class<T> bookmarkablePageClass) { System.out.println("mountBookmarkablePage 90"); System.out.println("Path = " path); System.out.println("pageMapName = " pageMapName); System.out.println("bookmarkablePageClass = " bookmarkablePageClass); this.application.mountBookmarkablePage(path, pageMapName, bookmarkablePageClass); } /** * {@inheritDoc} * * @see net.javaforge.wicket.osgi.service.IWicketPageService#mountSharedResource(java.lang.String, * java.lang.String) */ public void mountSharedResource(String path, String resourceKey) { this.application.mountSharedResource(path, resourceKey); } /** * {@inheritDoc} * * @see net.javaforge.wicket.osgi.service.IWicketPageService#unmount(java.lang.String) */ public void unmount(String path) { this.application.unmount(path); } public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { // TODO Auto-generated method stub } }
home.java , 已mount的page 可以直接在Broswer 輸入localhost:8080/mountPageName
或著使用setResponsePage(com.osgiweb.apps2.OSGiWebApp2.HomePages.home2.class)
package com.page; import org.apache.wicket.markup.html.WebPage; import org.apache.wicket.markup.html.basic.Label; public class home extends WebPage { // private IuserDaoImp dao = new IuserDaoImp(); /** * Constructor */ public home() { System.out.println("test for homePage 2"); // add(new Label("message", // "Hello OSGi-land! ffff" dao.selectUserName())); add(new Label("message", "Hello")); setResponsePage(com.osgiweb.apps2.OSGiWebApp2.HomePages.home2.class); } }
home.html
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <title>Wickety OSGi!</title> </head> <body> <p wicket:id="message">replace me</p> </body> </html>
MANIFEST.mf
Manifest-Version: 1.0 Import-Bundle: com.springsource.org.apache.taglibs.standard;version="[ 1.1.2,1.3)" Bundle-Version: 1.0.0 Tool: Bundlor 1.0.0.RELEASE Bundle-Name: OSGiWebApp web Import-Library: org.springframework.spring;version="[3.0,3.1)" Bundle-ManifestVersion: 2 Bundle-SymbolicName: OSGiWebApp2.web Web-ContextPath: OSGiWebApp Import-Package: javax.servlet.jsp.jstl.core;version="[1.1.2,1.2.0)", org.apache.wicket;version="1.4.16", org.apache.wicket.application;version="1.4.16", org.apache.wicket.markup.html;version="1.4.16", org.apache.wicket.markup.html.basic;version="1.4.16", org.apache.wicket.protocol.http;version="1.4.16", org.apache.wicket.settings;version="1.4.16", org.apache.wicket.spring;version="1.4.16", org.apache.wicket.spring.injection.annot;version="1.4.16", org.apache.wicket.markup.resolver;version="1.4.16", org.apache.wicket.util.file;version="1.4.16", org.apache.wicket.util.resource.locator;version="1.4.16", org.eclipse.virgo.web.dm;version="[2.0.0,3.0.0)", org.slf4j;version="1.6.1", org.springframework.beans;version="[3.0.0,3.1.0)", org.springframework.beans.factory.annotation;version="[3.0.0,3.1.0)", org.springframework.context;version="[3.0.0,3.1.0)", org.springframework.stereotype;version="[3.0.0,3.1.0)", org.springframework.web.servlet.mvc.annotation;version="[3.0.0,3.1.0)", org.springframework.osgi.context, com.osgiweb.apps2.OSGiWebApp2.Interface, com.osgiweb.apps2.OSGiWebApp2.HomePages Export-Package: com.app;version="1.0.0"; uses:="org.apache.wicket.protocol.http, org.osgi.framework, org.springframework.beans, org.springframework.beans.factory.annotation, org.springframework.context, org.springframework.osgi.context, org.springframework.stereotype", com.osgi;version="1.0.0"; uses:="org.apache.wicket, org.apache.wicket.application, org.apache.wicket.markup, org.apache.wicket.markup.resolver, org.osgi.framework, org.springframework.osgi.context", com.page;version="1.0.0";uses:="org.apache.wicket.markup.html", com.service;version="1.0.0"; uses:="com.osgiweb.apps2.OSGiWebApp2.Interface, org.apache.wicket.protocol.http, org.apache.wicket.request.target.coding, org.apache.wicket.util.lang, org.springframework.beans, org.springframework.context"
然後deploy到Server上 ,看看bundle狀況
輸入位置 http://localhost:8080/OSGiWebApp/
會看到OSGiWebApp2.HomePages 下的home2.html結果
查看server log, IResourceStream 會load resource from bundle class
Reference:
Pax Wicket
Wicket Osgi Extension
Wicket, OSGi, and Spring DM
沒有留言:
張貼留言