Apakah OSGi itu? Pendekatan berbeda untuk modularitas Java

OSGi memfasilitasi pembuatan dan pengelolaan komponen Java modular (disebut bundel ) yang dapat diterapkan dalam sebuah wadah. Sebagai pengembang, Anda menggunakan spesifikasi dan alat OSGi untuk membuat satu atau lebih bundel. OSGi menentukan siklus hidup untuk bundel ini. Itu juga menghosting mereka dan mendukung interaksi mereka dalam sebuah wadah. Anda dapat menganggap container OSGi secara kasar dianalogikan dengan JVM, dengan kekuatan tambahan. Demikian juga, anggap bundel sebagai aplikasi Java dengan kemampuan unik. Bundel dijalankan di dalam container OSGi sebagai komponen klien dan server.

Aliansi OSGi

OSGi dimulai pada tahun 1999, dan tidak seperti banyak spesifikasi lainnya, standar ini tidak dikelola oleh Oracle, Java Community Process, atau Eclipse Foundation. Sebaliknya, ini dikelola oleh aliansi OSGi.

Bagaimana OSGi berbeda

Filosofi OSGi berbeda dari kerangka kerja berbasis Java lainnya, terutama Spring. Di OSGi, beberapa aplikasi dapat berada dalam wadah yang sama: lingkungan runtime bundel OSGi . Penampung memastikan setiap komponen cukup diisolasi, dan juga memiliki akses ke dependensi apa pun yang diperlukannya. OSGi dapat mendukung injeksi ketergantungan, yang distandarisasi oleh proyek Aries Blueprint. Selain menyediakan wadah inversion of control (IoC) OSGi, Aries mendukung kerangka kerja Java standar seperti Java Persistence API (JPA).

Di OSGi, bundel dapat mengekspos layanan yang digunakan bundel lain. Sebuah bundel juga dapat mendeklarasikan sebuah versi, dan dapat menentukan bundle lain apa yang bergantung padanya. Runtime kemudian akan secara otomatis memuat semua bundelnya dalam urutan ketergantungan. Di OSGi, beberapa versi dari bundel yang sama dapat ada secara berdampingan, jika itu diperlukan oleh dependensi bundel.

OSGi di Eclipse IDE dan Equinox

OSGi telah ada dalam beberapa bentuk selama beberapa dekade. Ini digunakan untuk banyak aplikasi terkenal, dari perangkat seluler yang disematkan hingga server aplikasi dan IDE.

Eclipse IDE yang populer dibangun di atas OSGi. Implementasi Eclipse dari wadah OSGi disebut Equinox. Ini adalah contoh yang bagus untuk memahami OSGi. Didasarkan pada OSGi berarti Equinox adalah platform modular. Itu menjadi tuan rumah berbagai layanan yang dapat ditambahkan pengembang sesuka hati. Masing-masing menawarkan kemampuan yang mungkin dibutuhkan pengembang dalam IDE mereka. Anda dapat menambahkan editor untuk Java dan JavaScript, server aplikasi, dan konektor database. Masing-masing diimplementasikan sebagai bundel OSGi yang ditambahkan ke penampung dan dapat berinteraksi dengan layanan lain di penampung.

Baru-baru ini, ada peningkatan minat dalam menggunakan OSGi untuk Internet of Things (IoT). OSGi secara alami cocok untuk jenis pengembangan ini, yang memiliki berbagai komponen perangkat lunak yang berjalan berdampingan di perangkat, tanpa harus saling mengenal satu sama lain. Wadah OSGi menyediakan cara sederhana dan standar untuk menghosting komponen perangkat lunak dinamis ini.

Menggunakan OSGi dalam proyek Java: Knoplerfish OSGi

Kami akan bekerja melalui aplikasi contoh yang akan membuat konsep OSGi lebih konkret. Contoh kami didasarkan pada runtime OSGi Knoplerfish, yang digunakan di banyak penerapan produksi. Knoplerfish menyertakan GUI dan antarmuka baris perintah (CLI) untuk mengelola wadah OSGi dan bundelnya.

Hal pertama yang akan Anda lakukan adalah mengunduh Knoplerfish. Versi saat ini pada saat penulisan ini adalah Knoplerfish OSGi 6.1.3. Anda dapat mengganti versi tersebut dengan versi terbaru saat Anda membaca artikel ini.

Setelah Anda download dan diinstal Knoplerfish, menggunakan CLI untuk drop ke direktori di mana Anda download file JAR, dan masukkan: java -jar framework.jar. Itu akan menjalankan JAR yang dapat dieksekusi dan Anda akan disambut dengan jendela GUI.

GUI OSGi Knoplerfish

GUI Knoplerfish OSGi mungkin tampak luar biasa pada awalnya, tetapi dasarnya sederhana:

  • Di bagian atas layar adalah menu.
  • Di sebelah kiri adalah kumpulan bundel yang telah dimuat ke dalam runtime.
  • Di sebelah kanan adalah jendela informasi.
  • Di bagian bawah adalah konsol keluaran teks.
  • Di bagian paling bawah adalah konsol input.
Matthew Tyson

Ketik helpke konsol input jika Anda ingin melihat opsi bantuan.

Sebelum kita beralih ke contoh, lihat kumpulan bundel yang sedang berjalan. Anda akan melihat bundel yang disebut HTTP Server, yang berarti bundel yang menjalankan server HTTP sudah habis. Buka browser Anda, dan periksa // localhost: 8080. Benar saja, Anda akan melihat halaman web Knoplerfish.

Bundel 'Hello JavaWorld'

Mari gunakan runtime OSGi untuk membuat bundel sederhana, yang akan saya panggil Hello JavaWorld. Paket ini mengeluarkan pesan ke konsol.

Di Listing 1, kami menggunakan Maven untuk membangun bundel. Ini hanya memiliki satu ketergantungan, yang disediakan oleh aliansi OSGi.

Daftar 1. Ketergantungan OSGi di Maven POM

   org.osgi org.osgi.core   

Sekarang, kami juga akan menggunakan plug-in, milik proyek Apache Felix. Plug-in ini menangani pengemasan aplikasi sebagai bundel OSGi untuk digunakan. Kode 2 menunjukkan konfigurasi yang akan kita gunakan.

Kode 2. Plug-in OSGi Felix di Maven POM

   org.apache.felix maven-bundle-plugin true   org.javaworld.osgi org.javaworld.osgi.Hello     

Sekarang kita dapat melihat kelas sederhana yang akan menampilkan "Halo".

Kode 3. Bundel OSGi Halo JavaWorld

 package com.javaworld.osgi; import org.osgi.framework.BundleActivator; import org.osgi.framework.BundleContext; public class HelloJavaWorld implements BundleActivator { public void start(BundleContext ctx) { System.out.println("Hello JavaWorld."); } public void stop(BundleContext bundleContext) { } } 

Buat bundel dengan membuka baris perintah dan mengetik mvn clean install. Ini akan mengeluarkan file JAR yang berisi bundel. Sekarang, masuk ke Filemenu di Knoplerfish GUI, dan pilih Add Bundle. Ini akan menyediakan browser file. Temukan JAR yang baru saja kita buat dan pilih.

Mengelola bundel OSGi dalam wadah

In the output window of the Knoplerfish UI, you’ll see your “Hello, JavaWorld” message appear. Click on the bundle in the Knoplerfish GUI, and you can see the ID the container has assigned to it. When you are ready to stop the bundle, you could click the Stop menu item. Another way is to enter stop [bundle number] on the command line. You can manage bundles in the container using either the GUI or the command line.

Now you have a sense of how a simple bundle works in the OSGi container. Anywhere an OSGi container exists, you will find the same simplicity in starting and stopping bundles. OSGi creates an environment and lifecycle for the bundle.

Bundle Interactions: Services and clients

Next, we’ll look at how bundles communicate with each other.

The first thing we’ll do is create a service bundle. A service bundle is analogous to an EJB session bean: It provides a component that can be accessed by other bundles via a remote interface. To create a service bundle, we need to provide both an interface and an implementation class.

Listing 4. The service bundle interface

 package com.javaworld.osgi.service; public interface WhatIsOsgi { public Integer addNum(Integer x, Integer y); } 

Listing 4 is a simple interface. The only method is a addNum() method that will do what it implies: return the addition of two numbers. The implementation shown in Listing 5 is equally simple but adds a couple of OSGi-specific methods.

Listing 5. The service bundle implementation

 package com.javaworld.osgi.service; public class WhatIsOsgiImpl implements WhatIsOsgi, BundleActivator { private ServiceReference ref; private ServiceRegistration reg; @Override public Integer addNum(Integer x, Integer y){ return x + y; } @Override public void start(BundleContext context) throws Exception { reg = context.registerService( WhatIsOsgi.class, new WhatIsOsgiImpl(), new Hashtable()); ref = reg.getReference(); } @Override public void stop(BundleContext context) throws Exception { reg.unregister(); } } 

Let’s look closer at what’s happening in Listing 5:

  1. public class WhatIsOsgiImpl implements WhatIsOsgi, BundleActivator: Here we are implementing the interface we created. Note that we also implement the BundleActivator interface, as we did with the HelloJavaWorld example. The latter is because this bundle will activate itself.
  2. private ServiceReference ref; private ServiceRegistration reg;: These are variables for the OSGi registration service and the bundle reference for this service, respectively.
  3. public Integer addNum(Integer x, Integer y): This is the simple implementation of the add method.
  4. public void start(BundleContext context): This start method is part of the BundleActivator interface, and is executed by the container. In this example, we obtain a reference to the OSGi registration service and apply it to our WhatIsOsgi interface and implementation. The empty Hashtable is for config params, which we aren’t using here. We also get a reference to the service we have just created.
  5. public void stop(BundleContext context): Here, we simply unregister the service. This simple service just manages the barest elements of its lifecycle. Its main purpose is to expose the addNum method to the OSGi container.

The OSGi client

Next up, let’s write a client that can use the service. This client will again make use of the BundleActivator interface. It will also add the ServiceListener interface, as shown in Listing 6.

Listing 6. The OSGi service client bundle

 public class OsgiClient implements BundleActivator, ServiceListener { private BundleContext ctx; private ServiceReference service; public void start(BundleContext ctx) { this.ctx = ctx; try { ctx.addServiceListener(this, "(objectclass=" + WhatIsOsgi.class.getName() + ")"); } catch (InvalidSyntaxException ise) { ise.printStackTrace(); } } } 

Listing 6 has a start method that will add a service listener. This listener is filtered by the class name of the service we created in Listing 5. When the service is updated, it will call the serviceChanged() method, as shown in Listing 7.

Listing 7. serviceChanged method

 public void serviceChanged(ServiceEvent event) { int type = event.getType(); switch (type){ case(ServiceEvent.REGISTERED): serviceReference = event.getServiceReference(); Greeter service = (Greeter)(ctx.getService(service)); System.out.println("Adding 10 and 100: " + service.addNum(10, 100) ); break; case(ServiceEvent.UNREGISTERING): System.out.println("Service unregistered."); ctx.ungetService(event.getServiceReference()); // Releases reference to service so it can be GC'd break; default: break; } } 

Note that the serviceChanged method is used to determine what event has occurred for a service we are interested in. The service will then respond as specified. In this case, when the REGISTERED event appears, we make use of the addNum() method.

The OSGi alternative

This has been a quick introduction to OSGi, the Open Services Gateway Initiative. As you’ve seen through the Knoplerfish example, OSGi provides a runtime environment where you can define modular Java components (bundles). It provides a defined lifecycle for hosting bundles in the client, and it supports bundles interacting as clients and services within the container. All of these capabilities taken together provide an interesting alternative to standard Java runtimes and frameworks, especially for mobile and IoT applications.

Terakhir, perhatikan bahwa artikel sebelumnya dalam seri “Apa itu: Java” memperkenalkan Sistem Modul Platform Java, yang menawarkan pendekatan berbeda untuk tantangan modularitas Java yang sama.

Artikel ini, "Apa itu OSGi? Pendekatan berbeda untuk modularitas Java" awalnya diterbitkan oleh JavaWorld.