IoT アプリケーション構築用クラウド開発環境 PizzaFactory/Camerieri (Camerieri) の提供を開始いたしました。 Camerieri は,IoT アプリケーション構築用クラウド開発環境である Node-RED をクラウドホスティングする SaaS です。

東日本地区に置かれた 24hours x 7days 稼働のコンテナインスタンスを,30日間 2,500 円からで提供いたします。 詳細につきましては,ソリューション紹介のページ をご参照ください。

各サポートサービスの SLA において,有効期限を 90 日から 30 日に短縮いたしました。

本 SLA は,2016年12月8日午前0時以降に次回SLA更新までの期間に,各サポートサービスにお問い合わせ頂き契約したお客様に対し適用されます。 2016年12月8日午前0時よりも前にお問い合わせ頂いているお客様は,適用するLSAを以前のものまたは現在のもの,いずれかを契約時にご選択いただけます。

モデルベース開発を支援するための MWE2 コンポーネント群である A-Workflow を製品紹介に加えました。 2016年12月中に出荷を開始する予定です。ご期待ください。

弊社オンラインショップのリニューアル準備に伴い,Twitter アカウント @MonamiYaShop を閉鎖いたしました。 amazon.co.jp マーケットプレイスでおn弊社製品販売は継続しております。 引き続きのご愛顧を賜りますようお願い申し上げます。

Abstract

先日のホワイトペーパーでは,モデルワークフローを Pure Java で記述せずに DSL を使うことで得られるメリットを,具体的な実装を基に示しました。

しかし,「設計思想としては優れているものの,採用のコストに見合う効果が得られない」そんなフーレムワークも,ソフトウェアの世界では,しばしば見られるものです。

本稿は,そのような疑念を払拭するために執筆されました。 実存する Pure Java のモデル変換エンジンを題材に,部分的な MWE2 対応実装に対しメトリクス計測を行い,その結果を示します。

評価対象

先日のホワイトペーパーで例示した A-RTEGEN を利用します。

A-RTEGEN の全てを MWE2 に対応するのは不可能ではありませんが,それなりの工数がかかります。 そこで今回は,CONTRACT フェーズのワークフローに相当する部分のみを MWE2 相当に修正して比較します。

また,TOPPERSプロジェクトから配布されている A-RTEGEN はビルドシステムのサポートが貧弱でメトリクスを取りづらかったため,PizzaFactory プロジェクトが拡張した版をベースとしています。 この方針により,ソースコード行数など数値の厳密さは犠牲になっています。

A-RTEGEN は,モデルから自動生成されたソースコードを含み,ソースコード全体における割合も小さくありません。 そこで,評価対象は全体ではなく,今回の修正で大きな影響を受けた3つの plugin bundle のみとします。

以上のことからから,本稿が掲げる全ての数値は,相対的なもの,または傾向を示すものと捉えてください。

本稿のための MWE2 対応に要した工数は,1人で3.5時間です。 さらに時間をかければ,さらに MWE2 にとって有利な(つまりMWE2を採用した顧客が得られるメリットが高い)数値を引き出せます。 しかし,過度にチューニングしすぎた数値を提示しても現実と乖離してしまいますので,本稿では,敢えて時間に制約をかけました。

循環的複雑度

まず,ソフトウェアの保守性を示す指標である 循環的複雑度 を比較します。

循環的複雑度は,可読性や改変のしやすさといったプログラマの生産性に関わる情報を示すだけでなく,”全網羅するために最低限必要なテストケースの数”という品質活動に必要な情報を示します。

テストケースの爆発は,ランタイム側で既に起きています。 ランタイム側のテスト工数抑制を抑えるには,ツールに対する品質活動が欠かせません。 この流れに伴って,ツール側のテスト工数も増大の傾向にあります。 ツールの循環的複雑度の低減は,プロセス全体のテスト工数低減のために欠かせない課題です。

それぞれの plugin bundle における,総合・関数平均・クラス平均・ファイル平均の数値を比較すると,次のようになります。

Complexity : jp.ac.nagoya_u.is.nces.a_are.app

  Total Function Class File
Pure Java 124 3.4 9.5 10.3
MWE2 90 2.6 8.2 7.5

Complexity : jp.ac.nagoya_u.is.nces.a_are.codegen

  Total Function Class File
Pure Java 93 1.8 8.5 8.5
MWE2 91 1.8 8.3 8.3

Complexity : jp.ac.nagoya_u.is.nces.a_are.validation

  Total Function Class File
Pure Java 55 1.5 7.9 7.9
MWE2 49 1.7 7.0 7.0

MWE2 を用いた場合には,Pure Java に比べて循環的複雑度が低下する傾向が見られ,平均すると概ね 10% 弱程度の改善が見られます。

A-RTEGEN は,その背景にある AUTOSAR 仕様全体から見ると,RTE/Os/Schm という大事なソフトウェア部品を生成するツールであるものの,ツールという観点で見ると,他のランタイムコンポーネントのジェネレータに比べて格段に難しいことをしているというわけではありません。 つまりテストに要するコストは,MWE2 を使ったか Pure Java で組んだかによって,少なくとも 10% 程度の差がでます。

ここで,思い出してください。本稿の調査は,もともとは MWE2 を想定していなかったコードを 1人が 3.5 時間だけかけ,最低限の修正をかけたものに基づいています。 当初から MWE2 の存在を想定したコンポーネント設計を行っていた場合には,10% 弱の改善は,さらに良いスコアになります。

また,CONTRACT と GENERATE という2つのフェーズのうち,CONTRACT のみ修正していることにもご留意ください。 全てを修正することで,10% 弱の改善に,2倍前後のレバレッジがかかることになります。

AUTOSAR のランタイムコンポーネントを全て揃えるためには,20億円以上の開発費が必要と言われています。それらのうちテストが占める割合は不明ですが仮に3割として,その20%といえば,その費用的効果は実感して頂けるのではないでしょうか。

ソースコード行数

MWE2 がテスト工数の削減に繋がり得ることは示しました。 しかし,ソースコードの冗長性はどうでしょうか。 定性的に,フレームワークを採用すると,その流儀に合わせるためのコードが増え,ソースコードの量が増える傾向にあります。

LOC 実測値(コメントや空白行を除いたソースコード行数)を示します。

Lines Of Code

artifact-id Pure Java MWE2
jp.ac….app 660 508
jp.ac….codegen 467 461
jp.ac….validation 323 553

validation で行数が増えているのは,フレームワーク採用による弊害です。 この行数増加は抑制することも可能ですが,MWE2 スクリプトの作者のスキルレベル次第では,抑制し過ぎると使い勝手が悪くなるというトレードオフも存在します。

validation 以外の2つのバンドルに関しては,フレームワーク採用の割に,むしろ減っています。 MWE2 がワークフロー処理を肩代わりしたことによって,この現象は説明付けられます。

まとめ

ワークフローエンジンを採用した場合の保守性について,具体例を基に概説いたしました。 また,コード行数の増減についても軽く触れました。

モデルワークフローをともなうプロセスにおいて,ツールを Pure Java で作成するか MWE2 のようなワークフローエンジンを採用するかは,そのモデルやプロセスの規模感に大きく依存します。 AUTOSAR のような大規模なモデル仕様ならば MWE2 の採用を強くお勧めします。しかし,アジャイル・プロトタイピングのように初期の小規模な取り組みであれば,Pure Java を選択する余地はあるのかもしれません。

広告

MWE2 は,モデルワークフローの構築に大きなメリットをもたらしますが,MWE2 単体をインストールしただけではシステム構築は困難で,Eclipse プラグイン開発,EMF/ecore,Xtend/Xtext 等々の幅広い知識が必要となります。(いったんシステムが構築されてしまえば,利用者には深い知識は不要です)

合同会社もなみ屋では,Eclipse に関する10年の知見を,モデリングワークフローを構築しようとしている団体/企業に提供しています。ご興味をお持ちの方は,弊社サポート窓口 まで電子メールにてお問い合わせください。

Abstract

ソフトウェア開発の分野でモデルベース設計が広く用いられるに従って,上流の概念設計だけでなく,中流のソースコードや下流のテストコードまでを自動生成することが一般的になってきました。

このような開発プロセス全体にモデルが影響する場合には,上流から下流までの各段階でモデル変換を行いながら,詳細化,ならびにソースコード等成果物の出力を行います。 このようなモデルを軸とする開発の流れはモデリングワークフローと呼ばれます。 このようなモデル変換のフローを制御するソフトウェアはワークフローエンジンと呼ばれます。

従来,ワークフローエンジンは,Java や Ruby など汎用のプログラミング言語を用いて記述されてきました。 しかしながら,汎用プログラミング言語では,その記述の自由度が裏目に出てしまい,再利用性や記述の容易性が損なわれてしまいます。

そこで,大規模なモデルワークフローにおいては,独自の言語(DSL - Domain Specific Language)を定義し,再利用性や記述の容易性を担保しようという取組みが進められています。

本稿では,大規模なモデルワークフローを前提としている仕様である AUTOSAR を題材に,そのモデルワークフローエンジンを Pure Java で記述している実装例を分析し,課題を抽出します。 また,モデルワークフロー記述のための MWE2 言語を用いることで,課題が適切に解決されうることを示します。

前提

AUTOSAR の特徴

AUTOSAR は,欧州の自動車関連企業が中心となって仕様策定を進めている,車載ソフトウェアのための標準仕様です。AUTOSARは,下記のような特徴を持っています。

  • (補助的な外部モデルは存在するものの,概ね) 単一のメタモデルにより成立します。
  • OEM の設計から始まる V 字開発フローの大半が,メタモデルの中でモデル変換のワークフローとして形式化できます。

モデリングワークフロー構築時の留意点

モデリングワークフローは,開発プロセスの全体を串刺しするように用いられます。 開発プロセスの各段階で,特定の箇所が別の箇所の開発進捗を阻害するようなことがあると,プロセス全体が停滞するダウンリスクがあります。 そこで,ワークフローエンジンは,下記のような点に注意しなければなりません。

留意点A) ワークフローのコピーは,可能な限り減らすべきです。あるワークフローが変更された時の,他のワークフローへの影響分析コストが上昇します。

留意点B) ワークフローは分岐を可能な限り減らすべきです。分岐の多さ,すなわち高い循環的複雑度はテストケースの増大を招きます。

留意点C) ワークフローの各ワークノードが参照するリソースのスコープは,適切に設定されるべきです。ワークノード間の参照透明性が低いと,ワークフロー変更時の影響分析コストが上昇します。

留意点D) ワークフローエンジン,エンジンが処理するワークフローの実装,含まれるワークノードの実装は,それぞれ独立であるべきです。これらを分割統治することで,並列開発が可能となり,開発の規模に応じて開発者数をスケールさせることが可能となります。

Pure Java 実装例の分析

Pure Java での AUTOSAR 対応のモデリングワークフローエンジンの例示として,本稿では A-RTEGEN を用いることとします。

A-RTEGEN は名古屋大学大学院情報科学研究科付属 組込みシステム研究センター(NCES)が中心となって開発しており,ソースコードが公開されています。

A-RTEGEN の内部構造のうち,モデルワークフローを管理している部分のみを抽出すると,概ね図の通りになります。

GENERATE, CONTRACT 各フェーズの具体的なソースコードは https://github.com/PizzaFactory/a_rtegen/blob/1.3.0/src/jp.ac.nagoya_u.is.nces.a_rte.app/src/jp/ac/nagoya_u/is/nces/a_rte/app/internal/GeneratePhaseRteGenerator.java および https://github.com/PizzaFactory/a_rtegen/blob/1.3.0/src/jp.ac.nagoya_u.is.nces.a_rte.app/src/jp/ac/nagoya_u/is/nces/a_rte/app/internal/ContractPhaseRteGenerator.java にあります。

これらのコードは,先に一般論で挙げた留意点をことごとく満たしません。

留意点Aについて

まず,留意点A について,ソースコードの目視,また上掲の図を見ても理解可能なように CONTRACT / GENERATE の多くの箇所でソースクローンが存在しており満たせません。 クローン部分のみのテストが不可能な状態で,信頼性の担保が難しい状態になっています。

留意点Bについて

留意点B については,return 文による分岐,例外脱出による分岐,if 文による分岐が混合しており,また関数呼び出しのネストも存在します。 これらは Java 言語での記述としては自然です。しかし,ワークフローを Java で記述するという最上流の判断が間違えているといえます。

留意点Cについて

留意点C については,ソースコードのコンストラクタをご覧いただくと,満たせていないことが解ります。 使用するほぼすべてのオブジェクトがコンストラクタで生成されており,クラス内の処理から参照/変更が可能となっています。 車載ソフトウェアでもグローバル変数の使用は問題視されますが,同様の問題が,このコードには存在しています。

なお,使用するオブジェクトをコンストラクタで用意するのは,Java 言語での設計戦略としては悪くはありません。依存性注入のテクニックとしてコンストラクタインジェクションという技法があり,Java 言語の世界,特にエンタープライズ分野では広く使われています。 留意点Cを満たせない理由は,Java の単一クラスでワークフローを記述しようとした最上流の設計の失敗といえます。

留意点Dについて

留意点D については,A-RTEGEN が単一のアプリケーションとして提供されていることから,明らかに満たしていません。

MWE2 にすると何が変わるのか?

MWE2 は,最も狭義には,ワークフローエンジンです。広めに捉えると,ワークフローエンジンへの入力となる言語と,各ワークノードを実装するための API のセットと言えます。

A-RTEGEN の CONTRACT フェーズと等価な MWE2 言語記述は,下記のようになります。(イメージです。この記述は厳密には正しくなく,MWE2 は処理できません)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
var diagnostics = BasicDiagnostic {}
var loader = AutosarModelLoader {}
var GeneratorInitOptions initOptions 

/** 
 * CONTRACTフェーズ向けのRTEを生成する。
 */
Workflow {
	component = SystemOutPrintln {
		message = "Checking input AUTOSAR XMLs..."
	}
	component = InitResource {}
	component = LoadM2 {
		loader = loader
	}
	component = ValidateM2 {
		rteValidatorM2 = ModelValidator.forRteContractPhaseM2
		bswmValidatorM2 = ModelValidator.forBswmContractPhaseM2
		commonValidatorM2 = ModelValidator.forCommonContractPhaseM2
		diagnostics = diagnostics
	}
	component = LoadInstance {
		loader = loader
	}
	component = ValidateInstance {
		rteValidatorInstance = ModelValidator.forRteContractPhaseInstance
		bswmValidatorInstance = ModelValidator.forBswmContractPhaseInstance
		commonValidatorInstance = ModelValidator.forCommonContractPhaseInstance
		diagnostics = diagnostics
	}
	component = CheckDiagnostics {
		diagnostics = diagnostics
	}
	component = PrintPreBuildMessage {}
	component = BuildModuleModel {
		moduleModelBuilder = RteModuleModelBuilder {}
	}
	component = GenerateRte {
		codeGenerator = RteCodeGenerator {
			codeFormatter = UncrustifyCodeFormatter {
				executableFile = generatorInitOptions.uncrustifyExecutableFile
				configFile = generatorInitOptions.uncrustifyConfigFile
			}
		}
	}
	component = SystemOutPrintln {
		message = "Generation done." 
	}
	component = DumpModel {
		ignoreAbort = true
		modelFileName = "modeldump.xmi"
		serializer = ModelSerializer {}
	}
}

この記述には,component(ワークノード)の実行順序と,実行に際し必要となるオブジェクトの値設定が含まれます。

LoadInstance, ValidateInstance 等は,ワークノードです。これらは MWE2 自身では提供されず,MWE2 が提供する API を基に,実装する必要があります。 たとえば,LoadInstance は下記のように記述されます。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package jp.ac.nagoya_u.is.nces.a_rte.app.internal.workflow.contract

import org.eclipse.emf.mwe2.runtime.workflow.IWorkflowContext
import org.eclipse.xtend.lib.annotations.Accessors
import jp.ac.nagoya_u.is.nces.a_rte.persist.AutosarModelLoader
import jp.ac.nagoya_u.is.nces.a_rte.app.internal.workflow.ARteBuildAction

/**
 * AUTOSAR Instanceモデルの読み込み
 */
class LoadInstance extends ARteBuildAction {
	@Accessors var AutosarModelLoader loader

	override action(IWorkflowContext ctx) {
		this.loader.loadInstance(ctx.resource)
	}	
}

最終的には汎用言語で処理を記述する必要はありますが,処理の粒度を細かくすることで再利用性が高まります。 またワークフローの作成者とワークノードの作成者が分離することで,留意点Dが解決されます。

MWE2 のソースコードに戻ります。MWE2 スクリプトの初めに幾つかの大域変数が定義されていますが,これらは component から直接に参照することはできず,中括弧内で代入されることでのみ可視になります。 つまり留意点Cが解決されています。

MWE2 スクリプトには,例外脱出の処理記述がありません。これではエラー中断ができないように思われるかもしれません。 この問題は component に一枚の薄皮を被せることで解決可能です。

1
2
3
4
5
6
7
8
9
10
11
12
abstract class AutosarBuildAction implements IWorkflowComponent {
	static val ABORT_RETURN = "a-rtegen.abortReturn"
	@Accessors var boolean ignoreAbort = false
 
	/**
	 * Use `action` method instead of this.
	 */
	override final invoke(IWorkflowContext ctx) {
		if (ctx.get(ABORT_RETURN) == null || ignoreAbort) {
			action(ctx)
		}
	}

ワークノード中で例外が発生した場合,以降のすべてのワークノードは実行されない状態にし,かつエラー処理を行いたい場合は MWE2 ファイルに ignoreAbort=true と明示することで,強制的にワークノード内処理を行うようにします。 分岐のないワークフローであればこれで十分です。留意点 B が解決されます。

残るは 留意点 A についてです。 MWE2 を採用しても,CONTRACT / GENERATE それぞれのフェーズは,似てはいますが微妙に違います。つまり,似通っている 2つの mwe2 ファイルが必要になります。これは AUTOSAR 仕様に依存する事柄なのですが,同様のことは他のモデリングワークフローでも起こり得ることです。 ここは MWE2 のみを採用している範囲では解決できません。解決の方法は2通りありえます。

  • MWE2 に制御構文に相当する機能を付加する Viatra というツールを,Eclipse プロジェクトからダウンロードします。
  • CONTRACT / GENERAGE の両方の情報を内包するモデルから MWE2 を生成するようにします。

どちらかができたならば,留意点 A も解決します。

もし読者が最新版の AUTOSAR 仕様の愛読者であったならば,後者の方法でこの問題を解決する方法が暗に仕様記述されていることに気づくでしょう。

ここまでで,留意点AからDまですべての課題が解決されました。

まとめ

近年開発プロセスに浸透してきているモデリングワークフローを支えるツールを作成する際に,Pure Java で作成する場合の課題を実例を基に紹介し,ワークフロー記述用の DSL である MWE2 を用いることで課題を解消できることを紹介いたしました。

A-RTEGEN の実装者各位の名誉のために申し添えますが,Java アプリケーションとしての A-RTEGEN の品質は悪くはありません。目視によるレビューや静的解析ツールの結果も,それを裏付けています。 しかしながら,「Pure Java アプリケーションとしてワークフローを実装する」という最上流の方針決定に失敗しているとは言えます。 これは他の言語(Ruby を使った言語内 DSL など)を選んだとしても,汎用言語である限り同じ結果になりがちです。

合同会社もなみ屋は,ルネサスエレクトロニクス(株)が提供するR-INマイコンを軸に,ワールドワイドのインダストリー(産業界)を効率化、高付加価値化及び活性化を行うことを目的とする「R-INコンソーシアム」に参加いたしました. オープンソースを軸としたコンパイラ・デバッガ・RTOS・IDEといったソリューションを基盤に,mruby を始めとする次世代の産業界 (IoT, Industory4.0) に影響を及ぼしうる技術を携えて,様々なソリューションを引き続き提案していく所存です.