情シスは何度でも甦るさ。

VBA!VBA!

C#でNPOIを使ってExcelを編集するとフォームボタンに割り当てたマクロが消える

最近、社内で1人しかSQLがわからないために、日々SQLをシコシコと実行する作業に嫌気がさして、 誰でもできるようにWindowsフォームアプリをC#で開発して、運用作業を他の人に振ることにした。

で、DBから抜いた値をExcelに取り込む → ユーザーに渡してユーザー側で編集 → 編集後ExcelをもらってDBの値を洗い替えるって、運用作業があるんですが、

Excel上のマクロを割り当てたフォームボタンが、いざ、NPOIを通して出力するとマクロの割り当てが初期化されるという事象にめぐり当たった。

これが

f:id:ryoben:20180428103139p:plain

こう

f:id:ryoben:20180428103144p:plain

色々と調べてStackOverFlowにも投稿があったのだが、NPOIの仕様で、Excelに直接貼り付けたフォームオブジェクトのマクロは消えるから、フォームにボタン貼り付けたりしようね。

とか、書いてた。

うーん、しかし、ボタンでさくっとマクロを起動したいのですよ。

色々と悩んだ挙句、Excelオープン時に、ボタンにマクロを割り当てるマクロを起動することで、とりあえず難を逃れることに。

Private Sub Auto_Open()
    ActiveSheet.Shapes.Range(Array("ボタン 1")).Select
    Selection.OnAction = "InputCheck"
End Sub

なんかもやっとするよね。

ちなみに、拡張子xmlのファイルだと、普通にボタンに割り当てたマクロはそのままになってたりした。

WindowsでJRubyでsinatraでRest API

既存システムとのリアルタイム連携

既存システムのリプレースにあたり、販売管理システムの在庫データをリアルタイムに覗きたいという要望がでてきた。

社内システムの連携は基本はファイルベースでバッチ処理で行っているのだが、それではリアルタイムという要件に合致しない。

なので、ここは、販売管理システムにAPI生やすのが良さそうだな。と考え至る。

販売管理システムの開発・保守してるベンダーにその旨を告げると、

「レスト?」「エーピーアイ?」

あぁ、ダメだ。人のこと言えないけど、これは、無理だ。

自分で作るか。ならば、Ruby、キミに決めた!

ただ、単純にSQLでSELECTして返すだけなので、自分でAPI作る方が、まだましか。お金かからんし。 とりあえず、動けばいいし、問題が出たらそん時考えるべ。ということで、自分のスキル的にはRubySinatraでちゃちゃっとプロトタイプ作ってみよう。と思ってみたが、

あ、うちの会社OS Windows縛りあるわ。

Windows Server で Rubyのサービス動かすのは、地雷源をコサックダンスしながら通り抜けるようなものだ。

という程度の認識はあったので、悩む。うーん。。。

帰り道、電車の窓から外を眺めたら、一羽の鳥が。

f:id:ryoben:20171223161432p:plain

「はっ、その手があったか!」

Windows上で、JRubySinatraまで

前置き長かったが、WindowsJavaのWebアプリなら普通に動くだろ。ということで、JRuby試してみる。 JRuby触ったこと無いから、色々検索してみるが、欲しい情報はなかなかないので、手探りでやることに。 なるべく、CRubyを操作するのと同じ流れを意識する。

事前準備
JDKをインストール
そもそも入れてたので、不要だったが。バージョンは既存のシステムの影響でJDK7(古っ)

Jrubyをインストール
Jrubyのサイトから JRuby 9.1.15.0 Windows Executable (x64) を選択してインストール。

bundler をインストール

 jruby -S gem install bundler

jrubyフォルダを作成し、移動して、Gemfile作成。

cd jruby

jruby -S bundle init

Gemfile を開いてsinatraを追加

 gem "sinatra"

bundle install

jruby -S bundle install --path vendor/bundle

app.rbを新規作成

require 'sinatra'

get '/' do
  'Hello Sinatra on Jruby'
end

ここで、 jruby -S bundle exec jruby app.rb

で動くかと想いきや、エラーにもならず、沈黙するのみ。 ので、rackupで動かすことに。

config.ru を新規作成

require 'sinatra'
require File.expand_path '../app.rb', __FILE__

run Sinatra::Application

起動

jruby -S bundle exec rackup

動いた。

f:id:ryoben:20171225141932p:plain

DBに接続する *1

MSのサイトからjdbcのドライバを落としてきて、解凍して中のjarファイルをコピってくる。

JDKのバージョンによって、2種類あるので、自分の環境に適した方のファイルをjrubyフォルダ直下にコピる(JDK9はないみたい・・・)

mssql-jdbc-6.2.2.jre7.jar
mssql-jdbc-6.2.2.jre8.jar

Gemfileに追記

gem "activerecord-jdbcmssql-adapter"

bundle install

jruby -S bundle install

動作検証も兼ねてapp.rb にDB接続のコードをハードコーディング。

app.rb 全書き換え

require 'activerecord-jdbcmssql-adapter'
require './mssql-jdbc-6.2.2.jre7.jar'

# define hash of connection properties
config = {
  url: 'jdbc:sqlserver://192.168.0.1\インスタンス名;databaseName=データベース名',
  adapter: 'jdbc',
  username: 'ユーザー名',
  password: 'パスワード',
  driver: 'com.microsoft.sqlserver.jdbc.SQLServerDriver',
}

# establish connection
ActiveRecord::Base.establish_connection( config )

# define model and inherit from ActiveRecord Base
class Stock < ActiveRecord::Base
  # override table name as necessary
  self.table_name = 'M_STOCK' #テーブル名
end

get '/' do
# query model table, examples:
  stock = Stock.first #何も考えず1件取得
  "倉庫コード:" + stock.WH_CODE + "  数量:" + stock.STOCK_QT.to_s
end

で、起動して、アクセスして、テーブルの値が取れてればOK。
f:id:ryoben:20171225234222p:plain

データ取れた。

接続設定を外部ファイルに切り出したり、ロジックをmodelに切り出したりは、やっとく。

API設計する

ココらへん読んで、URLのエンドポイントについては/stock とし、条件はパラメータとして設定する。 (バージョンはとりあえず、含めてない)
今回でいうと、倉庫と商品コードという条件がパラメータがあるので、以下のようなイメージとなる

localhost:9292/stocks?shop=999999&product=1755

Jsonについては、データを配列として返す。
検索結果の件数とかは、Jsonに書くのではなく、HTTPレスポンスヘッダーに書くのが流行りらしいのでそうする。
sinatraでは、以下のようにセットするとレスポンスヘッダーを返してくれる。

response.headers['X-Stock-Count'] = stock_count.to_s

Jsonの描画については、DBのカラム名Json上の定義名にギャップがあるので、to_jsonなどは使わず、viewsフォルダの配下にerbファイルを作成して値をセットするようにした。

エラーについては、ココらへんにある通り、httpのステータスコードを設定して返す。さらに、Json上でもシンプルなメッセージをセットして返すことにした。

ロットリングは、社内でしか使わない仕組みなので今のとこ対応しない。

warに固めてTomcat上で動かす

まず、この時点でのフォルダ構成はこんな感じになってる。

jruby
├app
│ └app.rb  ・・・main処理
│   
├config
│ └database.yml ・・・データベースの接続情報を外出し
│   
├lib
│ └mssql-jdbc-6.2.2.jre7.jar  ・・・sqlserver用のjdbcドライバ
│   
├log  ・・・ アプリケーションログ用フォルダ
│   
├models
│ └stock.rb ・・・ビジネスロジックを記述
│   
├vendor 
├views
│ ├errors.erb ・・・ エラーを返すJsonのテンプレート
│ └stocks.erb ・・・ stockを返すJsonのテンプレート
│   
├config.ru ・・・ 起動用スクリプト
├Gemgile
└Gemfile.lock

DBの接続情報は外出しして、環境によってDev環境かProductionかを自動で変わるようにした。

で、warblerを使って、warファイルを作成していく。

Gemfileに追加

gem "warbler"

bundle install実行

jruby -S bundle install

完了後、warblerのconfigファイルを作成する。

jruby -S bundle exec warble config

デフォルトで config/warble.rb が作成されるので、これを編集する。以下の記述を追加

  config.dirs = %w(app config lib views models log) #warに含むフォルダを指定。gemファイルのフォルダは不要
  config.bundle_without = []  # bundler用のおまじない

war作成

jruby -S bundle exec warble

で、実行フォルダに、jruby.war (フォルダ名.war)が作成される。

次にTomcatをダウンロード&インストール。
公式サイトから、32-bit/64-bit Windows Service Installer を選択してダウンロード。 今回は、Java7なので、8.x系を選択。 インストールは、exeを実行して、次へ次へで。

インストール完了後、管理画面を使うために、conf/tomcat-users.xml を編集。以下の記載を追記して、TOMCAT再起動。

  <role rolename="manager-gui"/>
  <user username="tomcat" password="tomcat" roles="manager-gui"/>

http://localhost:8080 にアクセスして、Manager Appを選択し、でID/Passに先ほど設定ファイルに追記した 値 を入力する。
デプロイ画面に遷移するので、war ファイルを選択し、アップロード&配備ボタン実行!

そして、いざアクセス
http://localhost:8080/jruby/stocks?shop=999999&product=2075

にブラウザでアクセスすると、以下のJsonがちゃんと返ってきた。

{
  "shop": 999999,
  "stocks": [
    {
      "product": "207554013",
      "color": "47",
      "size": "38",
      "count": 1
    },
    {
      "product": "207554013",
      "color": "47",
      "size": "40",
      "count": 1
    }
  ]
}

レスポンスヘッダーにも、ちゃんと件数が表示されてた。

Content-Length   1608
Content-Type    application/json
Date    Wed, 10 Jan 2018 09:43:21 GMT
X-Content-Type-Options  nosniff
X-Stock-Count   2

めでたしめでたし

AWSとOracle Cloudのサービス比較

今、クラウドにするならどこ?

と聞いたら、情シス的には、ほぼ100%AWSと答える。なぜなら、情シスには技術力はあまりないので、みんなが使ってるやつに乗っかりたいからである。

ただ、そうはいかないケースもある。既存の取引先とか、今のインフラの環境とかもろもろの事情で、Oracleクラウドも検討せなあかんときもある。

さらに、コストメリットでいうと、AWSより安いってラリーが言い切ってるし、現にOracle DBの費用だけの勝負ならオラクルクラウドの方が安い気もする。

でもさ、AWSのあの機能はOracle Cloudにあるの?

という疑問。いや、そもそも情シスごときが使いこなせないサービスも山ほどあるんだけどさ、でも、やっぱり今の世の中AWSと比較しないと判断がしづらいよね。

というわけで、AWSの主なサービス・機能でOracleクラウドで対応するサービスを洗い出してみました。

カテゴリー サービス AWS Oracle Cloud
ストレージ オブジェクトストレージ S3 Object Storage
永続ストレージ EBS Block Volumes
ファイルストレージ Elastic File System File Storage
アーカイブ Glacier Archive Storage
オンプレからのデータ転送 Storage Gateway Database Backup(oracleDBのみ)
コンピューティング 仮想OS EC2 Compute
サーバレス処理 Lambda ない。出ると噂
リソースの自動拡張 Auto Scaling Auto Scaling
データベース フルマネージドDB RDS Database Cloud Servis(DBCS)
KeyValueストア DynamoDB NoSQL Database Cloud Service*1
インメモリキャッシュ Elastic Cache Oracle Application Container Cloud Service(キャッシュ利用)
DWH基盤 Redshift Database Exadata Cloud Service
ネットワーク 仮想プライベートクラウド VPC Virtual Cloud Network(VCN)
CDN CloudFront ない
DNS Route53 DNS
専用線接続 Direct Connect FastConnect
ロードバランサ Elastic Load Balancing Load Balancing
開発者ツール ソースコントロール CodeCommit Developer Cloud Service
ビルドとテスト CodeBuild Developer Cloud Service
デプロイ自動化 CodeDeploy Developer Cloud Service
管理者ツール モニタリング CloudWatch Infrastructure Monitoring、Application Performance Monitoring
構成管理ツール CloudFormation、OpsWorks Orchestration
分析 MR EMR Big Data Cloud Service、Big Data SQL Cloud Service
ストリーミング Kinesis Event Hub
メッセージング メッセージキュー SQS Messaging
モバイル通知 SNS ない
Eメール配信 SES Email Delivery*2
セキュリティ ID管理 IAM Identity and Access Management(IAM)
ソフトウェア マーケットプレイス Marketplace Marketplace
デスクトップ仮想化 仮想デスクトップ Workspace ない
アプリ配信 AppStream ない
移行 データベース移行 Database Migration Service ない?
サーバ移行 Server Migration Service Ravello Cloud Service

第一印象としては、結構対応してきてるなぁという感じ。
実際使えるかどうかは別だが、急ピッチで機能追加もしており、Oracleの本気度は伝わってくる。

いや、でも、やっぱAWSがいいなぁ。

なお、これを調べて一番胸にぐっときたのは、Developer Cloud ServiceのCI機能として、ハドソンの名前があったこと。
誰か、もうやつを引退させてやってくれよ・・・。

*1:本番リリースはまだ

*2:本番リリースはまだ