插件工程(Bundle)
依赖
<dependency>
<groupId>org.eclipse.tycho</groupId>
<artifactId>org.eclipse.osgi</artifactId>
<version>3.11.0.v20160603-1336</version>
</dependency>
IDEA中使用OSGI
编写代码
Bundle的启动类(implements BundleActivator)
package org.jsq;
import lombok.extern.slf4j.Slf4j;
import org.jsq.internal.PrintAdapter;
import org.jsq.internal.QueryAdapter;
import org.jsq.service.Adapter;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceRegistration;
import java.util.*;
@Slf4j
public class FirstBundleActivator implements BundleActivator {
private static final String CMD_NAME = "cmdname";
private List<ServiceRegistration<Adapter>> registrations = new ArrayList<>();
@Override
public void start(BundleContext context) throws Exception {
log.info("start to FirstBundleActivator");
registrations.add(context.registerService(Adapter.class, new PrintAdapter(), buildMetadata("print")));
log.info("register service of print");
registrations.add(context.registerService(Adapter.class, new QueryAdapter(), buildMetadata("query")));
log.info("register service of query");
}
@Override
public void stop(BundleContext context) throws Exception {
log.info("stop to FirstBundleActivator");
registrations.forEach(ServiceRegistration::unregister);
}
private Dictionary<String,String> buildMetadata(String value) {
return (Dictionary<String, String>) new Properties().setProperty(CMD_NAME, value);
}
}
Adapter接口(Bundle中相同位置也需要一份,且Bundle中需要导入)
package org.jsq.service;
import java.util.Map;
public interface Adapter {
Map<String, Object> run(Map<String, Object> data);
}
接口的各实现类
package org.jsq.internal;
import org.jsq.service.Adapter;
import com.google.common.collect.ImmutableMap;
import lombok.extern.slf4j.Slf4j;
import java.util.Map;
@Slf4j
public class PrintAdapter implements Adapter {
@Override
public Map<String, Object> run(Map<String, Object> data) {
log.info("start to print");
return ImmutableMap.of("result", "ok");
}
}
package org.jsq.internal;
import org.jsq.service.Adapter;
import com.google.common.collect.ImmutableMap;
import lombok.extern.slf4j.Slf4j;
import java.util.Map;
@Slf4j
public class QueryAdapter implements Adapter {
@Override
public Map<String, Object> run(Map<String, Object> data) {
log.info("start to query");
return ImmutableMap.of("result", "ok");
}
}
compile后build module,生成MANIFEST.MF
里面内容
Manifest-Version: 1.0
Bnd-LastModified: 1657447896210
Bundle-Activator: org.jsq.FirstBundleActivator
Bundle-ManifestVersion: 2
Bundle-Name: org.jsq
Bundle-SymbolicName: org.jsq
Bundle-Version: 1.0.0
Created-By: 1.8.0_161 (Oracle Corporation)
Export-Package: org.jsq;uses:="org.osgi.framework";version="1.0.0",org
.jsq.service;version="1.0.0"
Import-Package: com.google.common.collect,org.jsq.service,org.osgi.fra
mework,org.slf4j
Require-Capability: osgi.ee;filter:="(&(osgi.ee=JavaSE)(version=1.8))"
Tool: Bnd-5.1.2.202007211702
其中org.jsq.service是外部调用工程Adapter接口的存放位置,Import-Package是需要外面导入Bundle中是用的类(Bundle中由pom.xml引入),Export-Package是bundle中导出的内容,Bundle-Activator: org.jsq.FirstBundleActivator是启动类
代用Bundle工程
依赖
<dependency>
<groupId>org.eclipse.tycho</groupId>
<artifactId>org.eclipse.osgi</artifactId>
<version>3.11.0.v20160603-1336</version>
</dependency>
RESTful接口触发加载或刷新bundle.jar
@PostMapping("/execute")
public ResponseEntity<Map<String, Object>> execBundle(@RequestBody Map<String, Object> body) {
try {
log.info("body is {}",body);
BundleService instance = BundleService.getInstance();
Adapter adapter = instance.getAdapter((String) body.getOrDefault("cmdname", "print"));
if (null != adapter) {
Map<String, Object> result = adapter.run(body);
return ResponseEntity.ok(ImmutableMap.of("code", 0, "message", "success", "result", result));
}
} catch (RuntimeException e) {
log.error("start bundle error", e);
}
return ResponseEntity.ok(ImmutableMap.of("code", 1, "message", "failure"));
}
创建和启动osgi框架
public void startFramework() {
try {
log.info("start framework");
for (FrameworkFactory frameworkFactory : ServiceLoader.load(FrameworkFactory.class)) {
Map<String, String> config = new HashMap<>(2);
String importPackage = loadImportPackage(JAR_PATH);
config.put(Constants.FRAMEWORK_SYSTEMPACKAGES_EXTRA, importPackage);
config.put(Constants.FRAMEWORK_STORAGE_CLEAN, Constants.FRAMEWORK_STORAGE_CLEAN_ONFIRSTINIT);
log.info("config of framework is {}", config);
framework = frameworkFactory.newFramework(config);
framework.init();
framework.start();
}
} catch (IOException | BundleException e) {
log.error("Failed to start bundle", e);
throw new RuntimeException("Failed to start bundle", e);
}
}
加载或刷新Bundle.jar
private void refreshToLatestBundle(BundleVersionManager latestBundle) {
Optional<URI> bundleURI = latestBundle.getBundleURI();
if (bundleURI.isPresent()) {
Bundle bundle = framework.getBundleContext().getBundle(bundleURI.get().toString());
log.info("bundle is {}", bundle);
if (null != bundle) {
if (bundle.getVersion() == null || latestBundle.getBundleVersion() == null) {
return;
}
log.info("start to refresh bundle");
stopAndUninstallBundle(bundle);
}
installBundle(bundleURI);
}
}
private void installBundle(Optional<URI> bundleURI) {
try {
log.info("url of bundle is {}", bundleURI.get());
Bundle newBundle = framework.getBundleContext().installBundle(bundleURI.get().toString());
newBundle.start();
log.info("id is {},name is {}, version is {}, in {}, when {} ", newBundle.getBundleId(), newBundle.getSymbolicName(),
newBundle.getVersion(), newBundle.getLocation(), newBundle.getLastModified());
} catch (BundleException e) {
log.error("Failed to install Bundle", e);
}
}
private void stopAndUninstallBundle(Bundle bundle) {
try {
bundle.stop();
bundle.uninstall();
} catch (BundleException e) {
log.error("Failed to stop and uninstall bundle", e);
}
}
获得bundle中具体的Adapter类
public Adapter getAdapter(String name) {
try {
log.info("start to get Adapter");
for (Bundle bundle : framework.getBundleContext().getBundles()) {
log.info("id is {},name is {}, version is {}, in {}, when {} ", bundle.getBundleId(), bundle.getSymbolicName(), bundle.getVersion(), bundle.getLocation(), bundle.getLastModified());
if (bundle.getLocation().contains(JAR_PATH)) {
ServiceReference<?>[] serviceReferences = bundle.getBundleContext().getServiceReferences(Adapter.class.getName(), null);
log.info("serviceReferences is {}", serviceReferences);
if (null != serviceReferences && serviceReferences.length > 0) {
Adapter service = (Adapter) framework.getBundleContext().getService(serviceReferences[0]);
log.info("name of Adapter is {}",service.getClass().getName());
return service;
}
}
}
throw new BundleException("no Adapter");
} catch (InvalidSyntaxException | BundleException e) {
log.error("Failed to getAdapter", e);
}
return null;
}