OSGi 란 무엇입니까? Java 모듈성에 대한 다른 접근 방식

OSGi 는 컨테이너에 배포 할 수있는 모듈 식 Java 구성 요소 ( 번들 이라고 함 )의 생성 및 관리를 용이하게 합니다. 개발자는 OSGi 사양 및 도구를 사용하여 하나 이상의 번들을 생성합니다. OSGi는 이러한 번들의 라이프 사이클을 정의합니다. 또한이를 호스팅하고 컨테이너에서 상호 작용을 지원합니다. OSGi 컨테이너는 추가 기능이있는 JVM과 거의 유사하다고 생각할 수 있습니다. 마찬가지로 번들을 고유 한 기능을 가진 Java 애플리케이션으로 생각하십시오. 번들은 OSGi 컨테이너 내에서 클라이언트 및 서버 구성 요소로 실행됩니다.

OSGi 동맹

OSGi는 1999 년에 시작되었으며 다른 많은 사양과 달리 표준은 Oracle, Java Community Process 또는 Eclipse Foundation에서 관리하지 않습니다. 대신 OSGi 얼라이언스에서 관리합니다.

OSGi의 차이점

OSGi의 철학은 다른 Java 기반 프레임 워크, 특히 Spring과 다릅니다. OSGi에서는 OSGi 번들 런타임 환경 이라는 동일한 컨테이너 내에 여러 애플리케이션이 존재할 수 있습니다 . 컨테이너는 각 구성 요소가 충분히 격리되고 필요한 종속성에 액세스 할 수 있는지 확인합니다. OSGi는 Aries Blueprint 프로젝트에 의해 표준화 된 종속성 주입을 지원할 수 있습니다. OSGi의 IoC (Inversion of Control) 컨테이너를 제공하는 것 외에도 Aries는 JPA (Java Persistence API)와 같은 표준 Java 프레임 워크를 지원합니다.

OSGi에서 번들은 다른 번들이 사용하는 서비스를 노출 할 수 있습니다. 번들은 또한 버전을 선언 할 수 있으며 종속 된 다른 번들을 정의 할 수 있습니다. 그런 다음 런타임은 종속성 순서대로 모든 번들을 자동으로로드합니다. OSGi에서는 번들 종속성에 필요한 경우 동일한 번들의 여러 버전이 나란히 존재할 수 있습니다.

Eclipse IDE 및 Equinox의 OSGi

OSGi는 수십 년 동안 어떤 형태로든 존재 해 왔습니다. 임베디드 모바일 장치에서 애플리케이션 서버 및 IDE에 이르기까지 잘 알려진 많은 애플리케이션에 사용됩니다.

인기있는 Eclipse IDE는 OSGi를 기반으로 구축되었습니다. Eclipse의 OSGi 컨테이너 구현을 Equinox라고합니다. OSGi를 이해하는 좋은 예입니다. OSGi를 기반으로한다는 것은 Equinox가 모듈 식 플랫폼임을 의미합니다. 개발자가 마음대로 추가 할 수있는 다양한 서비스를 호스팅합니다. 이들 각각은 개발자가 자신의 IDE에서 필요할 수있는 기능을 제공합니다. Java 및 JavaScript 용 편집기, 앱 서버 및 데이터베이스 커넥터를 추가 할 수 있습니다. 이들 각각은 컨테이너에 추가되고 컨테이너의 다른 서비스와 상호 작용할 수있는 OSGi 번들로 구현됩니다.

최근 사물 인터넷 (IoT)에 OSGi를 사용하는 것에 대한 관심이 높아지고 있습니다. OSGi는 이러한 유형의 개발에 적합하며, 서로에 대해 알 필요없이 다양한 소프트웨어 구성 요소가 장치에서 나란히 실행됩니다. OSGi 컨테이너는 이러한 동적 소프트웨어 구성 요소를 호스팅하는 간단하고 표준화 된 방법을 제공합니다.

Java 프로젝트에서 OSGi 사용 : Knoplerfish OSGi

OSGi 개념을보다 구체적으로 만드는 예제 응용 프로그램을 살펴 보겠습니다. 우리의 예는 많은 프로덕션 배포에 사용되는 Knoplerfish OSGi 런타임을 기반으로합니다. Knoplerfish에는 OSGi 컨테이너 및 해당 번들을 관리하기위한 GUI 및 명령 줄 인터페이스 (CLI)가 포함되어 있습니다.

가장 먼저 할 일은 Knoplerfish를 다운로드하는 것입니다. 이 글을 쓰는 시점의 현재 버전은 Knoplerfish OSGi 6.1.3입니다. 이 기사를 읽을 때 해당 버전을 최신 버전으로 바꿀 수 있습니다.

Knoplerfish를 다운로드하고 설치 한 후 CLI를 사용하여 JAR 파일을 다운로드 한 디렉토리로 이동하고 다음을 입력 java -jar framework.jar합니다.. 그러면 실행 가능한 JAR이 실행되고 GUI 창이 표시됩니다.

Knoplerfish OSGi GUI

Knoplerfish OSGi의 GUI는 처음에는 압도적으로 보일 수 있지만 기본은 간단합니다.

  • 화면 상단에는 메뉴가 있습니다.
  • 왼쪽에는 런타임에로드 된 번들 세트가 있습니다.
  • 오른쪽에는 정보 창이 있습니다.
  • 하단에는 텍스트 출력 콘솔이 있습니다.
  • 맨 아래에는 입력 콘솔이 있습니다.
매튜 타이슨

입력 help하면 도움말 옵션을보고 싶다면 입력 콘솔에.

예제로 이동하기 전에 실행중인 번들 세트를 살펴보십시오. 라는 번들이 표시되며 HTTP Server이는 HTTP 서버를 실행하는 번들이 작동 중임을 의미합니다. 브라우저로 이동하여 // localhost : 8080을 확인하십시오. 물론입니다. Knoplerfish 웹 페이지가 표시됩니다.

'Hello JavaWorld'번들

OSGi 런타임을 사용하여 간단한 번들을 빌드 해 보겠습니다 Hello JavaWorld. 이 번들은 콘솔에 메시지를 출력합니다.

목록 1에서는 Maven을 사용하여 번들을 빌드합니다. OSGi 얼라이언스에서 제공하는 하나의 종속성 만 있습니다.

목록 1. Maven POM의 OSGi 종속성

   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.

마지막으로, "What is : Java"시리즈의 이전 기사에서 Java 모듈화라는 동일한 문제에 대해 다른 접근 방식을 제공하는 Java Platform Module System을 소개했습니다.

"OSGi 란 무엇인가? Java 모듈성에 대한 다른 접근 방식"이라는이 이야기는 원래 JavaWorld에 의해 출판되었습니다.