這次要實作自訂的Wicket tag , 達成簡單的Plugin , 原始的參考來自於 Wicket Osgi Extension ,不過因為他是embedded apache felix ,所以我沒使用,而是透過source code去使用。
這邊的概念就是在Wicket HTML 頁面裡面定義一個Tag , 大概就像:
<wicket:extension-point id="test.extension.panel" />這樣我們只要在一個Bundle 裡面定義好一個Extension class (extends Panel) , 然後再透過Bundle Activator 取得ExtensionService 來註冊這個Extension-Point id&class ,然後在Web Bundle裡碰到有這個Extension-point的tag會去看有沒有register的id & class, 有的話就取出來然後append 到html page裡。
其他的設定請參考 OSGi&Wicket - To locate class and resource in bundle context. , 這邊只例出Extension 相關的class實作 & 例子。
效果大概如下圖所示:
結果就會長這樣:
在原先的Bundle - OSGiWebApp2.Interface , 新增一個Class ExtensionPoints.java
裡面定義每個ExtensionPoints 的id
package com.osgiweb.apps2.OSGiWebApp2; public final class ExtensionPoints { private ExtensionPoints() { } public static final String HOMEPAGE_PANEL = "test.extension.panel"; public static final String HOMEPAGE_MENU = "test.extension.menu"; }
然後在Bundle - OSGiWebApp2.Web 裡面新增以下Class
ExtensionPointContainer.java , extends MarkupContainer, 主要是Convert <wicket:extension-point />
to <wicket:extension-point>...</wicket:extension-point>,
package com.osgi; import org.apache.wicket.MarkupContainer; import org.apache.wicket.markup.ComponentTag; import org.apache.wicket.markup.parser.XmlTag; public class ExtensionPointContainer extends MarkupContainer { private static final long serialVersionUID = 1L; public ExtensionPointContainer(String id) { super(id); System.out.println("ExtensionPointContainer start"); } /** * {@inheritDoc} * * @see org.apache.wicket.MarkupContainer#isTransparentResolver() */ @Override public boolean isTransparentResolver() { return true; } /** * {@inheritDoc} * * @see org.apache.wicket.Component#onComponentTag(org.apache.wicket.markup.ComponentTag) */ @Override protected void onComponentTag(final ComponentTag tag) { // Convert <wicket:extension-point /> into // <wicket:extension-point>...</wicket:extension-point> if (tag.isOpenClose()) { tag.setType(XmlTag.OPEN); } super.onComponentTag(tag); } }
OsgiExtensionPointResolver.java , 實作IComponentResolver , 然後在WebApplication 裡面還要做set的動作,這樣wicket 才會知道我們自訂的tag
package com.osgi; import java.util.Collection; import org.apache.wicket.MarkupContainer; import org.apache.wicket.markup.ComponentTag; import org.apache.wicket.markup.MarkupException; import org.apache.wicket.markup.MarkupStream; import org.apache.wicket.markup.WicketTag; import org.apache.wicket.markup.parser.filter.WicketTagIdentifier; import org.apache.wicket.markup.resolver.IComponentResolver; import org.osgi.framework.BundleContext; import org.springframework.osgi.context.BundleContextAware; import com.osgiweb.apps2.OSGiWebApp2.Interface.Extension; import com.osgiweb.apps2.OSGiWebApp2.Interface.IWicketExtensionService; public class OsgiExtensionPointResolver implements IComponentResolver { private static final long serialVersionUID = 1L; private BundleContext bundleCtx; private static final String TAG_NAME = "extension-point"; static { // register "wicket:extension-point" WicketTagIdentifier.registerWellKnownTagName(TAG_NAME); } public OsgiExtensionPointResolver(BundleContext btx){ System.out.println("constructor for OsgiExtensionPointResolver"); this.bundleCtx = btx; } /** * {@inheritDoc} * * @see org.apache.wicket.markup.resolver.IComponentResolver#resolve(org.apache.wicket.MarkupContainer, * org.apache.wicket.markup.MarkupStream, * org.apache.wicket.markup.ComponentTag) */ public boolean resolve(MarkupContainer container, MarkupStream markupStream, ComponentTag tag) { if (tag instanceof WicketTag) { WicketTag wtag = (WicketTag) tag; System.out.println("wtag.getName() =" wtag.getName()); if (TAG_NAME.equalsIgnoreCase(wtag.getName())) { String extensionPointId = wtag.getAttributes().getString("id"); if ((extensionPointId == null) || (extensionPointId.trim().length() == 0)) { throw new MarkupException( "Wrong format of <wicket:extension-point id='xxx'>: attribute 'id' is missing"); } System.out.println("extensionPointId = " extensionPointId); IWicketExtensionService s = (IWicketExtensionService)this.bundleCtx.getService(this.bundleCtx.getServiceReference(IWicketExtensionService.class.getName())); System.out.println("IWicketExtensionService s = " s); //extensions contain all extension-point id by class name, //such as extensions =[class com.osgiweb.apps2.OSGiWebApp2.HomePages.SimpleMenuContribution] Collection<Class<? extends Extension>> extensions = s.findExtensions(extensionPointId); System.out.println("extensions =" extensions); if (extensions != null && !extensions.isEmpty()) { for (Class<? extends Extension> ext : extensions) { try { final String compId = "_extension_" extensionPointId "_" container.getPage().getAutoIndex(); System.out.println("compId =" compId); Extension comp = ext.getConstructor(String.class).newInstance(compId); System.out.println("comp =" comp); comp.setRenderBodyOnly(container.getApplication().getMarkupSettings().getStripWicketTags()); System.out.println("container ->" container.getApplication().getMarkupSettings().getStripWicketTags()); container.autoAdd(comp, markupStream); markupStream.setCurrentIndex(markupStream.getCurrentIndex() - 1); } catch (Exception e) { throw new RuntimeException(e); } } markupStream.setCurrentIndex(markupStream.getCurrentIndex() 1); } else { ExtensionPointContainer comp = new ExtensionPointContainer(extensionPointId "_" container.getPage().getAutoIndex()); comp.setRenderBodyOnly(container.getApplication().getMarkupSettings() .getStripWicketTags()); container.autoAdd(comp, markupStream); } return true; } } // We were not able to handle the tag return false; } }
再來是WicketExtensionServiceImpl.java, 實作OSGiWebApp2.Interface下的IWicketExtensionService, 主要是透過WebApplication 發佈出來的osgi service 在每個bundle註冊Extension point
package com.service; import java.util.Collection; import java.util.HashSet; import java.util.Iterator; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import com.osgiweb.apps2.OSGiWebApp2.Interface.Extension; import com.osgiweb.apps2.OSGiWebApp2.Interface.IWicketExtensionService; public class WicketExtensionServiceImpl implements IWicketExtensionService { //Thread private ConcurrentHashMap<String, Set<Class<? extends Extension>>> extensionsRegistry = new ConcurrentHashMap<String, Set<Class<? extends Extension>>>(); /** * {@inheritDoc} * * @see net.javaforge.wicket.osgi.service.IWicketExtensionService#registerExtension(java.lang.String, * java.lang.Class) */ public void registerExtension(String extensionPointId, Class<? extends Extension> extension) { if (!this.extensionsRegistry.containsKey(extensionPointId)) { this.extensionsRegistry.put(extensionPointId, new HashSet<Class<? extends Extension>>()); } Set<Class<? extends Extension>> extensions = this.extensionsRegistry .get(extensionPointId); extensions.add(extension); } /** * {@inheritDoc} * * @see net.javaforge.wicket.osgi.service.IWicketExtensionService#findExtensions(java.lang.String) */ public Collection<Class<? extends Extension>> findExtensions( String extensionPointId) { return this.extensionsRegistry.get(extensionPointId); } /** * {@inheritDoc} * * @see net.javaforge.wicket.osgi.service.IWicketExtensionService#unregisterExtension(java.lang.Class) */ public void unregisterExtension(Class<? extends Extension> extension) { Collection<Set<Class<? extends Extension>>> values = this.extensionsRegistry .values(); for (Set<Class<? extends Extension>> subset : values) { Iterator<Class<? extends Extension>> it = subset.iterator(); while (it.hasNext()) { Class<? extends Extension> ext = it.next(); if (ext == extension) it.remove(); } } } }
然後在原本的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> Wicket extendsion poinrt for test.extension.panel as below : <Br> <wicket:extension-point id="test.extension.panel" /> </body> </html>
然後是WicketApplication.java, 這邊要在init()時 getPageSetting, 然後addComponentResolver
package com.app; import org.apache.wicket.Page; import org.apache.wicket.protocol.http.WebApplication; import org.apache.wicket.spring.injection.annot.SpringComponentInjector; 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.osgi.OsgiExtensionPointResolver; import com.osgi.OsgiResourceStreamLocator; 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(this.bundleCtx)); 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); } }
OSGiWebApp2.web修改後的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: com.osgiweb.apps2.OSGiWebApp2.HomePages, com.osgiweb.apps2.OSGiWebApp2.Interface, 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;version="1.4.16", org.apache.wicket.markup.html;version="1.4.16", org.apache.wicket.markup.html.link;version="1.4.16", org.apache.wicket.markup.html.basic;version="1.4.16", org.apache.wicket.markup.parser;version="1.4.16", org.apache.wicket.markup.parser.filter;version="1.4.16", org.apache.wicket.markup.resolver;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.util.file;version="1.4.16", org.apache.wicket.util.value;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.osgi.context, org.springframework.stereotype;version="[3.0.0,3.1.0)", org.springframework.web.servlet.mvc.annotation;version="[3.0.0,3.1.0)" 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"
**Update 2011/08/03, 在Virgo 3.0.x RC上 新的設定如下,
原本的設定在3.0.xRc版本裡面會找不到class ServerOsgiBundleXmlWebApplicationContext,要在MANIFEST.mf檔裡面加入Import-Bundle的部份
加入以下package
org.eclipse.virgo.web.dm完整如下,也許有辦法不用import-bundle ....
Manifest-Version: 1.0 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.apache.wicket.util.file, org.apache.wicket.util.resource, org.apache.wicket.util.resource.locator, 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" 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)" Import-Bundle: org.eclipse.virgo.web.dm Bundle-ManifestVersion: 2 Bundle-SymbolicName: OSGiWebApp2.web Web-ContextPath: OSGiWebApp Bundle-ClassPath: .,WEB-INF/classes Import-Package: com.app; com.osgiweb.apps2.OSGiWebApp2.Interface, org.apache.wicket, org.apache.wicket.application, org.apache.wicket.markup, org.apache.wicket.markup.html, org.apache.wicket.markup.html.basic, org.apache.wicket.markup.parser, org.apache.wicket.markup.parser.filter, org.apache.wicket.markup.resolver, org.apache.wicket.protocol.http, org.apache.wicket.request.target.coding, org.apache.wicket.settings, org.apache.wicket.spring.injection.annot, org.apache.wicket.util.file, org.apache.wicket.util.lang, org.apache.wicket.util.resource, org.apache.wicket.util.resource.locator, org.apache.wicket.util.string, org.apache.wicket.util.value, org.apache.wicket.spring, org.osgi.framework, org.springframework.beans, org.springframework.beans.factory.annotation, org.springframework.context, org.springframework.osgi.context, org.springframework.stereotype, org.springframework.web.servlet.mvc.annotation
然後是Bundle - OSGiWebApp2.HomePages ,
先新增SimpleMenuContribution.java, SimpleMenuContribution.html
SimpleMenuContribution.java
package com.osgiweb.apps2.OSGiWebApp2.HomePages; import org.apache.wicket.markup.html.link.BookmarkablePageLink; import com.osgiweb.apps2.OSGiWebApp2.Interface.Extension; public class SimpleMenuContribution extends Extension { private static final long serialVersionUID = 1L; public SimpleMenuContribution(String id) { super(id); this.add(new BookmarkablePageLink<String>("page1", home2.class)); // this.add(new BookmarkablePageLink<String>("page2", MyPage2.class)); } }
SimpleMenuContribution.html
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org" > <wicket:panel> <a href="#" wicket:id="page1" style="margin-left: 10px; margin-right: 10px;">page1</a> </wicket:panel> </html>
然後修改Activator.java, 新增IWicketExtensionService wicketPageService;的部份, 可以註冊extension point id,class,
package com.osgiweb.apps2.OSGiWebApp2.HomePages; import org.osgi.framework.BundleContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.osgi.context.BundleContextAware; import com.osgiweb.apps2.OSGiWebApp2.ExtensionPoints; import com.osgiweb.apps2.OSGiWebApp2.Interface.IWicketExtensionService; import com.osgiweb.apps2.OSGiWebApp2.Interface.IWicketPageService; public class Activator implements BundleContextAware { Logger logg = LoggerFactory.getLogger(Activator.class); private BundleContext bundleCtx; IWicketPageService wicketService; IWicketExtensionService wicketPageService; public void start(){ // wicketService.mountBookmarkablePage( "home2", home2.class); wicketPageService.registerExtension(ExtensionPoints.HOMEPAGE_PANEL, SimpleMenuContribution.class); // logg.info("check logback object1 = {}, object2={}","MOMO-x","YOtsubaObjects"); } 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; } public void setWicketPageService(IWicketExtensionService wicketPageService) { this.wicketPageService = wicketPageService; } }
也別忘了修改bundle-context.xml,跟bundle-context-osgi.xml
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" /> <property name="wicketPageService" ref="iwicketPageService" /> </bean> </beans>
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"/> <osgi:reference id="iwicketPageService" interface="com.osgiweb.apps2.OSGiWebApp2.Interface.IWicketExtensionService"/> </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.apache.wicket.markup.html.link, org.osgi.framework, org.slf4j, org.springframework.osgi.context Bundle-SymbolicName: com.osgiweb.apps2.OSGiWebApp2.HomePages
然後把這3個bundle deploy 到server上,
沒問題的話會看到以下的畫面
可以透過<wicket:extension-point id="test.extension.panel" /> 的方法來新增extension point的點, 然後每個xtension point在每個plugin bundle 裡面,這樣可以抽換每個bundle的plugin 。
Reference :
Wicket Osgi Extension
沒有留言:
張貼留言