2011年5月20日 星期五

OSGi&Wicket - using customized wicket tag for extension-point on the osgi web server.

上次的 OSGi&Wicket - To locate class and resource in bundle context. 中提到,如何讓Spring+Wicket 在Eclipse Virgo Server上運作,並通過修改WebApplication裡的設定如classResolver and ResourceStreamLocator , 使得Bundle 的wicket 元件能被使用。

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

沒有留言:

張貼留言