osgi

  •   如何用maven创建osgi项目
  •   如何启动osgi框架
  •   如何在osgi外部与osgi框架通信
  •   如何应用jndi配置资源引用
  •   如何发布osgi服务
  •   如何创建基于osgi的web应用项目
1.用maven创建osgi项目
 
  用maven的目的是使开发相率更高,而且不需要自己修改manifest.mf文件,用maven插件即可自动完成创建manifest并打包。创建这种项目的要求如下:
   项目包类型为:<packaging>bundle</packaging>
   要使用maven自动创建manifest.mf文件,需要插件maven-bundle-plugin,配置如下:
  <build>
        <plugins>
            <plugin>
                <groupId>org.apache.felix</groupId>
                <artifactId>maven-bundle-plugin</artifactId>
                <version>1.4.0</version>
                <extensions>true</extensions>
                <configuration>
                    <instructions>
                        <Bundle-SymbolicName>${pom.groupId}.${pom.artifactId}</Bundle-SymbolicName>
                        <Bundle-Name>${pom.name}</Bundle-Name>
                        <Bundle-Version>${pom.version}</Bundle-Version>
                        <Bundle-Activator>com.javaworld.sample.tomcat.webActivator</Bundle-Activator>
                        <Export-Package>com.javaworld.sample.tomcat</Export-Package>
                        <Import-Package>
                            org.osgi.framework
                        </Import-Package>
                    </instructions>
                </configuration>
            </plugin>
        </plugins>
    </build>
  使用maven-bundle-plugin请参考:http://wso2.org/library/tutorials/develop-osgi-bundles-using-maven-bundle-plugin ,配置好后在项目目录下执行mvn install即可
同时还要引用osgi框架支持依赖包:
    <dependencies>
            <dependency>
            <groupId>org.eclipse.osgi</groupId>
            <artifactId>framework</artifactId>
            <version>3.4.2.R34x_v20080826</version>
            <type>jar</type>
            <scope>provided</scope>
        </dependency>
    </dependencies>
 注意这里的type和scope,这种用法不仅表名bundel项目可以引用第三方bundle项目,普通项目也可以。下面将会看到。

2.启动osgi框架,并在外部与osgi框架通信
 这里用Equinox OSGi框架,让EquinoxContainer 实现接口OsgiServices 并实现OsgiContainable 的start和stop方法。对于框架的启动和停止,我们放在tomcat启动和停止时触发。
首先用maven创建普通的项目,在pom中添加依赖
    <dependencies>
        <dependency>
            <groupId>apache-tomcat</groupId>
            <artifactId>catalina</artifactId>
            <version>5.5.12</version>
            <type>jar</type>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.eclipse.osgi</groupId>
            <artifactId>services</artifactId>
            <version>3.1.200.v20071203</version>
            <type>jar</type>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.eclipse.osgi</groupId>
            <artifactId>framework</artifactId>
            <version>3.4.2.R34x_v20080826</version>
            <type>jar</type>
            <scope>provided</scope>
        </dependency>
    </dependencies>

public interface OsgiServices {

    public Object getOSGiService(String serviceName);

    public Class<?> getBundleClass(String bundleName, String className)
            throws ClassNotFoundException;
}

//OsgiContainable 扩展OsgiServices 并提供start和stop接口
public class EquinoxContainer implements OsgiContainable {
    private static Logger log = Logger
            .getLogger(EquinoxContainer.class.getName());

    private static BundleContext context;

    private static String equinoxHome;

    private static EquinoxContainer instance;

    private EquinoxContainer() {
    }

    public static EquinoxContainer getInstance() {
        if (instance == null) {
            instance = new EquinoxContainer();
        }
        return instance;
    }

    public void start() throws Exception {

        configFramework();

        EclipseStarter.run(new String[] { "-console" }, null);
        // get BundleContext
        context = EclipseStarter.getSystemBundleContext();
    }

    private void configFramework() {
        String catalinaHome = System.getProperty("catalina.home");
        log.info("The catalina home is " + catalinaHome);
        File filePath = new File(catalinaHome, "/osgi/equinox");
        if (!filePath.exists()) {
            throw new IllegalStateException(
                    "Can not find Equinox runtime on the specified the directory. "
                            + catalinaHome + "/osgi/equinox");
        }

        equinoxHome = filePath.getAbsolutePath();
        log.info("The osgi home is " + equinoxHome);

        filePath = new File(filePath, "plugins");
        String bundlePath = filePath.getAbsolutePath();
        log.info("The bundle home is " + bundlePath);
        // set the bundle home
        FrameworkProperties.setProperty("osgi.syspath", bundlePath);

        filePath = new File(equinoxHome, "configuration");
        String confHome = filePath.getAbsolutePath();
        // set the configuration home
        FrameworkProperties.setProperty("osgi.configuration.area", confHome);
        log.info("The configuration home is " + confHome);
        //config.ini中至少需要配置osgi.bundles需要启动的bundles
        Properties prop = loadConfigProperties(confHome, "/config.ini");
        setSystemProperties(prop);

        // set the framework properties
        FrameworkProperties.setProperty("osgi.noShutdown", "true");
        FrameworkProperties.setProperty("eclipse.ignoreApp", "true");
        FrameworkProperties.setProperty("osgi.bundles.defaultStartLevel", "4");
    }
    。。。
}
为了让tomcat等启动时执行EquinoxContainer 的start和stop,需要实现LifecycleListener接口,并在tomcat server.xml文件中配置Listener :
<Listener className="com.javaworld.sample.tomcat.osgi.OsgiLifecycleListener" osgiType="Equinox"/>

public class OsgiLifecycleListener implements LifecycleListener {

    private static Logger log = Logger.getLogger(OsgiLifecycleListener.class
            .getName());

    private static OsgiLifecycleListener listener;

   
    private String osgiType = "Equinox";

    private OsgiContainable osgiContent = null;

    public OsgiLifecycleListener() {
    }

    public static OsgiLifecycleListener getInstance() {
        return listener;
    }

    @Override
    public void lifecycleEvent(LifecycleEvent event) {
        try {
            if (Lifecycle.INIT_EVENT.equals(event.getType())) {
                log.info("Initializing osgi content. osgi type is " + osgiType);
                initContent();
                log.info("The osgi content initialized. osgi type is "
                        + osgiType);
            } else if (Lifecycle.START_EVENT.equals(event.getType())) {
                log.info("Starting osgi service.");
                osgiContent.start();
                log.info("The osgi service started.");
            } else if (Lifecycle.STOP_EVENT.equals(event.getType())) {
                log.info("Stopping osgi service.");
                osgiContent.stop();
                log.info("The osgi service stopped.");
            }
        } catch (Exception e) {
            log.info(e.getMessage());
            System.exit(-1);
        }
    }

    private void initContent() throws Exception {
        listener=this;
        log.info("listener"+listener.toString());
        osgiContent = OsgiContainerFactory.getInstance().getOsgiContent(osgiType);
    }
    。。。。
}
随着tomcat的启动将执行OsgiLifecycleListener,并通过OsgiLifecycleListener启动osgi框架,后者将完成bundles的安装和服务的注册等。

3。在osgi外部与osgi框架通信
OSGi通过BundleContext来获取OSGi服务,因此想在OSGi容器外获取OSGi服务,首要的问题就是要在OSGi容器外获取到BundleContext, EclipseStarter 中 提供了一个getSystemBundle- Context的方法,通过这个方法可以轻松拿到BundleContext,而通过BundleContext则可以轻易拿到OSGi服务的实例。不过 这个时候要注意的是,如果想执行这个OSGi 服务实例的方法,还是不太好做的,因为容器外的classloader和OSGi服务实例的class所在的classloader并不相同,因此不太好 按照java对象的方式直接去调用,更靠谱的是通过反射去调用。 如果想在容器外获取到OSGi容器里插件的class,一个可选的做法是通过BundleContext获取到Bundle,然后通过Bundle 来加载 class,采用这样的方法加载的class就可以保证是相同的。
   
    public  static Object getOSGiService(String serviceName) {
        ServiceReference serviceRef = context.getServiceReference(serviceName);
        if (serviceRef == null)
            return null;
        return context.getService(serviceRef);
    }
   
    public  static Class<?> getBundleClass(String bundleName, String className)
            throws Exception {
        Bundle[] bundles = context.getBundles();
        for (int i = 0; i < bundles.length; i++) {
            if (bundleName.equalsIgnoreCase(bundles[i].getSymbolicName())) {
                return bundles[i].loadClass(className);
            }
        }
        return null;
    }
在实现了OSGi容器外与OSGi交互之后,通常会同时产生一个需求,就是在OSGi容器内的插件要加载OSGi容器外的类,例如OSGi容器内提 供了一个mvc框架,而Action类则在OSGi容器外由其他的容器负责加载,那么这个时候就会产生这个需求了,为了做到这点,有一个比较简单的解决方 法,就是编写一个Bundle,在该Bundle中放置一个允许设置外部ClassLoader的OSGi服务,例如:

Java代码  
  1. public   class  ClassLoaderService{    
  2.     public   void  setClassLoader(ClassLoader classloader);    
  3. }   
 

      基于上面的方法,在外部启动Equinox 的类中去反射执行ClassLoaderService这个OSGi服务的setClassLoader方法,将外部的classloader设置进来, 然后在OSGi容器的插件中要加载OSGi容器外的类的时候就调用下面这个ClassLoaderService去完成类的加载。


 4.应用jndi配置资源引用
 上面知道要在osgi外面引用osgi服务或者osgi类,唯一可行的办法是获取BundleContext,这点是可以在osgi框架获取,这样一来BundleContext就是我们与osgi通信的桥梁了,OsgiServices 也是通过此桥梁与osgi框架通信,为方便对OsgiServices 的获取,现在将OsgiServices配置到jndi。同样把Resource添加到server.xml的Host标签中:
    <Resource name="osgi/services" auth="Container"
       type="com.javaworld.sample.tomcat.osgi.OsgiServices"
       factory="com.javaworld.sample.tomcat.osgi.OsgiServicesObjectFactory" />
在tomcat 的web.xml中添加Resource引用:
    <resource-env-ref>
       <description>osgi services</description>
       <resource-env-ref-name>osgi/services</resource-env-ref-name>
       <resource-env-ref-type>
           com.javaworld.sample.tomcat.osgi.OsgiServices
       </resource-env-ref-type>
    </resource-env-ref>

public class OsgiServicesObjectFactory implements ObjectFactory {

    private static Logger log = Logger.getLogger(OsgiServicesObjectFactory.class
            .getName());

    private OsgiServices osgiServices;

    @Override
    public Object getObjectInstance(Object obj, Name name, Context nameCtx,
            Hashtable<?, ?> environment) throws Exception {

        Map<String, String> attMap = new HashMap<String, String>();
        // Customize the bean properties from our attributes
        Reference ref = (Reference) obj;
        Enumeration<RefAddr> addrs = ref.getAll();
        while (addrs.hasMoreElements()) {
            RefAddr addr = addrs.nextElement();
            String attrName = addr.getType();
            String value = (String) addr.getContent();
            log.info("the attribute is (" + attrName + " == " + value + ")");
            attMap.put(attrName, value);
        }
        log.info("getObjectInstance called.");
        osgiServices=OsgiLifecycleListener.getInstance().getOsgiServices();
        return osgiServices;
    }
}
 5. 发布osgi服务
public interface HelloService {
    public String sayHello();
}

public class webActivator implements BundleActivator {
    ServiceRegistration serviceRegistration;

   
    public void start(BundleContext bundleContext) {
        serviceRegistration = bundleContext.registerService(
                HelloService.class.getName(), new HelloServiceImpl(),
                new Properties());
    }

   
    public void stop(BundleContext bundleContext) {
        serviceRegistration.unregister();
    }
}

 6.创建基于osgi的web应用项目
创建<packaging>war</packaging>web项目,添以下类:
public class TomcatWebCall {
    public static String invokeService() {
        try {
            HelloService service = OsgiServiceFactory.getOsgiService(
                    "osgi/services", HelloService.class);
            return service.sayHello();
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
            e.printStackTrace();
        }
        return null;
    }
}

public class OsgiServiceFactory {
    public static <T> T getOsgiService(String jndiName,Class<T> clazz) {
        OsgiServices services = (OsgiServices) Utils.lookup(jndiName);
        OsgiServiceInvocationHandler handler = new OsgiServiceInvocationHandler(
                services, clazz.getName());
        return Utils.getProxyObject(clazz,handler);
    }

    public class OsgiServiceInvocationHandler implements InvocationHandler {
    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        try {
            Object service = services.getOSGiService(servicName);
            if (service == null) {
                throw new IllegalServiceException("Cann't find out osgi service:"
                        + servicName);
            }
           
            Method[] methods = service.getClass().getDeclaredMethods();
            for (Method meth : methods) {
                if (meth.getName().equals(method.getName())) {
                    return meth.invoke(service, args);
                }
            }
            。。。
         }
    }
}

public class Utils {
   
    private static final String CONTAINER_PREFIX = "java:comp/env/";
   
    public static <T> T getProxyObject(Class<T> clazz, InvocationHandler handler) {
        Object o = Proxy.newProxyInstance(clazz.getClassLoader(),
                new Class[] { clazz }, handler);
        return clazz.cast(o);
    }
   
    private static String convertJndiName(String jndiName) {
        if (!jndiName.startsWith(CONTAINER_PREFIX)
                && jndiName.indexOf(':') == -1) {
            jndiName = CONTAINER_PREFIX + jndiName;
        }
        return jndiName;
    }
   
    public static Object lookup(String jndiName) {
        String convertedName = convertJndiName(jndiName);
        Object jndiObject = null;
        try {
            Context context = new InitialContext();
            jndiObject = context.lookup(convertedName);
        } catch (NamingException e) {
            throw new IllegalServiceException(
                    "The JNDI OSGi services name is error.", e);
        } catch (Exception e) {
            throw new IllegalServiceException(
                    "The JNDI OSGi services can not be initialized.", e);
        }

        return jndiObject;
    }
}
简单的jsp测试:
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="ISO-8859-1"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<%@page import="com.javaworld.sample.tomcat.TomcatWebCall"%>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
            <title>Demo invoke HelloService</title>
    </head>
    <body>
        <%=TomcatWebCall.invokeService() %>
    </body>
</html>

你可能感兴趣的:(osgi)