Spring(Java)からCORBAサービスを呼び出してみる

Springを使っているアプリからCORBAサービスを呼び出す必要にせまられそうなので、どうすればいいのかな〜と思ってちょっと試してみました。

# 検証バージョン

* Java SE 8 (JDK 1.8.0_121)
* Spring Boot 1.5.6.RELEASE (Spring Framework 4.3.10.RELEASE)

# JavaでCORBAサービスを作ってみる

まずIDLファイルを作ります。

```idl:Greeting.idl
module com {
  module example {
    module democorba {
      interface Greeting
      {
        string hello(in string message);
      };
    };
  };
};
```

つぎに、JDKから提供されているidljコンパイラを使ってJavaコードを生成します。

```
$ idlj -fall -td src/main/java src/main/resources/Greeting.idl
```

```
$ tree src/main/java
src/main/java
└── com
    └── example
        └── democorba
            ├── Greeting.java
            ├── GreetingHelper.java
            ├── GreetingHolder.java
            ├── GreetingOperations.java
            ├── GreetingPOA.java
            └── _GreetingStub.java
```

最後に、サービスの実装を行います。

```java
package com.example.democorba.server;

import com.example.democorba.GreetingPOA;

public class GreetingImpl extends GreetingPOA {

  public String hello(String message) {
    return "Hello !! (" + message + ")";
  }

}
```

# JavaでCORBAサービスを公開してみる

JDK(JRE)から提供されているorbdコマンドを利用して、作成したCORBAサービスを公開するためのクラスを作ります。

```java
package com.example.democorba.server;

import com.example.democorba.Greeting;
import com.example.democorba.GreetingHelper;
import org.omg.CORBA.ORB;
import org.omg.CORBA.ORBPackage.InvalidName;
import org.omg.CosNaming.NameComponent;
import org.omg.CosNaming.NamingContextExt;
import org.omg.CosNaming.NamingContextExtHelper;
import org.omg.CosNaming.NamingContextPackage.CannotProceed;
import org.omg.CosNaming.NamingContextPackage.NotFound;
import org.omg.PortableServer.POA;
import org.omg.PortableServer.POAHelper;
import org.omg.PortableServer.POAManagerPackage.AdapterInactive;
import org.omg.PortableServer.POAPackage.ServantNotActive;
import org.omg.PortableServer.POAPackage.WrongPolicy;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;

public class GreetingServer {

  public static final String[] ORB_OPTIONS = new String[]{"-ORBInitialPort", "1050", "-ORBInitialHost", "localhost"};

  public static void main(String args[]) throws IOException, InterruptedException {

    List<String> orbdStartupCommands = new ArrayList<>();
    orbdStartupCommands.add("orbd");
    orbdStartupCommands.addAll(Arrays.asList(ORB_OPTIONS));

    Process orbdProcess = new ProcessBuilder(orbdStartupCommands).start();
    Runtime.getRuntime().addShutdownHook(new Thread(() -> {
      System.out.println("GreetingService Exiting ...");
      orbdProcess.destroy();
    }));

    TimeUnit.SECONDS.sleep(1);

    try {
      bindService(ORB_OPTIONS);
    } catch (Exception e) {
      System.err.println("ERROR: " + e);
      e.printStackTrace(System.out);
    }

  }

  private static void bindService(String[] options) throws InvalidName, AdapterInactive, ServantNotActive, WrongPolicy, org.omg.CosNaming.NamingContextPackage.InvalidName, NotFound, CannotProceed {
    ORB orb = ORB.init(options, null);

    POA poa = POAHelper.narrow(orb.resolve_initial_references("RootPOA"));
    poa.the_POAManager().activate();

    GreetingImpl greeting = new GreetingImpl();

    org.omg.CORBA.Object ref = poa.servant_to_reference(greeting);
    Greeting href = GreetingHelper.narrow(ref);

    org.omg.CORBA.Object objRef =
      orb.resolve_initial_references("NameService");
    NamingContextExt ncRef = NamingContextExtHelper.narrow(objRef);

    NameComponent path[] = ncRef.to_name("Greeting");
    ncRef.rebind(path, href);

    System.out.println("GreetingService ready started and waiting request ...");

    orb.run();
  }

}
```

作成したクラスを実行すると・・・

```:コンソール
GreetingService ready started and waiting request ...
```

となります。

停止は、IDEが提供している停止機能や「Ctrl+C」を使ってください。停止すると・・・

```:コンソール
...
GreetingService Exiting ...
```

となります。

# JavaからCORBAサービスを呼び出してみる

CORBAサービスを呼び出すJUnitを作ってみます。

```java
package com.example.democorba;

import org.hamcrest.core.Is;
import org.junit.Assert;
import org.junit.Test;
import org.omg.CORBA.ORB;
import org.omg.CORBA.ORBPackage.InvalidName;
import org.omg.CosNaming.NamingContextExt;
import org.omg.CosNaming.NamingContextExtHelper;
import org.omg.CosNaming.NamingContextPackage.CannotProceed;
import org.omg.CosNaming.NamingContextPackage.NotFound;

public class GreetingTest {

  private static final String[] ORB_OPTIONS = new String[]{"-ORBInitialPort", "1050", "-ORBInitialHost", "localhost"};

  @Test
  public void test() throws InvalidName, CannotProceed, org.omg.CosNaming.NamingContextPackage.InvalidName, NotFound {
    ORB orb = ORB.init(ORB_OPTIONS, null);

    org.omg.CORBA.Object objRef =
      orb.resolve_initial_references("NameService");
    NamingContextExt ncRef = NamingContextExtHelper.narrow(objRef);

    Greeting greeting = GreetingHelper.narrow(ncRef.resolve_str("Greeting"));

    System.out.println("Obtained a handle on server object: " + greeting);

    String returnMessage = greeting.hello("I'm Kazuki!");

    Assert.assertThat(returnMessage, Is.is("Hello !! (I'm Kazuki!)"));

  }

}
```

> **Note:**
>
> クライアントで必要になるクラス・インタフェースは・・・
>
> * `Greeting`
> * `GreetingOperations`
> * `_GreetingStub`
> * `GreetingHelper`
>
> の4つです。

# Springの機能を使ってCORBAサービスを呼び出してみる

Spring Frameworkから提供されている`JndiRmiProxyFactoryBean`を使うと、CORBAサービスのインターフェースを実装したBeanをDIコンテナ登録することができます。（＝アプリケーションコードの中でCORBAを意識する必要はありません！！）

```java
package com.example.democorba;

import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.remoting.rmi.JndiRmiProxyFactoryBean;

import javax.naming.Context;
import java.util.Properties;

@SpringBootApplication
public class DemoCorbaApplication {

  public static void main(String[] args) {
    SpringApplication.run(DemoCorbaApplication.class, args);
  }

  @Bean
  CommandLineRunner demo(Greeting greeting) {
    return args -> System.out.println(greeting.hello("Hi Shimizu."));
  }

  @Bean
  JndiRmiProxyFactoryBean greeting() {
    JndiRmiProxyFactoryBean factoryBean = new JndiRmiProxyFactoryBean();
    Properties properties = new Properties();
    properties.setProperty(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.cosnaming.CNCtxFactory");
    properties.setProperty(Context.PROVIDER_URL, "iiop://localhost:1050");
    factoryBean.setJndiEnvironment(properties);
    factoryBean.setJndiName("Greeting");
    factoryBean.setServiceInterface(Greeting.class);
    return factoryBean;
  }

}
```

このクラスを実行すると・・・

```:コンソール
...

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v1.5.6.RELEASE)

2017-08-11 15:33:52.199  INFO 12777 --- [           main] c.e.democorba.DemoCorbaApplicationTests  : Starting DemoCorbaApplicationTests on Kazuki-no-MacBook-Pro.local with PID 12777 (started by shimizukazuki in /usr/local/apps/demo-corba)
2017-08-11 15:33:52.202  INFO 12777 --- [           main] c.e.democorba.DemoCorbaApplicationTests  : No active profile set, falling back to default profiles: default
2017-08-11 15:33:52.241  INFO 12777 --- [           main] s.c.a.AnnotationConfigApplicationContext : Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@3eb738bb: startup date [Fri Aug 11 15:33:52 JST 2017]; root of context hierarchy
Hello !! (Hi Shimizu.)
2017-08-11 15:33:52.906  INFO 12777 --- [           main] c.e.democorba.DemoCorbaApplicationTests  : Started DemoCorbaApplicationTests in 1.436 seconds (JVM running for 2.242)
2017-08-11 15:33:52.952  INFO 12777 --- [       Thread-3] s.c.a.AnnotationConfigApplicationContext : Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@3eb738bb: startup date [Fri Aug 11 15:33:52 JST 2017]; root of context hierarchy

```

となります。

> **Note:**
>
> クライアントで必要になるクラス・インタフェースは・・・
>
> * `Greeting`
> * `GreetingOperations`
> * `_GreetingStub`
>
> の3つです。（＝`GreetingHelper`は不要です）

ちなみに・・・テストを書くなら以下のような感じになります。

```java
package com.example.democorba;

import org.hamcrest.CoreMatchers;
import org.junit.ClassRule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.rule.OutputCapture;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest
public class DemoCorbaApplicationTests {

  @ClassRule
  public static OutputCapture outputCapture = new OutputCapture();

  @Test
  public void contextLoads() {
    outputCapture.expect(CoreMatchers.containsString("Hello !! (Hi Shimizu.)"));
  }

}
```

# まとめ

とりあえずサービス呼び出すだけなら楽勝？っぽいけど、こんなんでいいのかな〜？

# 参考サイト

* https://docs.oracle.com/javase/jp/8/docs/technotes/guides/idl/GShome.html
* https://docs.oracle.com/javase/jp/8/docs/technotes/guides/rmi-iiop/tutorial.html
* http://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/remoting/rmi/JndiRmiProxyFactoryBean.html