這次要實作自訂的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




沒有留言:
張貼留言