JavaOne 2014 サンフランシスコ報告会 Tokyoに参加しました

JavaOne 2014 サンフランシスコ報告会 Tokyoに参加してきたので、軽くまとめてみます。アーキテクチャトレンドと、EEのアップデート以外は関心が無いので、あまり触れません。

イベントの概要

イベントの雰囲気

  • 座席の占有率は30〜40%ほどで、盛り上がってる風でもなく、ボチボチという雰囲気でした。
  • 会場のOracle本社13Fは綺麗ですし、無料のWiFi環境の整備等、細かいところに気遣いを感じました。
  • 司会進行、説明等は丁寧でわかりやすかったです。場所が場所ですし、きっちりしてますね。

基調講演から振り返る JavaOne 2014

アーキテクチャトレンド

  • スライド -> http://www.slideshare.net/yusuke/j1jp2014-architecture
  • 個人的には、一番面白いセッションでした。
  • 「仕様の確定」から「価値の提供」へのシフト
    • SIerとしては、「う〜ん、そんなこと言われても・・・。」という感じです。
    • 製造業をモデルとして、階層化された流れ作業で仕事をするスタイルなので、適用は厳しいです。
    • ぶっちゃけて言うと、顧客価値よりも、自分の仕事の確保のほうが重要ですし(みんな口には出さないけど、行動原理は・・・。)
  • 今年最大の流行語はMicroservices Architecture
    • みんな試行錯誤中でカオス状態
    • リファレンスアーキテクチャを本番で適用した人が皆無らしいというのが・・・。
    • 個人的には、ミドル間の通信、永続化、リカバリ処理等のオーバーヘッド増大や、考慮漏れ、ミドルのバグ等が怖いです。あんな複雑で期待通りに動くのかなぁ・・・。
  • DDDも流行中
    • 「設計書に書いた日本語が正」のSIerでは受け入れられないだろうなぁ。
    • DDDの前に、OOADの有識者が殆どいないですし、「プログラミングは価値が低い、知らなくてもよい」という文化なので、適用が困難なのでは。
  • NoSQLを手放しで褒める人は皆無
    • NoSQLはイケてない、でもRDBMSもイケてない -> DBはもっと進化が必要!
    • Consistencyは欲しい!
  • Dockerも流行中
    • コンテナ型は何十年も前から存在するので、Dockerの本質ではない
    • OSを含めたイメージをリポジトリに登録し、再配布可能にするのが本質
    • エンタープライズの世界に普及するには数年かかりそう
  • その他
    • 「レイヤ」や「抽象化」等の言葉が出てきたのは、ソフトウェア技術の話っぽくて好き。
    • 流行をゴリ押しするわけではなく、俯瞰的に見る感じで、色々考えさせられました。

Java EE Report

  • スライド -> http://www.slideshare.net/agetsuma/java-ee8-report
  • トップバッターはServlet 4.0
    • やはりHTTP/2の影響大
    • SSEの仕様は決定していない模様。使いやすければ、Webアプリのつくりが根本的に変わりそう。
  • JSF, JAX-RSは地味に更新
  • MVC1.0
    • 「いまさらねぇよなぁ」「JSF/JAX-RSとのすみわけは?」等は激しく同意。
    • デザイン案は、JSFっぽい。
  • CDIの強化
    • Java SE環境でのDI、セキュリティ機能の統合は影響が大きそう。
    • TomcatやJettyが素でDI、トランザクション管理できると、Springの価値が大きく下がるのでは?
    • CDIの仕様分割には激しく同意。異様に複雑で全体像を掴みづらく、仕様バグだらけだし、人類には早すぎる。
  • JPA関連の話題が皆無
    • Survey関連とAvatarで名前がちらっと出てきただけ
    • 多分、最重要なJava SE/EEのAPIのうちの一つだと思うのですが・・・。
    • NoSQL対応、効率性向上、Spring Dataへの対抗等、ネタはいくらでもあるはず。
  • その他
    • Oracleユースケースを知りたがっている」とのことですが、多数決方式だとライトな使い方に偏りそう。
    • 機能追加の話がメインですが、JSRの整備や、公式チュートリアルとJSRの間を埋めるものが欲しい。

まとめ

  • 革新は無いけど、改善は続いているようです。
  • コミュニティを大事にしてくれるのはよいですね。
  • 海の向こうのスケールの大きいイベントなので、日本の中小企業に勤める普通のサラリーマンとしては、あんまり実感が沸きませんでした。
  • 今私が所属している会社のSIでJavaを使い続けるのは厳しいというのが、率直な感想です。
    • 更新のペースがSIの感覚と比べて速い(ランタイムやミドルがすぐEOLなのは困るというか、現在進行形で困っている)
    • フレームワーク、言語の仕様が複雑で下請け先の量産型プログラマでは使いこなせない(プロジェクト遅延に直結するくらい、道具の難しさがボトルネック。自動化は制約や拡張の開発で問題を余計に複雑にする。)
    • 機能、互換性の足切りや、やりたいことがあるけど、道具を理解できない etc...でドツボに嵌りそう(というか、嵌ってます)
    • 流行を中心に機能拡張されるので、SIの要求と方向性が異なる(採用を検討するけど、ニーズに合わないので却下されたり。
    • 独自の「SIのニーズに沿った言語、Fw」を自作するか、思い切ってJavaでのシステム開発はきっぱり諦めて、パッケージやSaaSの適用、コンサルに特化すべきなのでは。

オブジェクト指向の簡潔なまとめ

最近になって、オブジェクト指向について改めて見直す機会があったので、簡潔にまとめてみました。

そもそもオブジェクト指向とは?

オブジェクト指向は、一般的にプログラミングテクニック扱いされています。しかし、本来は変更の多い「機能」ではなく
安定した「問題領域」をモデル化することで、理解しやすく、変更、修正に強いソフトウェアを開発するための開発手法です。「問題領域」をモデル化してコンピュータの世界を隠ぺいし、人間に歩み寄るのが中核となるコンセプトなので、元来トップダウンで、下流の「How」よりも、上流の「Why」「What」を重視します。もちろん、設計や実装ではコンピュータの世界を強く意識する必要があります。

オブジェクトとモデル化

オブジェクトとは、ソフトウェアの世界で特定の問題を解決するために、現実世界の物理的なものや概念等をモデル化したものです。
また、モデルとは「現実世界の物事を特定の目的に合わせて抽象化したもの」になります。モデルは特定の目的を満たせば良く、目的と関係ない特性を省略できます。モデルの例は以下のとおりです。

  • プラモデル -> プラスチックで造形のみをモデル化
  • 地図 -> 現実世界の位置に関する情報のみをモデル化
  • 組織図 -> 集団の階層関係や関連をモデル化

上記の例と同様に、オブジェクトは「現実世界を元に、特定の目的のために、ソフトウェアの世界で表現されたモデル」になります。わかりづらいので、例としてJavaでデータベースを操作するためのオブジェクト「Connection」を挙げます。

Javaプログラムとデータベースの通信は複雑で、通信方式や操作、解釈が様々です。そのため、エンジニアがその都度、データベースの仕様を調べてプログラム化するのは負担が大きいです。

しかし、Connectionオブジェクトは、「データベースの共通的な操作のみ」をモデル化して、通信方式などを隠ぺいし、エンジニアの負担を軽減します。エンジニアは、Connectionのみを意識して、必要な操作を呼び出せばよいのです。

上記のように、オブジェクトは優れた特性を備えています。オブジェクトの主なメリットは以下の通りです。

  • 問題領域にちなんだ名前を持ち、人間が識別できる = ソフトウェアの見える化、抽象化
  • 再利用できる = 開発コストの削減、ソフトウェアの資産化
  • 既存のオブジェクトを元に拡張できる = 開発コストの削減
  • 容易に差し替えられる = 変更への対応性

オブジェクト指向の暗黒面

オブジェクト指向は強力ですが、致命的な弱点があります。それは、「オブジェクトの抽出、モデル化が極めて難しい」ということです。
オブジェクトの抽出、モデル化は「絵画」のようなもので正解がなく、効果的に使うには、かなりの熟練が必要です。

私の短い経験では、中途半端な理解で抽出されたオブジェクトは、まともに動かなかったり、変更も再利用もできず、維持メンテが困難なことが多いです。それならば、シンプルな分、丁寧にメンテされた構造化プログラムのほうがマシです。

別のパラダイムと組み合わせる

多くのエンジニアがオブジェクト指向を使いこなせない現実を踏まえてなのか、最近は関数型やDSL等の別のパラダイムが注目されています。オブジェクト指向は今後も利用されるでしょうが、ソフトウェアの生産性、品質向上のために、これらのパラダイムにも注目すべきでしょう。関数型やDSLについて、多くを語れるほど詳しくないため、この記事では踏み込まないこととします。

まとめ

最後にこの記事で言いたいことをまとめます。

参考文献

The Art and Science of Smalltalk
Object Oriented Modeling and Design
Domain-Driven Design: Tackling Complexity in the Heart of Software
Design Patterns: Elements of Reusable Object-Oriented Software

Java EE7 CDI1.1のまとめ : その2 各種機能の解説

第一回CDIの基本的な概念について解説したので、
第二回は具体的なCDIの各種機能について解説していきます。


依存関係の解決

CDIの最も基本的な機能になります。
CDIでは他のイネーブルド・ビーンへの参照を@Injectアノテーションで渡します。

/**
 * DIに使用されるイネーブルド・ビーン。
 */
@Dependent
public class DIBean {

    private static AtomicInteger counter = new AtomicInteger();

	// インスタンスの生成毎に番号を払い出します。
    private int count = counter.incrementAndGet();

    public String getName() {
        return "bean" + count;
    }
}
/**
 * インジェクト先のイネーブルド・ビーン。
 */
@Stateless
public class DIInjectee {

    /**
     * コンテナが生成したコンテキスチュアル・リファレンスをフィールド・インジェクションします。
     * Springの@Autowired(required = "true")相当です。
     * CDIの仕様では、曖昧な依存関係や、解決できない依存関係が存在する場合は、
     * デプロイメント・エラーとして処理されます。
     */
    @Inject
    private DIBean bean1;

    private DIBean bean2;

    private DIBean bean3;

    /**
     * イニシャライザ・メソッド。
     * Springのメソッドインジェクション相当です。
     *
     * @param bean2
     */
    @Inject
    public void setBean2(DIBean bean2) {
        this.bean2 = bean2;
    }

    /**
     * コンストラクタ・インジェクション。
     * Springと異なり、コンストラクタには必ず{@link javax.inject.Inject}を
     * 指定する必要があります。
     *
     * @param diBean3
     */
    @Inject
    public DIInjectee(DIBean diBean3) {
        this.bean3 =diBean3;
    }

    /**
     * このクラスの全てのスーパークラス及び、
     * このクラスのフィールド・インジェクション、
     * イニシャライザメソッドの後に実行されます。
     */
    @PostConstruct
    public void initialize() {
        System.out.println("initilized!");
    }

    public DIBean getBean1() {
        return bean1;
    }

    public DIBean getBean2() {
        return bean2;
    }

    public DIBean getBean3() {
        return bean3;
    }
}

Assignableなイネーブルド・ビーンをインジェクトする

CDIでは、タイプセーフ・レゾリューションの仕組みにより、直接インジェクト先の型とマッチしなかった場合でも、
代入可能(=サブクラス、実装)な場合は、その型のイネーブルド・ビーンがインジェクトされます。

/**
 * POJOのインターフェース。
 */
public interface BeanInterface {

    String getName();
}
/**
 * {@link my.apps.cdi.sample.di.BeanInterface}の実装。
 * CDIのタイプセーフ・レゾリューションにより、BeanInterfaceに
 * 代入可能なBeanImplがコンテキスチャル・リファレンスとして解決されます。
 */
@Dependent
public class BeanImpl implements BeanInterface {

    private static AtomicInteger counter = new AtomicInteger();

    private int count = counter.incrementAndGet();

    @Override
    public String getName() {
        return "bean" + count;
    }
}
@Stateless
public class AssignableInjectee {

    /**
     * CDIのタイプセーフ・レゾリューションにより、BeanInterfaceに
     * 代入可能なBeanImplがコンテキスチャル・リファレンスとして解決されます。
     */
    @Inject
    private BeanInterface bean;

    public BeanInterface getBean() {
        return bean;
    }
}

クオリファイヤ(@Qualifier)によるインジェクトするインスタンスの選択

インジェクト対象のイネーブルド・ビーンが一意に定まらない場合に、@Qualifierアノテーション
使用することで、ビーンを一意に識別します。

/**
 * 独自のQualifierを定義します。
 */
@Qualifier
@Retention(RetentionPolicy.RUNTIME) // RUNTIMEで必要! デフォルトはCLASSです。
public @interface Impl1Qualifier {
}
/**
 * 独自のQualifierを定義します。
 */
@Qualifier
@Retention(RetentionPolicy.RUNTIME)  // RUNTIMEで必要! デフォルトはCLASSです。
public @interface Impl2Qualifier {
}
/**
 * インジェクト対象のビーンのインターフェース。
 */
public interface TargetBe
/**
 * アノテーションにより、特定の実装に依存せずに
 * オブジェクトの識別子を定義します。
 */
@Impl1Qualifier
@Singleton
public class TargetBeanImpl1 implements TargetBean {
}
/**
 * アノテーションにより、特定の実装に依存せずに
 * オブジェクトの識別子を定義します。
 */
@Impl2Qualifier
@Singleton
public class TargetBeanImpl2 implements TargetBean {
}
/**
 * {@link javax.inject.Named}を用いることで、
 * 文字列ベースで独自のクオリファイヤを定義します。
 */
@Named("impl3")
@Singleton
public class TargetBeanImpl3 implements TargetBean {
}
/**
 * インジェクト先のセッション・ビーン。
 * {@link javax.inject.Qualifier}を使用することで、
 * 特定の実装への依存を排除しつつ、
 * インジェクト対象のビーンを識別します。
 */
@Stateless
public class Injectee {

    /**
     * TargetBeanに代入可能なManaged Beanは2つ存在するが、
     * Qualifierにより実装を一意に識別する。
     */
    @Impl1Qualifier
    @Inject
    private TargetBean targetBean1;

    @Impl2Qualifier
    @Inject
    private TargetBean targetBean2;

    /**
     * 文字列でインジェクト対象のビーンを選択します。
     */
    @Named("impl3")
    @Inject
    private TargetBean targetBean3;

    public TargetBean getTargetBean1() {
        return targetBean1;
    }

    public TargetBean getTargetBean2() {
        return targetBean2;
    }

    public TargetBean getTargetBean3() {
        return targetBean3;
    }
}


インジェクト対象のイネービルド・ビーンの生成

CDIでは、Springのファクトリ・ビーン機能に相当するプロデューサという機能が存在します。
プロデューサを使用することで、ビーンの生成をDIコンテナに任せず、ビーンの生成方法を独自にカスタマイズできます。
また、プロデューサを介して生成されたビーンは、CDIアノテーション無しでイネーブルド・ビーンとなるため、
CDIを前提としないフレームワークのクラスをイネーブルド・ビーンとしてCDI環境に組み込むのにも活用できます。

注意点としては、プロデューサで生成されるビーンは、通常のマネージド・ビーンと同様に、
デフォルトのコンストラクタ、@Injectで修飾されたパラメータ付きコンストラクタの
いずれかを定義する必要があります。
生成対象ビーンが外部のフレームワークのもので、上記の要件を満たしていない場合は、
CDIのためだけにラップする必要があるかもしれません。

/**
 * ビーンのプロデューサ。
 *
 * <p>
 * プロデューサのライフサイクル系アノテーションが
 * {@link javax.enterprise.context.Dependent}の場合、
 * ビーンの生成毎にインスタンスが破棄され非効率なので、
 * 特に理由が無い限りは、
 * {@link javax.enterprise.context.ApplicationScoped},
 * {@link javax.inject.Singleton},
 * {@link javax.ejb.Singleton}
 * のうちの何れかで定義すべきです。
 * </p>
 */
@ApplicationScoped
public class BeanProducer {

    private static AtomicInteger counter = new AtomicInteger();

    /**
     * {@link javax.enterprise.inject.Produces}で
     * プロデューサ・メソッドを定義します。
     * デフォルトのスコープはDependentになります。
     * また、Qualifierを指定可能です。
     * @return
     */
    @Named("count")
    @Produces
    private int incrementAndGet() {
        return counter.incrementAndGet();
    }

    /**
     * {@link javax.enterprise.inject.Produces}で
     * プロデューサ・メソッドを定義します。
     * メソッドにスコープ・アノテーションを定義することで、
     * 生成されるオブジェクトのライフサイクルを決定します。
     * また、パラメータはインジェクション・ポイントになります。
     * @param count
     * @return
     */
    @ApplicationScoped
    @Produces
    public ProducedBean produceBean(@Named("count") int count) {
        return new ProducedBean("ProducedBean: instance count=" + count);
    }
}
/**
 * 生成対象のビーン。
 *
 * <p>
 * どのスコープに属するかは、プロデューサが決定するため、
 * 生成対象のBeanにスコープを指定する必要はない。
 * 多様な使われ方があり得るライブラリの場合は、
 * CDIへの依存をなるべく排除したほうが良いかもしれません。
 * </p>
 */
public class ProducedBean {

    private final String name;

    public ProducedBean(String name) {
        this.name = name;
    }

    /**
     * プロデューサから生成するには、コンストラクタに@Injectを
     * 指定してインジェクション・ポイントとするか、
     * 引数無しのコンストラクタを指定する必要があります。
     * @Injectを指定する場合は、インジェクトする値を
     * 解決出来る必要があり、極めてややこしいので、
     * なるべくさけるべきかと思います。
     */
    public ProducedBean() {
        this.name = null;
    }

    public String getName() {
        return name;
    }
}
@Stateless
public class ProducedBeanInjectee {

    /**
     * プロデューサが生成したビーンをインジェクトします。
     */
    @Inject
    private ProducedBean producedBean;

    public ProducedBean getProducedBean() {
        return producedBean;
    }
}

また、CDIの特徴として、フィールドをビーンとして提供するプロデューサ・フィールドが存在します。
(プロデューサ・フィールドという名前ですが、戻り値を返すわけではないので、「提供」という方が適切?)

使い道に悩みそうですが、スレッドセーフでないJPAのEntityManagerを提供する等の用途が想定されます。
コンテナ管理のEntityManagerを取得するのには@PersistentContextを使用しますが、
@ApplicationScopedのサービスクラスに直接インジェクトすると、スレッドセーフ性が壊れる可能性があります。
かと言って、サービスクラスを@RequestScopedなどに変更すると、毎回インスタンスが生成されて無駄なので、
間にワンクッション挟むことになります。
(JSRのサンプルコードから察するに、JPAは元々ステートレス/ステートフル・セッションビーンからの
呼び出しのみを考慮していたのが、CDIの進化で前提が崩れて混乱を招いているようです・・・。)

この辺の話は非常にややこしいので、以下のstackoverflowのやりとりが参考になります。
http://stackoverflow.com/questions/11063567/java-cdi-persistencecontext-and-thread-safety

この記事はCDIに関するものなので、JPAには深入りしませんが、Stackoverflowを
参考にサンプルコードの断片を作成しました。厳密に検証しきれていないため、
APサーバのマニュアルやサンプルを参考にすべきです。

/**
 * リクエスト・スコープに属するビーンを作成します。
 */
@RequestScoped
public class EntityManagerProducer {

    @PersistenceContext
    @Produces
    private EntityManager em;
}
@ApplicationScoped
public class MyService {

    /**
	 * アプリケーション・スコープのビーンからリクエスト・スコープのビーンを触ることで、
	 * EntityManagerを安全に使用できます。
	 */
    @Inject
	private EntityManager em;
}


インターセプター

AOPの代替となる、メソッド呼び出しの前後に処理を挟み込む機能です。
インターセプターとインターセプト対象の双方に@InterceBindingを拡張したアノテーション
指定することで、バインディングを実現します。

注意しなければならないのは、後述のデコレータと異なり、「JSR-318 Interceptors 1.2」として、
CDIとは別仕様であるということです。この辺りが混乱を招いているせいか、Oracleの公式チュートリアルですら、
何故かbeans.xmlにベタ書きする方法しか記載されていません。
(実際はアノテーションのみで有効化可能です)

/**
 * インターセプターを識別するためのバインディングを定義します。
 */
@InterceptorBinding
@Retention(RetentionPolicy.RUNTIME) // 実行時に必要なため、RUNTIMEとします。
public @interface MyInterceptorBinding {
}
/**
 * インターセプタークラス。
 *
 * <p>
 * バインディングのアノテーションにより、インターセプト対象を一意に識別します。
 * インターセプターはCDIとは別仕様なので、詳細な有効化方法は
 * JSR-318 Interceptors 1.2に記載されています。
 * </p>
 *
 * <p>
 *  Oracleの公式チュートリアルでは、何故かbeans.xmlで定義していますが、
 *  実際はXMLレスで使用可能です。その場合は、インターセプターを
 *  コンテナに認識させるため、スコープ系アノテーションを定義する必要があります。
 *  インターセプターの場合、定義できるスコープは、Dependent固定です。
 *  CDI1.2では、この煩わしさが軽減されており、デフォルトでインターセプターが
 *  スキャン対象となります。
 * </p>
 */
@MyInterceptorBinding // インターセプターを一意に定義します。
@Priority(0) // インターセプターを有効化します。CDIのJSRに書いてない仕様なので要注意!
@Interceptor
@Dependent // インターセプターは標準ではスキャンされないため、明示的にスコープを指定します。
public class MyInterceptor {

    @AroundInvoke
    public Object invoke(InvocationContext context) throws Exception {
        return "Intercepted " + context.proceed();
    }
}
/**
 * インターセプターのバインド対象のセッション・ビーン。
 */
@MyInterceptorBinding
@Stateless
public class Interceptee {

    public String invoke() {
        return "Invoked!";
    }
}


デコレータ

処理を挟み込む対象を意識しないインターセプターと異なり、対象を意識してメソッド単位で処理を指定できます。
インターフェースの型でデコレート対象を識別できるため、前述のインターセプターと異なり、
特別なバインディング用のアノテーションは必要ありません。
また、デコレータを作成する場合は必ず@Delegateで修飾されたデコレート対象(へのプロキシ)を
インジェクトする必要があります。

/**
 * デコレート対象のインターフェース。
 * デコレータは、実装するインターフェースでデコレート対象を
 * 一意に識別できるため、特別なアノテーションなどは必要ありません。
 */
public interface Decoratee {

    String invoke();
}
/**
 * デコレート対象のクラス。インターフェースの実装である必要があります。
 */
@Stateless
public class DecorateeImpl implements Decoratee {

    @Override
    public String invoke() {
        return "Invoked!";
    }
}
/**
 * デコレータクラス。
 * 対象のクラスの各メソッド呼び出しをデコレートします。
 *
 * <p>
 *  Oracleの公式チュートリアルでは、何故かbeans.xmlで定義していますが、
 *  実際はXMLレスで使用可能です。その場合は、デコレータを
 *  コンテナに認識させるため、スコープ系アノテーションを定義する必要があります。
 *  デコレータの場合、定義できるスコープは、Dependent固定です。
 *  CDI1.2では、この煩わしさが軽減されており、デフォルトでデコレータが
 *  スキャン対象となります。
 * </p>
 *
 * <p>
 *  なお、大量のメソッドが定義されたクラスをデコレートするのに、冗長な実装をする手間を
 *  軽減するためか、デコレータは抽象クラスでも良いことになっています。
 *  (黒魔術をフル活用することを前提とした、豪快な仕様ですね・・・。)
 * </p>
  */
@Priority(0) // デコレータを有効化します。この仕様はCDIのJSRに記述されています。
@Decorator
@Dependent // デコレータはスキャンされないので、明示的にスコープを指定する必要があります。
public class MyDecorator implements Decoratee {

    /**
     * デコレート対象(へのクライアント・プロキシ)。
     * @Delegate付きのプロキシが定義されない場合、
     * スキャン時にエラーとなります。
     *
     * (余談ですが、IntelliJの有料ではこのあたりもサジェストしてくれます。)
     */
    @Delegate
    @Inject
    private Decoratee delegate;

    @Override
    public String invoke() {
        return "Decorated " + delegate.invoke();
    }
}


イベント

DIコンテナをベースとしたイベント通知機能です。アノテーションの記述のみで済む他の機能と異なり、
CDIが提供するインターフェースに依存するため、この機能を使う場合は完全にCDIが前提となります。
イベントの通知先は、クオリファイヤで識別します。
また、非常に高機能で、イベントの受信タイミングをトランザクション処理のタイミングと関連付けられます。
(送信はトランザクション処理中でも、実際の受信は、そのトランザクションの正常終了後まで遅延させる等)

/**
 * イベントの通知に使うオブジェクト。
 */
public class EventArgument {

    private final Date eventDate;

    private final String message;

    public EventArgument(Date eventDate, String message) {
        this.eventDate = eventDate;
        this.message = message;
    }

    public Date getEventDate() {
        return eventDate;
    }

    public String getMessage() {
        return message;
    }

    @Override
    public String toString() {
        return "EventArgument{" +
                "eventDate=" + eventDate +
                ", message='" + message + '\'' +
                '}';
    }
}
/**
 * イベントを識別するためのQualifierを定義します。
 * インターセプターと異なり、特別なアノテーションは必要ありません。
 */
@Qualifier
@Target({ElementType.PARAMETER, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface EventQualifier {
}
@Qualifier
@Target({ElementType.PARAMETER, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface TransactionalEventQualifier{
}
@Singleton
public class EventNotifier {

    /**
     * イベントの通知に使うインターフェースをインジェクトします。
     * 通知先などはQualifierで一意に識別します。
     */
    @EventQualifier
    @Inject
    private Event<EventArgument> event;

    @TransactionalEventQualifier
    @Inject
    private Event<EventArgument> transactionalEvent;

    public void fire(String msg) {
        event.fire(new EventArgument(new Date(), msg));
    }

    /**
     * トランザクショナルにイベントを通知します。
     * 通常のイベントと異なり、オブザーバが即時にイベントを受信せずに、
     * トランザクションの動作とリンクします。
     */
    @Transactional
    public void fireTransactionally(String msg) {
        transactionalEvent.fire(new EventArgument(new Date(), msg));

        // このメッセージの後にトランザクションの処理が完了してからイベントが通知されます。
        System.out.println("end transactional method");
    }
}
@Singleton
public class EventReceiver {

    private final List<EventArgument> receivedArgs = new ArrayList<EventArgument>();

    private final List<EventArgument> transactionalReceivedArgs = new ArrayList<EventArgument>();

    /**
     * イベントのオブザーバ・メソッド。
     * EventQualifierで修飾されたNotifierからのイベントを受信します。
     * @param arg
     */
    public void receiveEvent(@Observes @EventQualifier EventArgument arg) {
        receivedArgs.add(arg);
    }

    /**
     * トランザクション完了時にイベントを受信するレシーバ。
     * 受信のタイミングがトランザクション完了時なので、イベントの通知と、
     * オブザーバ・メソッドの呼び出しタイミングが変わります。
     * 成功時のみ、失敗時のみなど、細かく指定可能です。
     * @param arg
     */
    public void receiveTransactionalEvent(
            @Observes(during = TransactionPhase.AFTER_COMPLETION)
            @TransactionalEventQualifier EventArgument arg) {
        System.out.println("Transactional Event Received!: msg=" + arg.getMessage());
        transactionalReceivedArgs.add(arg);
    }

    public List<EventArgument> getReceivedArgs() {
        return receivedArgs;
    }

    public List<EventArgument> getTransactionalReceivedArgs() {
        return transactionalReceivedArgs;
    }
}

ここまでのテストケースは以下のGithubリポジトリからダウンロードできます。
https://github.com/zyake/cdi_sample


最後に

以上のように、CDIの各種機能を見ていきましたが、これだけ理解していれば、大抵の用途で使えるのではないかと思います。
(実際はもっと色々あって、とてつもなくややこしいです・・・)

CDIは後発で、恐らく標準仕様の意地があるためか、
「Springなどに負けない、野心的なものを作ろう」
という意気込みは感じられるのですが、その結果として、仕様が膨大で複雑過ぎ、理解するのが困難なものになっています。
(JSRの図表の少なさは一体・・・。読むのが大変過ぎるorz 敷居を上げるためにわざとやっているのでは?と勘ぐりたくなります。)

また、仕様策定者の間ですら混乱しているのか、CDI1.2では多数のドキュメントバグ修正、
記述の明確化が実施されており、複雑なユースケースでは、実装依存がありそうな雰囲気です。
(グレーゾーンがとても多い)

しかし、CDIが強力なのは間違いなく、CDI2.0ではセキュリティ機構や、タイマー実行等の機能が予定されていますし、
単なるDIコンテナから、「Java EEサーバで利用できるビーン管理のインフラ」に進化してJava EE開発で
抑えておくのが必須の技術になりそうです。

次回は、もっと特定の実装に踏み込んで(RIのWeldを想定)、ソースコードを解析してみます。

Java EE7 CDI1.1のまとめ : その1 重要な概念

Javaのベンダー公式フレームワークであるJava EEでは、DI(Dependency Injection)の仕様である
CDI(Context and Dependency Injection for Java EE)が提供されています。

CDIJava EEでアプリケーションを開発する上で避けては通れませんが、それ自体が複雑なのに加え、
仕様が複数にまたがっていて理解するのが難しいです。
そのため、CDI使う上で理解する必要のある重要な概念や基本的な使い方、
はまりポイントなどをまとめていきます。

この記事は基本的な使い方、概念についての解説なので、CDIや周辺仕様を全て網羅していません。その点はご了承ください。
なお、本記事で使用するのは、最新のCDI1.2ではなく、Java EE7の一部であるCDI1.1になります。
また、Spring, Seasar2などの経験があることを前提とし、DIの概念は詳細に解説しません。
DIのコンセプトについては、以下の@マークITの記事が参考になります。
なぜDIコンテナを使うのか - @マークIT

CDIとは?

CDIの特徴、ポイントを列挙します。
  • Java EE環境(必ずしもサーバ上で実行する必要はない)で使用可能なDIの標準仕様です。
  • DIにより、プログラムの実行時にオブジェクトの依存関係を組み立てることで、オブジェクト間の依存関係を排除します。
  • Servlet, JAX-RS, JAX-WS, JSF, EJB等、Java EE上で実行するコンポーネント上で利用可能です。
  • DIコンテナのデファクトスタンダードであるSpringに似ています(一部アノテーションは互換性があります)
  • オブジェクト間のイベント通知、AOP相当の機能等、単なるDIコンテナよりも、カバー範囲が広いです。

事前知識

CDIを学ぶ上で、予め頭に入れておいたほうが良い知識をまとめました。

複数の仕様を横断する

CDIは複数の仕様が複雑に絡み合っており、他のJava EEの仕様を横断します。この仕様だけでは完結しないところもあります。
以下は関連する仕様の一部です。
  • リクエスト、セッション、カンバセーションの各スコープ => Servlet, JAX-RS, JAX-WS, EJBの各仕様の処理と密接に関連します。
  • セッション・ビーン => EJBの仕様です。
  • インターセプター => 似ている機能であるデコレータと異なり、JSR-318として別仕様です。
etc...

Springとくらべて、ビーン管理の思想がやや異なっている

Springと似てはいますが、ビーンの管理の考え方がやや異なっています。
詳細は後述しますが、Springと同じだと考えるとハマる可能性があります。

重要な概念

マネージド・ビーン

CDIで扱うことのできるビーンです。条件は以下のとおりです。 (要するに、EJBを除いたほとんどの普通のJavaクラス)
  • non-static inner classでない
  • 具象クラス(or @Decorator付きの抽象クラス)
  • javax.enterprise.inject.spi.Extensionを実装しない
  • EJBではない(@Statelessなどがない)
  • @Vetoedがついていない(これで対象から除去可能)
  • 実行時に解決できる適切なコンストラクタを持つ ※CDIではコンストラクタ・インジェクションをサポートするので、必ずしも、引数無しのコンストラクタをサポートする必要はない

なお、わかりづらいのですが,「マネージド・ビーンであること」と、「コンポーネント・スキャンされてDIで使用可能なこと」は
別の概念になります。CDI1.0では全てのビーンがDIの対象なので、ほぼイコールでしたが、CDI1.1では仕様が変更され、
後述するスコープ・アノテーションか、EJBのセッション・ビーンアノテーションを付与する必要があります。
アノテーションが付与され、DIで使用可能なビーンのことをイネーブルド・ビーンと呼びます。
(設定により、CDI1.0と同じ挙動に変更できます。)

また、CDIではコンストラクタをインジェクション対象とする場合は、コンストラクタを@Injectでの修飾が
必要なのにも注意が必要です。そのため、CDIで管理対象とするビーンがデフォルト・コンストラクタを
サポートするか、パラメータ付きコンストラクタが@Injectで修飾されているかは、
よく確かめるべきです。
特に外部ライブラリのビーンがどちらもサポートしていない場合は、CDIのためだけに
ラップする必要があるかもしれません。

スコープとコンテキスト

CDIを使う上で最も重要の概念になります。考え方がSpringと若干異なるため、注意が必要です。

スコープ

オブジェクトのライフサイクルを決定する識別子になります。CDIの仕様のみでは、以下のスコープが予め定義されています。

アプリケーション (@ApplicationScoped)

アプリケーションのライフサイクルと一致します。Springのシングルトン相当です。

セッション (@SessionScoped)

Servletのセッションのライフサイクルと一致します。Springのセッション相当です。

カンバセーション(@ConversationScoped)

JSFのためのスコープです。複数のHTTP通信をまたがるJSFリクエストを一つのユニット・オブ・ワークとして処理するときに、
そのユニット・オブ・ワークとライフサイクルが一致するオブジェクトを定義します。
セッションに似ていますが、ブラウザで現在表示中の画面(タブ)をまたがず、
その画面内の特定の処理の範囲で完結します。Springには無いスコープです。
(Spring4.0のデフォルトの場合。Web FlowやJSF用拡張は未調査。)

リクエスト (@RequestScoped)

ServletEJBのリモート呼び出し、タイマーコールバック、MDBコールバック、Webサービス呼び出しの1リクエストの
処理の間と、ライフサイクルが一致します。
Springの同名スコープはHTTPのリクエスト処理専用なので、よりカバー範囲が広いです。

ディペンデント (@Dependent)

使われ方によってライフサイクルが変わり、擬似スコープと呼ばれます。
(一方でアプリケーション〜リクエストまでの@NormalScopeで修飾されたスコープはノーマル・スコープと呼ばれます。)

他のビーンにインジェクトされる場合や、デコレータ、インターセプターで特定のビーンと関連付けられる場合は、
インジェクト先ビーンのライフサイクルと一致します。また、プロデューサや、イベント、JSP/JSFのEL上から参照された場合は、
呼び出し毎の使い捨てとなります。
その都度、新しいインスタンスを生成するのはSpringのプロトタイプと似ていますが、ライフサイクル管理がなされる点が異なります。
(Springの場合、払いだしたらそれっきりで、@PreDestoryのコールバックは無い)

また、CDIと関連する仕様である「JSR 330: Dependency Injection for Java」では以下のスコープを定義しています。

シングルトン (@Singleton)

所謂シングルトンとしてオブジェクトを定義します。CDIのビルトイン・スコープではないので、詳細な動作が定義されていません。
CDIのリファレンス・実装であるWeldのマニュアルに、注意点等が記述されているので、参考になります。

5.4. The singleton pseudo-scope - Weld – CDI Reference Implementation

このシングルトン・スコープも擬似スコープなので、クライアント・プロキシが適用されず、同期化、シリアライズの時に
問題が発生する可能性があるようです。

以上、CDIのスコープを見ていきましたが、スコープのまとめとして、挙動を確かめるための簡単なサンプルテストケースを作成しました。
テストケースは組み込みEJBコンテナを使用しており、Java SE6以上の環境ならば、APサーバ無しで動かせるはずです。
(テストケースの実行にとても時間がかかります)


ScopeTest.Java

コンテキスト・オブジェクト

それぞれのスコープに紐づくオブジェクトです。コンテキストにより、インスタンスの生成、破棄のタイミング、可視性等が決定されます。
どのスレッドから、どのコンテキスト・オブジェクトが見えるのかが重要です。以下の図は、コンテキストとスレッドの関係を可視化したものです。

クライアント・プロキシ

シングルトン、ディペンデントを除くノーマル・スコープの場合、オブジェクトをインジェクトしても、オブジェクトのインスタンス
直接参照されません。代わりに、特定のコンテキストに属するオブジェクトへの参照を持つプロキシがインジェクトされます。
この仕組により、アプリケーション・スコープとリクエスト・スコープ、セッション・スコープとリクエスト・スコープ等、
ライフサイクルが異なるオブジェクト同士を連携させて、適切なコンテキストのインスタンスを操作可能となります。
以下の図は、クライアント・プロキシの動作のイメージを可視化したものです。

クライアント・プロキシはSpringの世界で言うところの「動的プロキシベースのインターフェースによるプロキシ」ではなく、
「クラスの拡張ベースのプロキシ」であることに注意が必要です。

動的プロキシベースの場合は、必ずインターフェースを作成する必要があるため、面倒ですが挙動が素直です。
一方でクラスの拡張ベースの場合は、実行時に対象マネージド・ビーンを動的に継承し、プロキシ・ビーンを定義するため、
インターフェースの定義が不要ですが、拡張対象のビーンのコンストラクタが実行されます。

CDIの実装は未見ですが、恐らくフィールドのメモリ領域が確保されますし、
クライアント・プロキシと本物のインスタンスで2倍の処理時間が必要になります。
これがアプリケーション・スコープのビーンならば、オブジェクトの総数が少ないはずですし、
初期化処理の実行はAPサーバの起動時のみで済みます。
一方でリクエスト・スコープやセッション・スコープの場合、処理毎の初期化処理時間、メモリ使用量が
2倍になる可能性があるため、要注意です。
(実装によってはクライアント・プロキシがキャッシュされるかもしれないし、デフォルト・コンストラクタや、
フィールドの定義を上手く削除する可能性もありますが・・・。そこまでするとは思えない。)

また、プロキシは余計なフィールドやメソッドが追加される可能性が高いので、
リフレクションや永続化等の処理でも注意が必要です。

クオリファイヤ(@Qualifier)

オブジェクト(のグループ)を識別するための識別子です。イジェクト対象の識別や、後述するイベント、
インターセプター・バンディング等、CDI全体を通して、識別子として使用されます。
クオリティファイヤは@Qualifierアノテーションで定義し、以下のように独自に拡張可能です。


@Qualifier // 識別子の一種であることを指定
@Retention(RUNTIME) // 実行時に欲しい情報なのでRetention PolicyはもちろんRuntimeで!
@Target({METHOD, FIELD, PARAMETER, TYPE}) // 識別子はあらゆるところに書きたいはず
public @interface Asynchronous {}

クオリファイヤの動作を確認するためのサンプルテストケースを作成しました。使い方のイメージは掴めるかと思います。

QualifierTest.java

最後に

以上、CDIを扱う上で知る必要のある重要な概念を見ていきました。
特にクライアント・プロキシはSpringのデフォルトの動的プロキシの挙動と異なりますし、
スコープはよくわからないで使うと、ハマる可能性があるので良く理解しておくべきかと思います。
次回はCDIで提供される機能について見ていきます。

参考URL

SIerの問題点

世間一般では、SIerは非効率、プログラマのスキルが酷い、ガラパゴス化している等、評判が悪く袋叩きにあっているようです。実際にはグローバル水準で戦える高スキル者はいるし、特に某親会社の人達はみな頭の回転が速いし、志が高いです。

一方で私の5年そこそこの短い経験の中でも、世間の評判通り、開発現場の信じられないような体たらくっぷりをたくさん見てきました。すごく立派な看板の裏でのしょぼい設計実装、ミス等 etc...
(私も人のことを言えないですが・・・)

そこで、私の見える範囲の世界で、どうしてこのような現状なのか考えてみました。
(私の見える範囲なので、他社だと全く状況は違うだろうし、他者には違った世界が見えているかもしれません。)

SIerの問題点

1. 技術力向上のインセンティブが少ない
恐らく最大の原因はこれではないかと思います。要するに、必死に技術力を磨くインセンティブが乏しいのです。例えば、私のグループでは、それぞれのメンバの時間給が示されますが、桁が違うくらい高スキルな人と、他人の手助けが必要なぺーぺーの新人でも、せいぜい1.x倍にしかなりません。スキルを磨いてすごい能力を持っていても、まるで対数関数の極限に近づくように、たいして単金があがりません。(もちろん、「セット価格」ということもあると思いますが・・・)

そうすると、必死にスキルを磨いて、スムーズに仕事を進めるよりも、「土方仕事でも良いから、BPをいっぱい引き連れていけ(技術よりマネージメントのほうが稼げる)」となるし、(実際にやってることはしょぼくても)、炎上プロジェクトで土日出勤を繰り返したほうが金を稼げてしまいます。また、単金交渉も難しく、たかだか数%の単金を上げるだけでも大騒ぎです。(さすがに名前で一本引きされるくらい突出すると、価格交渉ができるかもしれませんが・・・。)

もちろん、人の評価に恐らく正解はないし、「○○倍の能力」なんて定量的に測れませんが、ソフトウェア開発の仕事は専門的で極めて難しく、現実に品質、生産性に比較にならない差が出ます。そこをもう少し評価して、単金の下限と上限のメリハリがあっても良いではないかなぁと素朴に思います。

また、これも日本的ですが「雇用制度が硬直的=似たような人たちで、看板を変えながら似たようなことを繰り返す、タコツボ化の促進」という問題もあり、「スキル磨いてすごい成果物を出すよりも、有力な○○さんと仲良くなって、仕事してるふりをする」方が、なぜか評価されたりします。実際には、まともに一人で仕事ができなくても、です。
(もちろん、まわりの人はみんないらいらしてるのですが、スキルを定量的に測るすべはなく、見かねた周りがどうにかするので、そのままです。)

実際に私が某プロジェクトに参画したときにリーダーに「守ってあげるからね」と言われましたが、私はこの言葉に非常に頭にきました。小学校の仲良しクラブじゃあるまいし、その人の成果物、貢献度等で外すかどうかは判断すべきで、それ以上でも、それ以下でもないはずです。「だれでも平等にチャンスを与える」というのと「私情、恩情」は違います。
(余談ですが、「失敗の本質」という書籍を読みましたが、この国は何十年も前から、本質的なところは何も変わっていないようです・・・。たぶん、数百年単位で培われてきた日本人の基本的な性質なのでは。)

2. ソフトウェア開発の技術力に対するコモンセンスがない
「1.」と密接に結びついていると思いますが、「ソフトウェア開発の技術力」という言葉に対するコモンセンスがないというのも、大きな問題です。要するに「なんだかわからないので評価できない」のです。単金が対数関数的にしか上がらないのも、スキルレベルの差により、生産性、品質が桁違いに変わるという現実がわからないので、考えるのを放棄しているではないかと疑っています。

私の場合は、たまたま上司や先輩が天然記念物みたいな人で、ソフトウェアの技術(デザインパターンアーキテクチャパターン等のOOP、並行処理、RDBMSアーキテクチャ、複雑なソフトウェアの設計 etc...)に強く、がっつり教え込まれて、怪しい仕事ばかり任せられたのでそういう方向に進んでしまいました。もちろん、文法やAPIフレームワーク、ツールの使い方を知っているのも重要ですが、ソフトウェア開発における技術力は、アーキテクチャや思考法、設計手法等がどれだけ身についているかが重要であって、どんな道具をかついでいるかは、そこまで重要ではないはずです。
(コンテキストごとに、最適なものを選ぶだけ。)

このような「ソフトウェア開発の技術力」に対するコモンセンスがないので、「頻繁に更新されるカラムにインデックスを張って性能劣化で大騒動」とか、「よくわからないから、とりあえずsynchronizedしちゃおう」、「保守性も責務の分割もあったものでないクラス設計」等の目を疑いたくなるような成果物が淘汰されないのだと思います。別に難しいことをやる必要はなく、「当たり前のことを当たり前にやる」ことが重要なのでは・・・。

謎の資格信仰も、恐らくコモンセンスがないことが原因で、とりあえず国家資格かベンダーの資格と取らせてお墨付きを得ようということなのでしょう。
(余談ですが、某社のP-CDPは、テスト勉強のしようがなく、極めて実践的だと思いました。)

3. 基本的に外注である
これも、「1.」「2.」と密接に関わっています。基本的に外注なので、人を選べないのです。もちろん、「チェンジで!」もないわけではないですが、会社間の関係もあるし、次の人が都合よく捕まるわけもなく、現実に目の前に仕事があるので、結局、「いる人に合わせる」やり方をせざるを得ません。もちろん、「低い方」にです。
しかも、私も生粋の日本人なので、あんまりはっきりものを言えず、他メンバとの関係を悪くしないように、うまく取り繕いながら仕事を進めざる負えなくなります。

外注なのは、ノウハウの蓄積、共有の上でも大きな障害となっています。それぞれのプロジェクトに数人ずつで常駐し、数か月に一回、打ち合わせで集合する感じなので、やる気のある人は勝手に伸びますが、そうでない人はいつまでも放置で一向に改善されませんし、優秀者ほど打ち合わせに参加できないのが常なので、ますます情報共有の場がなくなっていきます。
(そのことに気づいた私は、最近、メールでノウハウをやり取りするようにしています。これなら遠隔地にいても情報共有できます。)

4. プロセス重視である
とにかくプロセス重視です、何が何でも。もちろん、開発プロセスは必要ですし、無茶苦茶なカウボーイプログラミングを称賛するわけではないですが、手段が目的化しており、膨大なコストをかける割には、だれもうれしくないことが間々あります。しかも、それが「プロセス通り仕事を進めている」と称賛される傾向さえあります。
たぶん、責任を負いたくない、不確実性が怖いということなのでしょうが、顧客不在の自己保身的な仕事で、生産性も何もあったものではないです。

どうしてこうなのか?

私が思うには、前述したすべての問題の原因は、「雇用の流動性が低い」という一点に帰着するのではないでしょうか。
雇用の流動性が低いので、外注せざるを得ません。外注のリスクを減らすために、プロセスを徹底的に重視し、不確実性をつぶす方向に動きます。縛れば縛るほど自由度がなるし、外注はコスト競争が激しくなるので、単金の上げ幅が少なくなります。それでも仕事はあるので、技術力向上のインセンティブはなかなか働かないです。

これがもっと雇用の流動性が高く、弱肉強食の戦場のような業界ならば、淘汰圧力が働くので、しょぼいスキルの人は生き残れないはずです。
(そんな人に仕事は回ってこない、切られるだけ。)

もっとも、現実はもっと複雑だろうし(たぶん、そこまで技術は関係ない)、SIerでも、ちゃんとしたスキルを持った人はいっぱいいるので、そうとう偏ったものの見方ではあると思います。

どうすればよいのか?

できる範囲で、もっと競争を促進すべきではないでしょうか。
恩情主義ではなく、成果、貢献度主義にする。例えば、最近流行りのチケット駆動などを厳密に適用し、生産性、貢献度を見える化する。能力のランク分け、単金等にもっとメリハリをつける etc...

成果物、貢献度の競争が激しければ、ツールやフレームワークも、効率の良い新しいものを使わざるを得ないし、設計ミスや問題点が誰の責任か問われるようになれば、みんな勉強せざるを得なくなるはずです。
(責任も取れないのに、設計するなという気もします・・・。)

もっとも、現実には難しいだろうし、このままだらだら続くのかも・・・。

最後に

以上、長々と書いていきましたが、周りを変えられなくとも、せめて私一人だけでもスキルを磨いて、改善していけたらなぁと思ってます。
いろんなことを言う人がいますが、最後は立場や経歴等ではなく、「あなたが今、できることな何ですか。」と問われるだけのはずなので・・・。

Oracle Masterに見る資格試験の価値

昨年10〜12月頃に、「なんとなく面白そうだから」という理由で、Oracle MasterのBronze〜Goldまで一気に取得しましたが、今一すっきりせず、「何も理解できていないのではないか?」という気持ちでいっぱいでした。そこでOracle通の間で有名な「Exper Oracle Database Architecture」を読んだところ(まだ40%くらい)、頭の中のモヤモヤがスッキリして色々思うことがあるので、書いてみます。

Expert Oracle Database Architecture。Oracleのバイブル的な書籍です。凄く深くて広い。

Oracle Masterの価値

私はアプリ寄りなので、インフラ屋さんの仕事をしたことは無いですが、Oracle Masterはインフラ屋さんの間では非常に評価が高いようです。別の部署には、転職のためにOracle Master Platinaまで取得された方もいますし、Oracle専門の部隊では、はじめの数ヶ月でOracle Master Goldまで取得することが義務付けられています。

Oracle Master取得=価値が高い」というのは、アプリ屋の間でも半ば常識化していて、私がBronzeは5日ほど、Silverは週間ほどで取得して上司に報告したら、とても喜ばれていましたし、Goldを参考書一冊のみで一ヶ月強でいけました、と報告したら一気に株が上がったようです。

しかし、この試験に、それほど価値はあるのか、私は大いに疑問です。
例えば、Oracle Masterでは、「SQLは集合演算である」「SQLはRelational Modelをベースにしているが、中途半端に二次元の表ライクに歪められているので、非常にトリッキー」といった話は一切でてこないですし、「何故そのような仕組みなのか」「その仕組が存在することで、どのようなことが示唆されるか」った、現実世界の開発において、その知識がどのように活用されるのかも示されていません。
(Goldになると少しは現実世界に近い問題が出た気がしますが・・・)

資格試験とはそういうものなのかもしれませんが、機能や仕組みが、他と切り離されたぶつ切りの点のようなものとして示され、暗記を強要されます。ぶつ切りの点を暗記するのは、他の機能との関連や実際の挙動が示されないので、それ以上の理解が進まないし、忘れやすく、非常に効率が悪いです。こうなると、頭が悪くても短期の暗記力が強い人が優位に立ててしまいます。(例えば私とか・・・)

正直、未経験でRDBMSSQLの事がわからない人でも、暗記力が強い人が1〜2ヶ月で一気に取れる資格に「何の価値も無い」と言っても言い過ぎではないのでは。
(余談ですが、私はSilverに2週間、Goldに1ヶ月強かけましたが、無理して落ちるのも嫌なので、非常にのんびり勉強しており、資格試験取得のプロみたいなできる人なら両方合わせて2〜3週間くらいで行けるだろうな、と思います。その程度の試験です。)

本当に必要な知識

私は知識というのは「ぶつ切りの点」ではなく、「他の機能や仕組みとの横のつながりや、導入経緯、その機能、仕組みが示唆すること等も含めた面」を脳内で広げていくことだと考えています。また、知識を得る目的は「暗記マニアになること」ではなく、「現実に起こりうる状況下で、その知識を活用して問題を解決する手助けをするためである」というのが持論です。

Oracleの話で言えば、例えばLGWRがログを定期的にflushすることで得られるメリットは、「LGWRの仕組み」という点ではなくて、「Commitなどのトランザクション制御を含めた仕組み」にまで広げないと見えてこないはずです。
(もちろんそれだけではないでしょうが。)

他にもTransaction Isolation LevelがStatement Levelでどのように働くのか、Multi Version Concurrency ControlとUndo領域との関係なども、Oracleに閉じた話としても極めて大事ですが、他のRDBMSはどうなのか等にまで話を広げられると、「RDBMS」というカテゴリの話として、知識を広げられます。以上のように、「暗記特化で点ばかり重視する人」と、「知識を面で考えられる人」では、知識の幅、活用力で大きな差がでます。

また、「知識を面で考えること」が大事なのはプログラミングやソフトウェアアーキテクチャに関しても同様だと思います。APIや言語の機能のような「点」も大事ではあるのですが、RDBMS等のインフラ層の世界(個人的に最もクリティカルだと思います)、各種アーキテクチャパターンプロトコル、要件定義、テスティング等、様々な知識を縦と横に広げた「総合力」が必要なのではないでしょうか。
(総合力が無いと、非常に閉じた世界の、ひとりよがりなものしか作れないはずです。)

まとめ

長くなってしまいましたが、まとめると、資格試験はぶつ切りの点の知識を暗記させるという点で、それなりに役に立ちますが、それ以上のものではないです。知識と知識を繋がて、縦と横に広げたり、現実世界のコンテキストで活用するには、それなりに難しい本を読んで研究したり、実物を動かして経験を積むことが重要です。そして、そのような「現実に使える」知識を身につけるには、そう簡単なことではなく、一ヶ月や二ヶ月ではできず、年単位の勉強と考察(これ、極めて重要です)、経験が必要でしょう。

また、私の今の環境は恵まれており、調整担当の量産型SEではなく、高スキル者が評価されるので、もっと広く、深く、突き詰めていければ良いなぁと思っています。

自作のJavaScriptフレームワークを公開しました。

巷では、Backbone.js、AngularJS等が流行っているようですが、Java資産を使いたい、
そもそもスクリプト言語で開発なんてナンセンス!という、保守的なSIerさんには
評判が悪いようです。

そこで良い感じのSIによる、SIのための、SIのフレームワークを自作しました。
https://github.com/zyake/PAC.js

基本、JavaScriptビジネスロジックを書かせず、サーバサイドのJava等の言語で実行する設計です。
また、数年前に、大規模SIのJavaScript開発で死にそうになった経験を活かして、
非常に防御的な作りになっています。

ソースはMITライセンスなので、好きに使ってください。
ドキュメントは英語にしていますが、日本語のわかりやすい解説資料も用意しています。
Introduction of PAC.js (タイトルだけ英語です・・・)
https://onedrive.live.com/redir?resid=D5A32B4890940113!14185&authkey=!AE8bXpvvdSTdH9U&ithint=file%2c.pptx