JBoss Messagingでメッセージの永続化ストアを使わなくする方法
JBoss MessagingではデフォルトでHSQLを使ったJMSメッセージの永続化が設定されています。メッセージの永続化は、メッセージの配信を保障するために重要な機構ですが、開発や単体試験実行時に、サーバーを再起動してもキュー内のメッセージが消えないと非常に不便で、逆にトラブルの元になります。
サーバーディレクトリー\deploy\messaging
に格納されている、hsqldb-persistence-service.xmlを単純に削除し、
\docs\examples\jms\null-persistence-service.xml
を代わりにコピーするだけでは、以下の例外となってサーバー自体が起動しなくなります。
java.lang.RuntimeException: Unable to locate the transaction manager at org.jboss.tm.TransactionManagerLocator.locate(TransactionManagerLocator.java:134) at org.jboss.tm.TransactionManagerLocator.locate(TransactionManagerLocator.java:113) at org.jboss.messaging.core.jmx.JDBCServiceSupport.getTransactionManagerReference(JDBCServiceSupport.java:175) at org.jboss.jms.server.plugin.JDBCJMSUserManagerService.startService(JDBCJMSUserManagerService.java:74) at org.jboss.system.ServiceMBeanSupport.jbossInternalStart(ServiceMBeanSupport.java:376) at org.jboss.system.ServiceMBeanSupport.start(ServiceMBeanSupport.java:269) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) at java.lang.reflect.Method.invoke(Unknown Source) at org.jboss.mx.interceptor.ReflectedDispatcher.invoke(ReflectedDispatcher.java:157) at org.jboss.mx.server.Invocation.dispatch(Invocation.java:96) at org.jboss.mx.interceptor.AbstractInterceptor.invoke(AbstractInterceptor.java:138) at org.jboss.mx.server.Invocation.invoke(Invocation.java:90) at org.jboss.mx.interceptor.ModelMBeanOperationInterceptor.invoke(ModelMBeanOperationInterceptor.java:140) at org.jboss.mx.server.Invocation.invoke(Invocation.java:90) at org.jboss.mx.server.AbstractMBeanInvoker.invoke(AbstractMBeanInvoker.java:264) at org.jboss.mx.server.MBeanServerImpl.invoke(MBeanServerImpl.java:668) at org.jboss.system.microcontainer.ServiceProxy.invoke(ServiceProxy.java:206) at $Proxy38.start(Unknown Source) at org.jboss.system.microcontainer.StartStopLifecycleAction.installAction(StartStopLifecycleAction.java:42) at org.jboss.system.microcontainer.StartStopLifecycleAction.installAction(StartStopLifecycleAction.java:37) at org.jboss.dependency.plugins.action.SimpleControllerContextAction.simpleInstallAction(SimpleControllerContextAction.java:62) at org.jboss.dependency.plugins.action.AccessControllerContextAction.install(AccessControllerContextAction.java:71) at org.jboss.dependency.plugins.AbstractControllerContextActions.install(AbstractControllerContextActions.java:51) at org.jboss.dependency.plugins.AbstractControllerContext.install(AbstractControllerContext.java:348) at org.jboss.system.microcontainer.ServiceControllerContext.install(ServiceControllerContext.java:286) at org.jboss.dependency.plugins.AbstractController.install(AbstractController.java:1631) at org.jboss.dependency.plugins.AbstractController.incrementState(AbstractController.java:934) at org.jboss.dependency.plugins.AbstractController.resolveContexts(AbstractController.java:1082) at org.jboss.dependency.plugins.AbstractController.resolveContexts(AbstractController.java:984) at org.jboss.dependency.plugins.AbstractController.change(AbstractController.java:822) at org.jboss.dependency.plugins.AbstractController.change(AbstractController.java:553) at org.jboss.system.ServiceController.doChange(ServiceController.java:688) at org.jboss.system.ServiceController.start(ServiceController.java:460) at org.jboss.system.deployers.ServiceDeployer.start(ServiceDeployer.java:163) at org.jboss.system.deployers.ServiceDeployer.deploy(ServiceDeployer.java:99) at org.jboss.system.deployers.ServiceDeployer.deploy(ServiceDeployer.java:46) at org.jboss.deployers.spi.deployer.helpers.AbstractSimpleRealDeployer.internalDeploy(AbstractSimpleRealDeployer.java:62) at org.jboss.deployers.spi.deployer.helpers.AbstractRealDeployer.deploy(AbstractRealDeployer.java:50) at org.jboss.deployers.plugins.deployers.DeployerWrapper.deploy(DeployerWrapper.java:171) at org.jboss.deployers.plugins.deployers.DeployersImpl.doDeploy(DeployersImpl.java:1439) at org.jboss.deployers.plugins.deployers.DeployersImpl.doInstallParentFirst(DeployersImpl.java:1157) at org.jboss.deployers.plugins.deployers.DeployersImpl.doInstallParentFirst(DeployersImpl.java:1178) at org.jboss.deployers.plugins.deployers.DeployersImpl.install(DeployersImpl.java:1098) at org.jboss.dependency.plugins.AbstractControllerContext.install(AbstractControllerContext.java:348) at org.jboss.dependency.plugins.AbstractController.install(AbstractController.java:1631) at org.jboss.dependency.plugins.AbstractController.incrementState(AbstractController.java:934) at org.jboss.dependency.plugins.AbstractController.resolveContexts(AbstractController.java:1082) at org.jboss.dependency.plugins.AbstractController.resolveContexts(AbstractController.java:984) at org.jboss.dependency.plugins.AbstractController.change(AbstractController.java:822) at org.jboss.dependency.plugins.AbstractController.change(AbstractController.java:553) at org.jboss.deployers.plugins.deployers.DeployersImpl.process(DeployersImpl.java:781) at org.jboss.deployers.plugins.main.MainDeployerImpl.process(MainDeployerImpl.java:702) at org.jboss.system.server.profileservice.repository.MainDeployerAdapter.process(MainDeployerAdapter.java:117) at org.jboss.system.server.profileservice.repository.ProfileDeployAction.install(ProfileDeployAction.java:70) at org.jboss.system.server.profileservice.repository.AbstractProfileAction.install(AbstractProfileAction.java:53) at org.jboss.system.server.profileservice.repository.AbstractProfileService.install(AbstractProfileService.java:361) at org.jboss.dependency.plugins.AbstractControllerContext.install(AbstractControllerContext.java:348) at org.jboss.dependency.plugins.AbstractController.install(AbstractController.java:1631) at org.jboss.dependency.plugins.AbstractController.incrementState(AbstractController.java:934) at org.jboss.dependency.plugins.AbstractController.resolveContexts(AbstractController.java:1082) at org.jboss.dependency.plugins.AbstractController.resolveContexts(AbstractController.java:984) at org.jboss.dependency.plugins.AbstractController.change(AbstractController.java:822) at org.jboss.dependency.plugins.AbstractController.change(AbstractController.java:553) at org.jboss.system.server.profileservice.repository.AbstractProfileService.activateProfile(AbstractProfileService.java:306) at org.jboss.system.server.profileservice.ProfileServiceBootstrap.start(ProfileServiceBootstrap.java:271) at org.jboss.bootstrap.AbstractServerImpl.start(AbstractServerImpl.java:461) at org.jboss.Main.boot(Main.java:221) at org.jboss.Main$1.run(Main.java:556) at java.lang.Thread.run(Unknown Source)
解決策は以下に載っていました。
Persistence Manager Problem - null-persistence-... |JBoss Developer
ステップ1
サーバーディレクトリー\deploy\messaging\hsqldb-persistence-service.xml
を削除する。
ステップ2
サーバーディレクトリー\deploy\messaging\messaging-jboss-beans.xml
の10行からの以下のタグをコメントアウト。
<application-policy xmlns="urn:jboss:security-beans:1.0" name="messaging"> <authentication> <login-module code="org.jboss.security.auth.spi.DatabaseServerLoginModule" flag="required"> <module-option name="unauthenticatedIdentity">guest</module-option> <module-option name="dsJndiName">java:/DefaultDS</module-option> <module-option name="principalsQuery">SELECT PASSWD FROM JBM_USER WHERE USER_ID=?</module-option> <module-option name="rolesQuery">SELECT ROLE_ID, 'Roles' FROM JBM_ROLE WHERE USER_ID=?</module-option> </login-module> </authentication> </application-policy>
ステップ3
サーバーディレクトリー\conf\login-config.xml
に、以下を追加。
<application-policy name="messaging"> <authentication> <login-module code="org.jboss.security.auth.spi.UsersRolesLoginModule" flag="required"> <module-option name = "unauthenticatedIdentity">guest</module-option> <module-option name="usersProperties">props/messaging-users.properties</module-option> <module-option name="rolesProperties">props/messaging-roles.properties</module-option> </login-module> </authentication> </application-policy>
ステップ4
以下の内容をnull-persistence-service.xmlとして、
<?xml version="1.0" encoding="UTF-8"?> <!-- Null persistence config. Use this if you don't actually want to persist anything $Id$ --> <server> <!-- Persistence Manager MBean configuration ======================================== --> <mbean code="org.jboss.messaging.core.jmx.NullPersistenceManagerService" name="jboss.messaging:service=PersistenceManager" xmbean-dd="xmdesc/NullPersistenceManager-xmbean.xml"/> <!-- Messaging Post Office MBean configuration ========================================= --> <mbean code="org.jboss.messaging.core.jmx.MessagingPostOfficeService" name="jboss.messaging:service=PostOffice" xmbean-dd="xmdesc/MessagingPostOffice-xmbean.xml"> <depends optional-attribute-name="ServerPeer">jboss.messaging:service=ServerPeer</depends> <depends optional-attribute-name="TransactionManager">jboss:service=TransactionManager</depends> <!-- The name of the post office --> <attribute name="PostOfficeName">JMS post office</attribute> <!-- This post office is clustered. If you don't want a clustered post office then set to false --> <attribute name="Clustered">false</attribute> </mbean> <!-- Messaging JMS User Manager MBean config ======================================= --> <mbean code="org.jboss.jms.server.plugin.JDBCJMSUserManagerService" name="jboss.messaging:service=JMSUserManager" xmbean-dd="xmdesc/JMSUserManager-xmbean.xml"> <depends optional-attribute-name="TransactionManager">jboss:service=TransactionManager</depends> </mbean> </server>
サーバーディレクトリー\deploy\messaging
に格納。docsフォルダーに格納されているものと違うため注意が必要です。(クラスター設定がないことと、TransactionManagerに対する依存が追加されている。)
Java EEや.NETはCOBOLやVB6よりも本当に生産性が高いか?
プログラミングと設計は本来切り離せないものなのではがすごい反響だったのですが、結局この記事で私が言いたかったことは、
- Java EEなどの現代的な開発環境はCOBOLなどの古い言語を使った開発とは根本的に設計の手法が異なる
- 多くの現場では未だに古い設計手法を使っているため、オブジェクト指向などの最近の開発環境のメリットが活用できず、低い生産性にとどまっている。
ということに要約できると思います。ただし、どうして、Javaではオブジェクト指向で開発しないといけないのか、どうして昔ながらの伝統的なやり方を改め、新しい設計手法を採り入れないといけないのかと疑問を持たれた方もいらっしゃるかもしれません。ここでは、開発手法と生産性の問題について、もう少し掘り下げて検討してみたいと思います。
レガシー言語の生産性
最近のCOBOLでは、オブジェクトやスタック変数すら使えますが、ここではCOBOL85のような伝統的な手続き指向の言語についてちょっと考えてみます。最近はCOBOLで書かれたプログラムを読んだことがまったくないプログラマーも珍しくないかもしれませんが、固定長ファイルの読み書きや帳票の出力など、もともとCOBOLの得意分野については、下手なJavaプログラムよりよほど生産性が高いのではないかとすら思えます。たとえば、固定長ファイルを処理するロジックなら大体以下のような感じで記述できます。
IDENTIFICATION DIVISION. PROGRAM-ID. SAMPLE. * 環境部 ENVIRONMENT DIVISION. INPUT-OUTPUT SECTION. FILE-CONTROL. SELECT DATA-FILE ASSIGN TO infile ORGANIZATION IS LINE SEQUENTIAL. * データ部 DATA DIVISION. FILE SECTION. FD DATA-FILE. 01 DATA-RECORD. 02 USER-RECORD. 03 USER-CODE PIC X(4). 03 PIC X. 03 USER-NAME PIC X(20). 03 PIC X. 03 USER-AGE PIC 9(3). * 手続き部 PROCEDURE DIVISION. OPEN INPUT DATA-FILE. LOOP-POINT. READ DATA-FILE AT END GO TO TERM-PROC ... 各レコードの処理を記述 GO TO LOOP-POINT. TERM-PROC. CLOSE DATA-FILE. END PROGRAM SAMPLE.
確かに、最近のスクリプト言語とは比べ物にならないくらい冗長な記述ではありますが、固定長ファイルを処理するという本質的な部分についてみると
- ファイルの中身が特定の変数に自動的にバインドされている
- 変数の型変換も自動的に処理される
などを考えると、最近Javaでようやく利用できるようになったアノテーションを使ったBeanバインディングと大差ないようにも思えます。見方によってはPIC句による文字種や桁数の宣言はソース内部に記述されたメタデータといえなくない気がしますし。また、決まりきった部分をテンプレートとして流用すれば*1、可変部分の記述はシンプルで、IoC*2のように見えなくもないです。そのほか、COBOLでは固定小数演算で誤差のない金額計算などが得意といった特徴もあります。とにかく、COBOLでは細かい部分の設計としてSEがデータの型桁と処理ロジックを決め、アーキテクチャー的な部分は大規模なプログラムのモジュール分割や呼び出し順序の設計をすれば、基本的にはひたすらコードを書き下すだけです。*3インターフェースとかデザインパターンなどは本当に考える余地がないのです。変数も基本的にグローバルな静的変数となります。同じ第三世代の手続き型言語といってもCやPascalのような柔軟性の高い汎用言語しか知らないとCOBOL独特のこうした特徴は理解しづらいでしょう。COBOLというのは事務処理フレームワークが内蔵された言語と言えます。キーワード(予約語)が500種類*4もあるという点もこの事実を物語っています。
同様に、VB6などのツールはGUIを作成するツールとしては生産性が非常に高く、データアクセス用の部品を使えばCRUD処理などは簡単に実現できてしまいます。このようなことを考えると最近では開発ツールの使い勝手が向上したり、PCの性能が向上したりしたことによる生産性の向上はあるのですが、言語自体の生産性は昔からそれほど代わっていないのではないかという意見も当然出てくると思います。だから、最近でも
などといった意見も時々出てくるのだと思います。
どうしてオブジェクト指向言語なのか?
それではどうしてJavaなどのオブジェクト指向言語が現在では一般的になっているのでしょうか?こうしたことは書籍やネットなど、さまざまなところで既に多くの人によって語られていると思いますが、結局
- 部品化によって再利用性が向上する
- インターフェースと実装の分離などにより、変更の容易な保守性の高いプログラムが作成できる
- 複雑なロジックをオブジェクト内に隠蔽することにより、巨大なプログラムを作成できる
ということになるのだと思います。実際、Windowsや3Dを駆使したゲームなど、きわめて複雑なプログラムをオブジェクト指向抜きで実装することは現実的でないと言ってよいと思います。業務システムでもオブジェクト指向言語を使って適切な設計をすることで生産性や保守性の高いシステムを構築することができるのです。ただし、ここで大切なことはオブジェクト指向言語を使ったからといって自動的にオブジェクト指向のメリットが得られるのでは決してないことです。Java言語を使っても、デザインパターンやドメインモデリングなどを適切に利用して、正しい設計ができて初めて上記のメリットが得られるのであって、従来と同じ発想でデータ構造とロジックだけ考えていたのでは、高い生産性を享受することは絶対にできません。それどころか、固定長ファイルを単にバッチ処理するロジックを(クラスライブラリーやフレームワークを一切使わず)Java言語で手続き的に実装したらCOBOLよりもかえって生産性が下がるという危険性すらあるのです。
SIer製Javaフレームワークの問題点
つまり、Javaなどのオブジェクト指向言語では適切にオブジェクト指向設計できる高スキルのプログラマーなら高い生産性を享受できるのに、初心者だと多くの場合COBOL以下の生産性しか得られない。ここに問題の本質があります。
SIerの多くはCOBOL時代からの伝統的な慣習からか、
プログラマー = PG = 初級レベルのエンジニア = 下流 = 単価が低い
という構図で考える傾向があります。だから、一般的に日本のSIerの作ったJavaフレームワークというのは、「いかにプログラマーに頭を使わせないか」という点に最大限の注意を払っているように思います。実際、今まで私が見てきたSIerのフレームワークは以下の2通りのパターンのいずれかです。
- なんちゃってパターンの使用を強制する。XXDto、XXAction、XXLogicなどのように同じ接尾辞が付く多数のクラスを作らせるのは典型的なケースです。(侵略的なフレームワーク - 達人プログラマーを目指して)ここではどうしてパターンを使うのかということは*5どうでもよくて、とにかく規約として一連のクラスの作成を強制させます。そうすることでオブジェクト指向している気分になっているのかもしれませんが。
- 何でもかんでもとにかく自動生成させたがる。特にExcelなどの表から大量のクラスを自動生成させるなど。たいていそのようにして生成されたクラスはゴミで保守も大変なものになりがちです。
結局、たいていの場合SIer製のフレームワークというのは、プログラマーのオブジェクト指向設計する自由を奪っているのであって、できるプログラマーの能力を殺してしまうのですが、もっとも重要な事として先に述べたとおり、Java開発の生産性をCOBOLやVB以下に制約しているということすらいえる可能性があるのではないでしょうか?もともとの思惑としては、オブジェクト指向を使わずにCOBOLと同じ発想で設計していても、新しい言語なのだからJavaの方が多少なりとも生産性が高いだろうという期待があるのかもしれませんが、そんなことは決してないと思います。むしろ、本来の使い方とはかけ離れた不自然な設計を強制することで、多くの場合COBOL以下の生産性しか得られていないというのが実態な気がします。*6
最近のOSSフレームワークは何が違うか?
SIerのフレームワークと比較して、Seasar2、Spring Frameworkなど最近のOSSのフレームワークは、プログラマーの自由を奪わない*7(侵略的でない)ことが特徴とされています。つまり、基本的に「優秀なプログラマーの優秀なプログラマーによる優秀なプログラマーのための」フレームワークとなっているのです。だから、必ずしもオブジェクト指向やデザインパターンがわからないような初心者にやさしいかというと必ずしもそんなことはありません。しかし、天才プログラマーでなくてもある程度これらの基礎をきちんと勉強しているプログラマーが使うと、高い生産性を享受できるのが特徴です。きちんと勉強しさえすれば、平均的なプログラマーであっても天才プログラマーの考えた仕組みを取り込むことができるのです。
ところで、SIerのフレームワークであっても最近はこれらのOSSフレームワークを取り込んでいることが多くあります。しかしながら、私の知る限りにおいて現状では、従来のSIer製フレームワークと大きく変わるものではないと思います。基本的にプログラマーは初心者という前提で作られていることは以前とかわらず、OSSフレームワークやプログラミング言語が本来持っている柔軟性や良さを殺しているケースがほとんどであると思います。
生産性とプログラマーのスキル
以上の説明をもとに、生産性とプログラマーのスキルとの関係をグラフに表すとだいたい以下のような感じになるのではないでしょうか。(あくまでも定性的な傾向を私の個人的な主観で表現したものです。)
SIerは一般的に領域①で大量のプログラマーを低い単価で雇って仕事をさせようと考える傾向があります。思惑としてはJavaのフレームワークを導入することでCOBOLよりも高い生産性を確保したいということがありますが、多くの場合は、COBOL以下の生産性でプロジェクトが失敗したり遅延したりします。成功したといわれるプロジェクトでも性能面などの品質が悪かったり、機能拡張が極めて困難だったりと、オブジェクト指向の本来のメリットを享受できません。どうしてこんな非効率なことになっているのかということを、会社の上司などに言うとたいていは、「ハイスキルのスーパープログラマーを確保することは不可能だから」といいます。また、ビジネス的にも少人数のできるプログラマーをわざわざ集めてよいシステムを短期間に作るより、大量の頭数をそろえて非効率に開発した方が(少なくとも短期的な視野からは)儲かります。*8ただし、そのような領域で開発を続ける限りCOBOLやVB*9の時代の生産性を大きく超えることはありませんし、長期的には競争力を失って仕事が取れなくなるという危険性すらあるのです。
確かに天才プログラマーを集めて領域③で開発をするということはMicrosoftやGoogleのような特殊な会社を除き、一般的な開発プロジェクトでは現実的でないでしょう。ただし、オブジェクト指向などの基礎教育を行い、また、プログラマーの技術力もきちんと評価するようになれば、少なくとも領域②の範囲で仕事をすることは不可能ではないと私は思います。*10そうすればもっと効率的に開発でき、プログラマーも待遇面でもっと評価されるようになり、皆が幸せになれると思いますがいかがでしょうか。
*1:COBOLにはCOPY文などテンプレートから一部置換しながら自動的にコピペする仕組みすらある。
*2:Inversion of Control=制御の反転
*3:そうは言ってもPERFORMのくくり出しや段落分割などでいかにきれいに構造化するかといったような工夫の余地は多少なりともありますが。
*4:日本人プログラマーの記憶している英単語の大半が予約語だったりするということです。
*5:本来パターンを導入するにはそれなりの理由=フォースが重要。
*6:ここではIDEなどの開発ツールの差は考慮していません。しかし、重たいアプリケーションサーバーやIDEが本当にCOBOLの開発環境より生産性の向上に寄与しているという証拠もありません。
*7:ここでは多くのSI開発で利用されているJavaフレームワークを例として取り上げましたが、RubyやScalaなどの軽量言語やルール記述言語、関数型言語などを考えてみても、プログラミングの柔軟性を奪うという方向の言語はほとんどないと思います。以前よりもプログラマーのセンスやスキルが重要になっているということは一般的な傾向だと言えると思います。
*8:ただし、昨今ではユーザー企業も財布の紐が硬くなっており、生産性や費用対効果を重視することが多く、この従来モデルで売上や利益を上げることが徐々に難しくなってきている傾向があるようです。たまたま会社の合宿で技術系の役員の方と話をする機会があったのですが、多くのSIerで従来の生産性の低いモデルを何とか改善しないといけないという危機感を持っているところが多いと聞きました。ただし、SOAとかクラウドとか新しい言語やツールなどの手段の話は出てきてもプログラマーのスキルの重要性という面には会社のお偉いさん方の考えが及びにくいという傾向はあります。ただし、今後はそういう面に気づいて新しいビジネスモデルで仕事をするSIerが出てきてもおかしくないと思いますし、実際に改革に取り組んでいる会社も多いと信じます。
*9:ここではCOBOLとVBを取り上げましたが、PL/SQL、Transact-SQL、Perlなど一般的にはオブジェクト指向言語より劣るあるいは時代遅れと「信じられている」ような言語に当てはめて考えることもできます。
*10:実際、プログラマーの能力を過小評価する傾向はゼネコン構造も関連した日本のIT業界独自の傾向という話もあります。たとえば、アメリカではSIerに丸投げということは稀であり、ユーザー企業が直接必要なプログラマーを雇うモデルのようなので、生産性やスキルは非常に重視される傾向があります。たとえばSpringのエキスパート的な知識があれば相当高い給料がもらえる一方で、スキルのないプログラマーはすぐに首になるという厳しい面もあるようです。だから、プログラマーも相当がんばって勉強することになります。ある程度そのような生産性が重視される方式に業界の構造が変化していけば、自然と領域①から領域②へ中心がシフトすることにならざるを得ないと思います。