Apa itu OSGi? Pendekatan yang berbeza untuk modulariti Java

OSGi memfasilitasi membuat dan mengurus komponen Java modular (disebut bundle ) yang dapat digunakan dalam wadah. Sebagai pembangun, anda menggunakan spesifikasi dan alat OSGi untuk membuat satu atau lebih kumpulan. OSGi menentukan kitaran hidup untuk kumpulan ini. Ia juga menjadi tuan rumah dan menyokong interaksi mereka di dalam bekas. Anda boleh menganggap bekas OSGi hampir sama dengan JVM, dengan kekuatan tambahan. Begitu juga, fikirkan kumpulan sebagai aplikasi Java dengan kemampuan unik. Kumpulan berjalan di dalam bekas OSGi sebagai komponen pelanggan dan pelayan.

Perikatan OSGi

OSGi bermula pada tahun 1999, dan tidak seperti banyak spesifikasi lain, standard tersebut tidak dikendalikan oleh Oracle, Java Community Process, atau Eclipse Foundation. Sebaliknya, ia dikendalikan oleh pakatan OSGi.

Bagaimana OSGi berbeza

Falsafah OSGi berbeza dengan kerangka kerja Java yang lain, terutamanya Spring. Dalam OSGi, banyak aplikasi boleh wujud dalam wadah yang sama: persekitaran waktu operasi bundle OSGi . Bekas memastikan setiap komponen terpencil dengan secukupnya, dan juga mempunyai akses ke pergantungan yang diperlukannya. OSGi dapat menyokong suntikan ketergantungan, yang diseragamkan oleh projek Aries Blueprint. Selain menyediakan wadah kawalan terbalik (IoC) OSGi, Aries menyokong kerangka kerja Java standard seperti Java Persistence API (JPA).

Dalam OSGi, kumpulan dapat memaparkan perkhidmatan yang digunakan kumpulan lain. Bundel juga dapat menyatakan versi, dan dapat menentukan kumpulan mana yang bergantung kepadanya. Masa berjalan kemudian akan memuatkan semua kumpulannya secara automatik mengikut urutan kebergantungan. Dalam OSGi, banyak versi bundle yang sama dapat wujud berdampingan, jika diperlukan oleh dependensi bundle.

OSGi dalam Eclipse IDE dan Equinox

OSGi telah wujud dalam beberapa bentuk selama beberapa dekad. Ini digunakan untuk banyak aplikasi terkenal, dari peranti mudah alih tertanam hingga pelayan aplikasi dan IDE.

Eclipse IDE yang popular dibina di atas OSGi. Pelaksanaan Eclipse terhadap wadah OSGi disebut Equinox. Ini adalah contoh yang baik untuk memahami OSGi. Berasaskan OSGi bermaksud Equinox adalah platform modular. Ia menyediakan pelbagai perkhidmatan yang dapat ditambahkan oleh pemaju sesuka hati. Setiap ini menawarkan kemampuan yang mungkin diperlukan oleh pemaju dalam IDE mereka. Anda mungkin menambahkan editor untuk Java dan JavaScript, pelayan aplikasi, dan penyambung pangkalan data. Setiap ini dilaksanakan sebagai bundle OSGi yang ditambahkan ke wadah dan dapat berinteraksi dengan perkhidmatan lain di dalam wadah.

Baru-baru ini, terdapat minat untuk menggunakan OSGi untuk Internet of Things (IoT). OSGi sangat sesuai untuk jenis pengembangan ini, yang mempunyai pelbagai komponen perisian yang berjalan berdampingan pada peranti, tanpa harus saling mengenali antara satu sama lain. Bekas OSGi menyediakan cara mudah dan standard untuk menjadi tuan rumah komponen perisian dinamik ini.

Menggunakan OSGi dalam projek Java: Knoplerfish OSGi

Kami akan mengusahakan aplikasi contoh yang akan menjadikan konsep OSGi lebih konkrit. Contoh kami berdasarkan pada runtime Knoplerfish OSGi, yang digunakan dalam banyak pengeluaran pengeluaran. Knoplerfish merangkumi GUI dan antara muka baris perintah (CLI) untuk menguruskan wadah OSGi dan kumpulannya.

Perkara pertama yang anda akan lakukan ialah memuat turun Knoplerfish. Versi terkini pada masa penulisan ini adalah Knoplerfish OSGi 6.1.3. Anda boleh mengganti versi itu dengan yang terkini semasa anda membaca artikel ini.

Setelah anda memuat turun dan dipasang Knoplerfish, menggunakan CLI untuk jatuh ke direktori di mana anda memuat turun fail JAR, dan masukkan: java -jar framework.jar. Itu akan menjalankan JAR yang boleh dilaksanakan dan anda harus disambut dengan tetingkap GUI.

GUI Knoplerfish OSGi

GUI Knoplerfish OSGi nampaknya luar biasa pada mulanya, tetapi asasnya mudah:

  • Di bahagian atas skrin terdapat menu.
  • Di sebelah kiri adalah himpunan kumpulan yang telah dimuat ke dalam jangka masa.
  • Di sebelah kanan adalah tetingkap maklumat.
  • Di bahagian bawah adalah konsol output teks.
  • Di bahagian paling bawah adalah konsol input.
Matthew Tyson

Ketik helpke konsol input jika anda ingin melihat pilihan bantuan.

Sebelum kita beralih ke contoh, lihatlah kumpulan bundel yang sedang berjalan. Anda akan melihat kumpulan yang dipanggil HTTP Server, yang bermaksud bahawa kumpulan yang menjalankan pelayan HTTP sudah habis. Pergi ke penyemak imbas anda, dan lihat // localhost: 8080. Sudah tentu, anda akan melihat laman web Knoplerfish.

Kumpulan 'Hello JavaWorld'

Mari gunakan runtime OSGi untuk membina kumpulan ringkas, yang akan saya panggil Hello JavaWorld. Bundel ini mengeluarkan mesej ke konsol.

Dalam Penyenaraian 1, kami menggunakan Maven untuk membina kumpulan. Ia hanya mempunyai satu ketergantungan, yang disediakan oleh pakatan OSGi.

Penyenaraian 1. Ketergantungan OSGi di Maven POM

   org.osgi org.osgi.core   

Now, we’re also going to use a plug-in, courtesy of the Apache Felix project. This plug-in takes care of packaging the app as an OSGi bundle for use. Listing 2 shows the configuration we’ll use.

Listing 2. OSGi Felix plug-in in the Maven POM

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

Now we can take a look at the simple class that will output a “Hello.”

Listing 3. Hello JavaWorld OSGi bundle

 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) { } } 

Build the bundle by going to the command line and typing mvn clean install. This will output a JAR file containing the bundle. Now, go to the File menu in the Knoplerfish GUI, and select Add Bundle. This will provide a file browser. Find the JAR we’ve just built and select it.

Managing OSGi bundles in the container

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.

Akhirnya, perhatikan bahawa artikel sebelumnya dalam seri "Apa itu: Java" memperkenalkan Sistem Modul Platform Java, yang menawarkan pendekatan yang berbeda untuk tantangan modularitas Java yang sama.

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