简体   繁体   中英

What is the BKM to deploy OSGI bundles with new changes?

We have been using OSGI with Jboss 7.0.1 and have multiple bundles supporting our application. We are trying to adapt the versioning strategy of Major.Minor.Micro for Interface + Service Implementation + Consumer based model, but it seems our strategy might not be right.

When we bump up the minor version for api and service the consumer is unable to use the new service without having to do package refresh.

Below is the usecase.

Package is exported with version 1.0.0 from bundle (interface bundle) 是从捆绑包 (接口捆绑包)以1.0.0版导出的

public interface IHelloService {
    public void sayHello(String abc);
}

Menifest File for 清单文件

Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Helloworld API
Bundle-SymbolicName: exp1.com.helloworld.api
Bundle-Version: 1.0.0.qualifier
Bundle-Activator: com.helloworld.internal.Activator
Bundle-Vendor: ABC
Bundle-RequiredExecutionEnvironment: JavaSE-1.6
Import-Package: org.osgi.framework;version="1.3.0",
org.osgi.util.tracker;version="1.4.0"
Bundle-ClassPath: .
Export-Package: com.helloworld.api;version="1.0.0"

Implemented by from bundle 实现

public class HelloServiceImpl implements IHelloService {

@Override
  public void sayHello(String abc) {
      System.out.println(" \n\n ~~~~~~~~~ "+abc+" ~~~~~~~~~  " + "   \n\n");
  }
}

Menifest File for 清单文件

Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Helloworldservice
Bundle-SymbolicName: exp1.com.helloworld.service
Bundle-Version: 1.0.0.qualifier
Bundle-Activator: com.helloworldservice.internal.Activator
Bundle-Vendor: ABC
Bundle-RequiredExecutionEnvironment: JavaSE-1.6
Import-Package: com.helloworld.api;version="1.0.0",
 org.osgi.framework;version="1.3.0"
Bundle-ClassPath: 

Consumed by Consumer from bundle (Using service tracker or Google Guice approach). 的Consumer (使用服务跟踪器或Google Guice方法)。 It keeps on calling sayHello in loop.

HelloWorldServiceProxy.getInstance().getHelloWorldService().sayHello("abc  " + "Index" + i);

Menifest File for 清单文件

Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Consumer Service
Bundle-SymbolicName: exp1.com.consumer
Bundle-Version: 1.0.0.qualifier
Bundle-Activator: com.consumer.internal.Activator
Bundle-RequiredExecutionEnvironment: JavaSE-1.6
Import-Package: com.helloworld.api;version="1.0.0",
 org.osgi.framework;version="1.3.0",
 org.osgi.util.tracker;version="1.4.0"

After that we make the modification to api and add new method. This results into bumping up the minor version to 1.1.0. To support this we implement the new method in service but expect the consumer to be untouched.

package is exported with version 1.1.0 from bundle 从捆绑包导出为1.1.0版

public interface IHelloService {
   public void sayHello(String abc);
   public void sayHello(String abc, String def, int a);
}

Menifest File for 清单文件

Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Helloworld API
Bundle-SymbolicName: exp1.com.helloworld.api
Bundle-Version: 1.0.0.qualifier
Bundle-Activator: com.helloworld.internal.Activator
Bundle-Vendor: ABC
Bundle-RequiredExecutionEnvironment: JavaSE-1.6
Import-Package: org.osgi.framework;version="1.3.0",
org.osgi.util.tracker;version="1.4.0"
Bundle-ClassPath: .
Export-Package: helloworld.api;version="1.1.0"

Implemented by from bundle 实现

public class HelloServiceImpl implements IHelloService {
  @Override
  public void sayHello(String abc) {
    System.out.println(" \n\n ~~~~~~~~~ "+abc+" ~~~~~~~~~  " + "   \n\n");
  }
  @Override
  public void sayHelloNew(String abc, String def, int a) {
    System.out.println(" \n\n ~~~~~~~~~ "+abc+" NEW METHOD ### WITH CHANGE ### ~~~~~~~~~  " + abc +"  " + def +"   \n\n");
  }
}

Menifest File

Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Helloworldservice
Bundle-SymbolicName: exp1.com.helloworld.service
Bundle-Version: 1.0.0.qualifier
Bundle-Activator: com.helloworldservice.internal.Activator
Bundle-Vendor: ABC
Bundle-RequiredExecutionEnvironment: JavaSE-1.6
Import-Package: com.helloworld.api;version="1.1.0",
 org.osgi.framework;version="1.3.0"
Bundle-ClassPath:

However, now when we deploy HelloWorld Api and HelloWorld Service bundles, the consumer bundle is unable to start using the new service exported with package 1.1.0. We are having to refresh the consumer bundle in order to make it work.

Are we doing something wrong here ?

So far we have been deploying new bundles without any version (defaults to 0.0.0). Deploying new bundles with same package revision works fine for micro changes but any method signature change or new method usage results into NoSuchMethod exception until we perform the global package refresh from Jboss. That is the reason we decided to use Jboss version strategy.

After you install/update new bundles you should ALWAYS refresh. No exceptions. The only reason that refresh is not integrated with update/install is that you can now install/update multiple bundles before you refresh.

I am also a bit puzzled about your examples. You talk about semantic versioning but your imports are from [1.3.0,∞), so there is no semantic version in play? (It also helps to remove unnecessary information like most of the headers.)

So what is the best practice?

First, a service is defined by a package . For this service you can have two types of interfaces: interfaces implemented by the consumers and interfaces by the providers . A consumer interface is only changed in a non-backward compatible when for a major release bump. A provider interface is broken on a minor release bump. (major.minor.micro) OSGi supports the @ConsumerType (default) and @ProviderType annotations for an interface.

If you use bnd then you automatically get an import range. Ie if your IHello (better not append it with service) starts at 1.0:

 IHello.java:
 package com.example.hello;

 public interface IHello {
   void sayHell();
 }

 @Version("1.0.0")
 package-info.java:

 import org.osgi.annotation.versioning.Version;

Since the default case this is a @ConsumerType we will get the following import:

 Import-Package: com.example.hello;version='[1.0,2)'

However, the IHello interface likely represents a provider. So we should annotate it with the @ProviderType:

 IHello.java:
 package com.example.hello;

 import org.osgi.annotation.versioning.ProviderType;

 @ProviderType
 public interface IHello {
   void sayHell();
 }

If you now write a provider that implements the IHello service bnd will detect the version of the com.example.hello package. Since it sees that you implement it, it will write out the following import:

 Import-Package: com.example.hello;version='[1.0,1.1)'

If we had written a client of the IHello service our import would have been [1.0,2).

If we now add a method to IHello we should bump the version to 1.1 since IHello was a @ProviderType. (If it had been a @ConsumerType we would have been forced to bump it to 2.0)

 package-info.java:
 @Version("1.1.0")
 package-info.java:

 import org.osgi.annotation.versioning.Version;

 IHello.java:
 package com.example.hello;

 import org.osgi.annotation.versioning.ProviderType;

 @ProviderType
 public interface IHello {
   void sayHell();
   void sayHello(String s);
 }

We now effectively broke the provider of the IHello service, which is exactly what we want! This will prevent calling a 1.1 service that is backed by an implementation that was compiled against 1.0.

Summary:

  • refresh is mandatory after install/update.
  • Used bnd

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM