基于Maven构建OSGI应用(Maven和OSGI结合)

基于Maven构建OSGI应用。

使用Maven来构建项目,包括项目的创建、子模块buldle的创建等。使用OSGI来实现动态模块化管理,实现模块的热插拔效果(即插即用)。

创建一个Maven项目:helloworld,并在该项目下创建两个Maven 子模块:helloworld-client、helloworld-server。

创建 helloworld maven项目、填写参数及Advanced Settings:

创建 helloworld-server maven子模块:

同样的方式再创建 helloworl-client maven 子模块。

接下来就是 hellworld-server、helloworld-client 编码以及OSGI及编译打包配置。

OSGI及编译打包配置,直接通过修改3个pom文件(1个主pom、2个子模块的pom)来配置,最终配置结果如下:

1)代码结构:

client->Activator.java:

package com.xxx.osgi.helloworld.client;

import com.xxx.osgi.helloworld.server.HelloWorldImpl;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;

import java.util.Objects;

/**
 * @author frank
 * @date 2023/12/8
 */
public class Activator implements BundleActivator {
    public void start(BundleContext bundleContext) throws Exception {
        System.out.println("helloworld-client: start");
        System.out.println("helloworld-client: call server getHelloMsg()");

        ServiceReference<HelloWorldImpl> reference = bundleContext.getServiceReference(HelloWorldImpl.class);
        if (Objects.nonNull(reference)) {
            HelloWorldImpl service = bundleContext.getService(reference);
            if (Objects.nonNull(service)) {
                String msg = service.getHelloMsg("Frank");
                System.out.println("SUCCESS: return msg is:\n" + msg);
            } else {
                System.out.println("ERROR: service not found!");
            }
            bundleContext.ungetService(reference);
        } else {
            System.out.println("ERROR: service reference not found!");
        }
    }

    public void stop(BundleContext bundleContext) throws Exception {
        System.out.println("helloworld-client: stop");
    }
}

server->Activator.java

package com.xxx.osgi.helloworld.server;

import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;

import java.util.Dictionary;
import java.util.Hashtable;
import java.util.Objects;

/**
 * @author frank
 * @date 2023/12/9
 */
public class Activator implements BundleActivator {
    public void start(BundleContext bundleContext) throws Exception {
        System.out.println("helloworld-server: start");
        HelloWorldImpl server = new HelloWorldImpl();
        Dictionary<String, Object> properties = new Hashtable<String, Object>();
        bundleContext.registerService(HelloWorldImpl.class, server, properties);
        System.out.println("helloworld-server: 服务已发布(注册)!");
    }

    public void stop(BundleContext bundleContext) throws Exception {
        System.out.println("helloworld-server: stop");
    }
}

server->IHelloWorld.java

package com.xxx.osgi.helloworld.server;

/**
 * @author frank
 * @date 2023/12/9
 */
public interface IHelloWorld {
    String getHelloMsg(String name);
}

server->HelloWorldImpl.java

package com.xxx.osgi.helloworld.server;

/**
 * @author frank
 * @date 2023/12/9
 */
public class HelloWorldImpl implements IHelloWorld {
    public String getMethodName() {
        return "[" + Thread.currentThread().getStackTrace()[2].getMethodName() + ":" +
                Thread.currentThread().getStackTrace()[2].getLineNumber() + "] ";
    }

    public String getHelloMsg(String name) {
        return getMethodName() + " HelloWorld " + name;
    }
}

2)主pom:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>org.xxx.osgi</groupId>
  <artifactId>helloworld</artifactId>
  <version>1.0.0-SNAPSHOT</version>
  <modules>
    <module>helloworld-client</module>
    <module>helloworld-server</module>
  </modules>
  <packaging>pom</packaging>

  <name>helloworld</name>
  <url>http://maven.apache.org</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>8</maven.compiler.source>
    <parent.maven.bundle.plugin.version>2.4.0</parent.maven.bundle.plugin.version>
  </properties>

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <!-- 该版本 maven 仓库找不到,如果要用该版本可以在 Project Structure->Project Settings->Modules 中设置:-->
      <!-- 设置 OSGI:General->Configure OSGI Core Library->Use Library 指定本地 jar 文件静态添加 osgi lib  -->
      <!--
      <groupId>org.eclipse</groupId>
      <artifactId>osgi</artifactId>
      <version>3.18.600.v20231110-1900</version>
      <scope>provided</scope>
      -->
      <!-- 该版本maven仓库可以找到,可以用这个版本。在 pom 中指定 osgi lib 的 dependency 依赖 -->
      <groupId>org.eclipse</groupId>
      <artifactId>osgi</artifactId>
      <version>3.10.0-v20140606-1445</version>
      <scope>provided</scope>
    </dependency>
  </dependencies>
</project>

3)hellworld-client 子模块pom:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>helloworld</artifactId>
        <groupId>org.xxx.osgi</groupId>
        <version>1.0.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>helloworld-client</artifactId>
    <packaging>bundle</packaging>

    <name>helloworld-client</name>
    <url>http://maven.apache.org</url>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <!-- osgi lib 使用主 pom 中定义的依赖,子模块不再重复定义 osgi lib dependency -->
        <dependency>
            <groupId>org.xxx.osgi</groupId>
            <artifactId>helloworld-server</artifactId>
            <version>${project.version}</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <!-- osgi 打包配置,使用 maven-bundle-plugin 插件进行 osgi 打包 bundle jar -->
                <!-- 使用maven-bundle-plugin打包方式时指定manifest文件不生效,但可在 instructions 中配置 manifest 参数 -->
                <groupId>org.apache.felix</groupId>
                <artifactId>maven-bundle-plugin</artifactId>
                <version>${parent.maven.bundle.plugin.version}</version>
                <extensions>true</extensions>
                <configuration>
                    <instructions>
                        <!-- 如果要把依赖的 jar 也一起打包进去,在 Import-Package 中指定、并设置 Embed-Dependency -->
                        <!-- <Embed-Dependency>*;scope=compile|runtime</Embed-Dependency> -->
                        <Bundle-Name>${project.name}</Bundle-Name>
                        <Bundle-SymbolicName>$(replace;${project.artifactId};-;_)</Bundle-SymbolicName>
                        <Bundle-Version>${project.version}</Bundle-Version>
                        <Bundle-Activator>com.xxx.osgi.helloworld.client.Activator</Bundle-Activator>
                        <Import-Package>org.osgi.framework,com.xxx.osgi.helloworld.server;version=${project.version}"</Import-Package>
                        <Export-Package>
                            com.xxx.osgi.helloworld.client;version="${project.version}"
                        </Export-Package>
                    </instructions>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

4)helloworld-server 子模块pom:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>helloworld</artifactId>
        <groupId>org.xxx.osgi</groupId>
        <version>1.0.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>helloworld-server</artifactId>
    <packaging>bundle</packaging>

    <name>helloworld-server</name>
    <url>http://maven.apache.org</url>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <!-- osgi lib 使用主 pom 中定义的依赖,子模块不再重复定义 osgi lib dependency -->
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <!-- osgi 打包配置,使用 maven-bundle-plugin 插件进行 osgi 打包 bundle jar -->
                <!-- 使用maven-bundle-plugin打包方式时指定manifest文件不生效,但可在 instructions 中配置 manifest 参数 -->
                <groupId>org.apache.felix</groupId>
                <artifactId>maven-bundle-plugin</artifactId>
                <version>${parent.maven.bundle.plugin.version}</version>
                <extensions>true</extensions>
                <configuration>
                    <instructions>
                        <!-- 如果要把依赖的 jar 也一起打包进去,在 Import-Package 中指定、并设置 Embed-Dependency -->
                        <!-- <Embed-Dependency>*;scope=compile|runtime</Embed-Dependency> -->
                        <Bundle-Name>${project.name}</Bundle-Name>
                        <Bundle-SymbolicName>$(replace;${project.artifactId};-;_)</Bundle-SymbolicName>
                        <Bundle-Version>${project.version}</Bundle-Version>
                        <Bundle-Activator>com.xxx.osgi.helloworld.server.Activator</Bundle-Activator>
                        <Import-Package>org.osgi.framework</Import-Package>
                        <Export-Package>
                            com.xxx.osgi.helloworld.server;version="${project.version}"
                        </Export-Package>
                    </instructions>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

注意:

i)OSGI框架(OSGI Library)通过pom配置后自动manve刷新就可以自动在 Project Settings-> Modules 中自动生成 OSGI 配置了,包括OSGI Library也自设置了,这里无需手动修改其他OSGI配置,默认即可。根据pom自动生成的OSGI配置如下:

Configure OSGI Core Library 点击打开显示如下:

版本号就是pom中指定OSGI dependency 的版本。

ii)pom文件中配置打包插件使用:maven-bundle-plugin 插件,该插件是专门为OSGI打包提供的插件,但是它不能导出 META-INF 中的内容到 Export-Package jar 包中。也就是说使用 maven-bundle-plugin 插件打包导出的 bundle jar 包中的 manifest 只能通过 pom.xml 文件中的 maven-bundle-plugin 打包参数项来配置,不能直接指定使用自己项目中指定的 manifest 文件(指定了也不生效)。另外,Project Settings -> Modules 中的 Manifest Generation 配置也没有用。

另外,maven-bundle-plugin 打包插件支持了一个标签:
<Embed-Dependency>*;scope=compile|runtime</Embed-Dependency>,
有了这个标签,可以直接把依赖的 jar 打入 bundle jar 包中去。注意:这种方式仅对第三方依赖(dependency)有效,例如 pom 中j加入 mysql-connector-java 驱动依赖:

<dependency>
  <groupId>mysql</groupId>
  <artifactId>mysql-connector-java</artifactId>
  <version>8.0.15</version>
</dependency>

打包生成的bundle jar包中查看内容(jar tf xxx.jar),只要配置了 Embed-Dependency,对于 <dependency>定义的第三方依赖,都会打入目标bundle jar包中,直接把依赖的 mysql-connector-java-8.0.15.jar 文件导入进去了,位于根目录下:

但是,对于本地Lib库文件,例如下面这种依赖方式,Embed-Dependency 则无效,不会把 test-common-1.0.0.jar 打入bundle jar包中:

对于这种,可以使用另外一种方式,通过 Export-Package 导出。

还是用 mysql-connector-java 驱动来尝试,以本地Lib方式设置pom依赖(scope设置为system、并指定本地Lib文件路径),然后再设置 Export-Package 进行导出:

<Export-Package>
    com.xxx.osgi.helloworld.client;version="${project.version}",
    com.mysql.cj;version=8.0.32,
    com.mysql.jdbc;version=8.0.32
</Export-Package>

jar tf helloworld-client-1.0.0-SNAPSHOT.jar 查看bundle jar包结构如下:

 5)编译打包:

mvn clean package

执行命令,就会生成目标jar文件:

生成的jar内容结构查看,使用 jar tf jarFileName 命令查看:

e:\ws2\qf\helloworld\helloworld-client\target> jar tf helloworld-client-1.0.0-SNAPSHOT.jar

6)添加 debug / run 配置,可在idea中运行或调试:

为client和server分别添加一个 debug/run 配置,Bundle name配置中添加 4个 必须依赖的系统jar和各自子模块的jar:

添加 debug/run 配置并运行后,会自动生成  out 目录:

7)拷贝生成的 client & server bundle(jar) 到OSGI环境执行:

我本地Windows配置的OSGI运行环境位于:d:\osgi\equinox\

d:> cd d:\osgi\equinox\
d:\osgi\equinox> ls 
org.eclipse.osgi_3.18.600.v20231110-1900.jar
plugins
start.bat
d:\osgi\equinox> mkdir  my_bundles
d:\osgi\equinox> cp e:\ws2\qf\helloworld\helloworld-client\target\helloworld-client-1.0.0-SNAPSHOT.jar my_bundles\helloworld-client-1.0.0-SNAPSHOT.jar
d:\osgi\equinox> cp e:\ws2\qf\helloworld\helloworld-server\target\helloworld-server-1.0.0-SNAPSHOT.jar my_bundles\helloworld-server-1.0.0-SNAPSHOT.jar

8)执行bundles:

install & start bundles,server需要先启动、再启动client:

到此为止,基于Maven构建OSGI应用示例完毕。

注意:如果pom中指定<maven.compiler.source>8</maven.compiler.source>时编译报错:java: Compilation failed: internal java compiler error,报错截图如下:

检查下面几处相关设置是否正确:

1)检查File->Project Structure->Project Settings->Modules配置中的Dependencies->Module SDK

2)检查Settings->Buile,Execution,Deployment->Compiler->Java Compiler设置Module的Per-module bytecode version->Target bytecode version

如果 Per-module bytecode version -> Target bytecode version 不一致(我最初默认是1.5)、修改为一致(8),问题就解决了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/240397.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

JAVA BIO深入剖析

目录 JAVA BIO深入剖析1 Java BIO 基本介绍2 Java BIO 工作机制3 传统的BIO编程实例回顾客户端案例如下服务端案例如下小结 4 BIO模式下多发和多收消息客户端代码如下服务端代码如下小结 5 BIO模式下接收多个客户端概述客户端案例代码如下服务端案例代码如下小结 6 伪异步I/O编…

【C进阶】C程序是怎么运作的呢?-- 程序环境和预处理(下)

前言&#xff1a; 这是程序环境和预处理的下半篇文章。至此&#xff0c;关于c语言知识点:从编译到运行的过程已讲解完毕。传送&#x1f6aa;&#xff0c;上半篇&#xff1a; http://t.csdnimg.cn/hvxmr 本章涉及的知识点&#xff1a; 宏和函数对比、命名约定、#undef、命令行定…

Linux 系统 SSH 和 SCP 服务器搭建、配置、访问以及出现的问题

SSH是Secure Shell的缩写&#xff0c;是一种网络协议&#xff0c;用于通过本地或远程网络在计算机上进行远程登录和命令操作。SSH 是 Telnet 协议的演变&#xff1a;正如其名称所描述的&#xff0c;SSH 是安全的&#xff0c;并对通过网络传输的数据进行加密。 SSH 是目前较为可…

分布式-分布式事务理论、模型、方案、框架

一、分布式事务理论模型 分布式事务问题也叫分布式数据一致性问题&#xff0c;简单来说就是如何在分布式场景中保证多个节点数据的一致性。分布式事务产生的核心原因在于存储资源的分布性&#xff0c;比如多个数据库&#xff0c;或者MySQL和Redis两种不同存储设备的数据一致性…

5. PyTorch——数据处理模块

1.数据加载 在PyTorch中&#xff0c;数据加载可通过自定义的数据集对象。数据集对象被抽象为Dataset类&#xff0c;实现自定义的数据集需要继承Dataset&#xff0c;并实现两个Python魔法方法&#xff1a; __getitem__&#xff1a;返回一条数据&#xff0c;或一个样本。obj[in…

鸿蒙开发之状态管理@Prop和@Link

一、用法 在父子组件需要进行数据同步的时候&#xff0c;可以通过Prop和Link装饰器来做到。在父组件中用State装饰&#xff0c;在自组件中用Prop或Link装饰。 结论&#xff1a;Prop用于子组件只监听父组件的数据改变而改变&#xff0c;自己不对数据改变 Link用于子组件与父组…

Skype的介绍及使用

介绍及使用说明 Windows自带的Skype是一款全球通用的即时通讯软件&#xff0c;它可以让用户通过文字、语音和视频进行免费的在线沟通。下面是Skype的使用说明&#xff1a; 1.安装和登录&#xff1a;如果你的Windows系统中没有预装Skype&#xff0c;你可以在Microsoft官…

锁定屏幕与挂起

概要&#xff1a; 本篇主要讲述Ubuntu22.04中的锁定屏幕和挂起 锁定屏幕就是大家通常所说的息屏、锁屏&#xff0c;英文单词是lock 挂起一般也被称为休眠、睡眠&#xff0c;英文单词是suspend 一、锁定屏幕 1、CtrlL 按下键盘上的CtrlL键&#xff0c;即可锁定屏幕&#x…

【华为鸿蒙系统学习】- HarmonyOS4.0开发工具和环境配置问题总结|自学篇

&#x1f308;个人主页: Aileen_0v0 &#x1f525;热门专栏: 华为鸿蒙系统学习|计算机网络|数据结构与算法 &#x1f4ab;个人格言:"没有罗马,那就自己创造罗马~" 目录 官方链接 HUAWEI DevEco Studio和SDK下载和升级 | HarmonyOS开发者 安装教程 &#xff08;…

【PTA刷题+代码+详解】求二叉树度为1的结点个数(递归法)

文章目录 题目C代码详解 题目 在二叉树T中&#xff0c;其度为1的结点是指某结点只有左孩子或只有右孩子。利用递归方法求二叉树T的度为1的结点个数。 1&#xff09;如果TNULL&#xff0c;则是空树&#xff0c;度为1的结点个数为0&#xff0c;返回值为0&#xff1b; 2&#xff0…

Python爬虫实战 | 爬取拼多多商品的详情价格SKU数据

本案例将为大家演示如何爬取拼多多商品的详情数据。目的是爬取大量的商品以及商品的评论&#xff0c;所以在程序设计上要考虑到该爬虫的高并发以及持久化存储。爬虫工具选用了Scrapy框架&#xff0c;以满足爬虫的高并发请求任务&#xff1b;持久化存储用了MongoDB&#xff0c;对…

python:五种算法(SSA、WOA、GWO、PSO、GA)求解23个测试函数(python代码)

一、五种算法简介 1、麻雀搜索算法SSA 2、鲸鱼优化算法WOA 3、灰狼优化算法GWO 4、粒子群优化算法PSO 5、遗传算法GA 二、5种算法求解23个函数 &#xff08;1&#xff09;23个函数简介 参考文献&#xff1a; [1] Yao X, Liu Y, Lin G M. Evolutionary programming made…

vue 集成行政区域选择插件region和数据回显

故事&#xff1a;最近&#xff0c;项目需要进行行政区域围栏的绘制&#xff0c;由于老旧项目是利用js保存全国行政区域地址和编码&#xff0c;在选择器select进行匹配显示&#xff0c;但此方法复杂&#xff0c;因此选择集成区域插件region 步骤一&#xff1a;用命令安装region…

Vue3-09-条件渲染-v-show 的基本使用

v-show 的作用 v-show 可以根据条件表达式的值【展示】或【隐藏】html 元素。v-show 的特点 v-show 的实现方式是 控制 dom 元素的 css的 display的属性&#xff0c; 因此&#xff0c;无论该元素是否展示&#xff0c;该元素都会正常渲染在页面上&#xff0c; 当v-show 的 条件…

如何通过 SSH 访问 VirtualBox 的虚机

VirtualBox 是一款免费虚机软件。在用户使用它安装了 linux 以后&#xff0c;它默认只提供了控制台的管理画面。 直接使用控制台管理 Linux 没有使用诸如 putty 或者 vscode 这样的 ssh 远程管理工具方便。那么可不可以直接使用 ssh 访问 VirtualBox 上的 Linux 呢&#xff1f…

GNN 学习笔记

稍微看一下之后备用。 【图神经网络综述】GNN原理&#xff0b;落地应用实现框架全解_gnn实现-CSDN博客 GNN相比CNN最大的区别在于数据结构&#xff0c;CNN一般作用在二维、三维数据里&#xff0c;如图像、表格数据等&#xff0c;可以进行卷积操作。而GNN作用在一个由节点和边…

模拟目录管理 - 华为OD统一考试(C卷)

OD统一考试(C卷) 分值: 200分 题解: Java / Python / C++ 题目描述 实现一个模拟目录管理功能的软件,输入一个命令序列,输出最后一条命令运行结果。 支持命令: 1)创建目录命令: mkdir 目录名称,如mkdir abc为在当前目录创建abc目录,如果已存在同名目录则不执行任何操作…

案例055:基于微信小程序的四六级词汇

文末获取源码 开发语言&#xff1a;Java 框架&#xff1a;SSM JDK版本&#xff1a;JDK1.8 数据库&#xff1a;mysql 5.7 开发软件&#xff1a;eclipse/myeclipse/idea Maven包&#xff1a;Maven3.5.4 小程序框架&#xff1a;uniapp 小程序开发软件&#xff1a;HBuilder X 小程序…

超简单的新手重装Win10系统教程图解

如果我们的电脑系统出现问题了&#xff0c;那么就可以选择重装安装系统&#xff0c;轻轻松松解决系统问题&#xff0c;从而恢复对电脑的正常使用。但是&#xff0c;作为新手用户不懂很多的装机专业知识&#xff0c;所以重装系统的难度比较大&#xff0c;接下来小编给大家介绍超…

pytest-fixtured自动化测试详解

fixture的作用 1.同unittest的setup和teardown,作为测试前后的初始化设置。 fixture的使用 1.作为前置条件使用 2.fixture的的作用范围 1.作为前置条件使用 pytest.fixture() def a():return 3def test_b(a):assert a3 2.fixture的作用范围 首先实例化更高范围的fixture…