Въведение в OSGi

1. Въведение

Няколко критични за мисията приложения на Java и междинен софтуер имат някои твърди технологични изисквания.

Някои трябва да поддържат горещо разполагане, за да не нарушават работещите услуги - а други трябва да могат да работят с различни версии на един и същи пакет, за да поддържат външни наследени системи.

Платформите OSGi представляват жизнеспособно решение в подкрепа на този вид изисквания.

В инициативата Open Service Gateway е спецификация, определяща системен компонент на Java-базирани. Понастоящем се управлява от OSGi Alliance , а първата му версия датира от 1999 г.

Оттогава той се оказа чудесен стандарт за компонентни системи и е широко използван в наши дни. В Eclipse логическо устройство , например, е OSGi -базирани приложение.

В тази статия ще разгледаме някои основни характеристики на OSGi, използвайки изпълнението, предоставено от Apache .

2. Основи на OSGi

В OSGi един компонент се нарича пакет.

Логично, пакетът е част от функционалността, която има независим жизнен цикъл - което означава, че може да се стартира, спира и премахва независимо.

Технически пакетът е просто файл с jar с файл MANIFEST.MF, съдържащ някои заглавия, специфични за OSGi.

Платформата OSGi предоставя начин за получаване на известия за пакети, които стават достъпни или когато бъдат премахнати от платформата. Това ще позволи на правилно проектиран клиент да продължи да работи, може би с влошена функционалност, дори когато услугата, от която зависи, е моментално недостъпна.

Поради това пакетът трябва изрично да декларира до какви пакети трябва да има достъп и платформата OSGi ще го стартира само ако зависимостите са налични в самия пакет или в други пакети, вече инсталирани в платформата.

3. Получаване на инструментите

Ще започнем нашето пътуване в OSGi, като изтеглим последната версия на Apache Karaf от тази връзка. Apache Karaf е платформа, която изпълнява базирани на OSGi приложения; базира се на изпълнението на Apache на OSGi спецификация, наречена Apache Felix .

Karaf предлага някои удобни функции върху Felix, които ще ни помогнат да се запознаем с OSGi , например интерфейс на командния ред, който ще ни позволи да взаимодействаме с платформата.

За да инсталирате Karaf , можете да следвате инструкциите за инсталиране от официалната документация.

4. Входна точка на пакета

За да изпълним приложение в OSGi среда, трябва да го опаковаме като пакет OSGi и да дефинираме точката за влизане на приложението, а това не е обичайният метод за публична статична void (String [] args) .

И така, нека започнем с изграждането на базирано на OSGi приложение „Hello World”.

Започваме да настройваме проста зависимост от основния API на OSGi :

 org.osgi org.osgi.core 6.0.0 provided 

Зависимостта се декларира като предоставена, тъй като тя ще бъде достъпна по време на изпълнение на OSGi и пакетът не трябва да я вгражда.

Нека сега напишем простия клас HelloWorld :

public class HelloWorld implements BundleActivator { public void start(BundleContext ctx) { System.out.println("Hello world."); } public void stop(BundleContext bundleContext) { System.out.println("Goodbye world."); } }

BundleActivator е интерфейс, предоставен от OSGi, който трябва да бъде реализиран от класове, които са входни точки за пакет.

Методът start () се извиква от платформата OSGi при стартиране на пакета, съдържащ този клас. От друга страна stop () се извиква преди точно преди пакетът да бъде спрян.

Нека имаме предвид, че всеки пакет може да съдържа най-много един BundleActivator . Обектът BundleContext, предоставен и на двата метода, позволява взаимодействие с времето на изпълнение на OSGi . Скоро ще се върнем към него.

5. Изграждане на пакет

Нека модифицираме pom.xml и да го превърнем в действителен пакет OSGi.

На първо място, трябва изрично да заявим, че ще изградим пакет, а не буркан:

bundle

След това използваме приставката maven-bundle-с любезното съдействие на общността Apache Felix , за да опаковаме класа HelloWorld като пакет от OSGi :

 org.apache.felix maven-bundle-plugin 3.3.0 true    ${pom.groupId}.${pom.artifactId}  ${pom.name} ${pom.version}  com.baeldung.osgi.sample.activator.HelloWorld   com.baeldung.osgi.sample.activator    

В раздела с инструкции посочваме стойностите на заглавията на OSGi, които искаме да включим във файла MANIFEST на пакета.

Bundle-Activator е напълно квалифицираното име на изпълнението на BundleActivator, което ще се използва за стартиране и спиране на пакета, и се отнася до класа, който току-що написахме.

Private-Package не е заглавка на OSGi, но се използва, за да каже на приставката да включи пакета в пакета, но не и да го направи достъпен за други. Вече можем да изградим пакета с обичайната команда mvn clean install .

6. Инсталиране и стартиране на пакета

Нека започнем Karaf, като изпълним командата:

/bin/karaf start

където е папката, в която е инсталиран Karaf . Когато се появи подкана на конзолата Karaf, можем да изпълним следната команда за инсталиране на пакета:

> bundle:install mvn:com.baeldung/osgi-intro-sample-activator/1.0-SNAPSHOT Bundle ID: 63

Това инструктира Karaf да зареди пакета от локалното хранилище на Maven.

In return Karaf prints out the numeric ID assigned to the bundle that depends on the number of bundles already installed and may vary. The bundle is now just installed, we can now start it with the following command:

> bundle:start 63 Hello World

“Hello World” immediately appears as soon the bundle is started. We can now stop and uninstall the bundle with:

> bundle:stop 63 > bundle:uninstall 63

“Goodbye World” appears on the console, accordingly to the code in the stop() method.

7. An OSGi Service

Let's go on writing a simple OSGi service, an interface that exposes a method for greeting people:

package com.baeldung.osgi.sample.service.definition; public interface Greeter { public String sayHiTo(String name); }

Let's write an implementation of it that is a BundleActivator too, so we'll be able to instantiate the service and register it on the platform when the bundle is started:

package com.baeldung.osgi.sample.service.implementation; public class GreeterImpl implements Greeter, BundleActivator { private ServiceReference reference; private ServiceRegistration registration; @Override public String sayHiTo(String name) { return "Hello " + name; } @Override public void start(BundleContext context) throws Exception { System.out.println("Registering service."); registration = context.registerService( Greeter.class, new GreeterImpl(), new Hashtable()); reference = registration .getReference(); } @Override public void stop(BundleContext context) throws Exception { System.out.println("Unregistering service."); registration.unregister(); } }

We use the BundleContext as a mean of requesting the OSGi platform to register a new instance of the service.

We should also provide the type of the service and a map of the possible configuration parameters, which aren't needed in our simple scenario. Let's now proceed with the configuration of the maven-bundle-plugin:

 org.apache.felix maven-bundle-plugin true    ${project.groupId}.${project.artifactId}   ${project.artifactId}   ${project.version}   com.baeldung.osgi.sample.service.implementation.GreeterImpl   com.baeldung.osgi.sample.service.implementation   com.baeldung.osgi.sample.service.definition    

It's worth noting that only the com.baeldung.osgi.sample.service.definition package has been exported this time, through the Export-Package header.

Thanks to this, OSGi will allow other bundles to invoke only the methods specified in the service interface. Package com.baeldung.osgi.sample.service.implementation is marked as private, so no other bundle will be able to access the members of the implementation directly.

8. An OSGi Client

Let's now write the client. It simply looks up the service at startup and invokes it:

public class Client implements BundleActivator, ServiceListener { }

Let's implement the BundleActivator start() method:

private BundleContext ctx; private ServiceReference serviceReference; public void start(BundleContext ctx) { this.ctx = ctx; try { ctx.addServiceListener( this, "(objectclass=" + Greeter.class.getName() + ")"); } catch (InvalidSyntaxException ise) { ise.printStackTrace(); } }

The addServiceListener() method allows the client to ask the platform to send notifications about the service that complies with the provided expression.

The expression uses a syntax similar to the LDAP's one, and in our case, we're requesting notifications about a Greeter service.

Let's go on to the callback method:

public void serviceChanged(ServiceEvent serviceEvent) { int type = serviceEvent.getType(); switch (type){ case(ServiceEvent.REGISTERED): System.out.println("Notification of service registered."); serviceReference = serviceEvent .getServiceReference(); Greeter service = (Greeter)(ctx.getService(serviceReference)); System.out.println( service.sayHiTo("John") ); break; case(ServiceEvent.UNREGISTERING): System.out.println("Notification of service unregistered."); ctx.ungetService(serviceEvent.getServiceReference()); break; default: break; } }

When some modification involving the Greeter service happens, the method is notified.

When the service is registered to the platform, we get a reference to it, we store it locally, and we then use it to acquire the service object and invoke it.

When the server is later unregistered, we use the previously stored reference to unget it, meaning that we tell the platform that we are not going to use it anymore.

We now just need to write the stop() method:

public void stop(BundleContext bundleContext) { if(serviceReference != null) { ctx.ungetService(serviceReference); } }

Here again, we unget the service to cover the case in which the client is stopped before the service is being stopped. Let's give a final look at the dependencies in the pom.xml:

 com.baeldung osgi-intro-sample-service 1.0-SNAPSHOT provided   org.osgi org.osgi.core 6.0.0 

9. Client and Service

Let's now install the client and service bundles in Karaf by doing:

> install mvn:com.baeldung/osgi-intro-sample-service/1.0-SNAPSHOT Bundle ID: 64 > install mvn:com.baeldung/osgi-intro-sample-client/1.0-SNAPSHOT Bundle ID: 65

Always keep in mind that the identifier numbers assigned to each bundle may vary.

Let's now start the client bundle:

> start 65

Therefore, nothing happens because the client is active and it's waiting for the service, that we can start with:

> start 64 Registering service. Service registered. Hello John

What happens is that as soon as the service's BundleActivator starts, the service is registered to the platform. That, in turn, notifies the client that the service it was waiting for is available.

След това клиентът получава препратка към услугата и я използва, за да извика изпълнението, доставено чрез пакета от услуги.

10. Заключение

В тази статия разгледахме основните характеристики на OSGi с ясен пример, че е достатъчно, за да разберем потенциала на OSGi.

В заключение, когато трябва да гарантираме, че едно приложение трябва да бъде актуализирано без никакви лоши услуги, OSGi може да бъде жизнеспособно решение.

Кодът за тази публикация може да бъде намерен в GitHub.