SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

japan.internet.com翻訳記事

アノテーションとアスペクトによる宣言型プログラミングの試み

EJB 3.0を利用して分野横断的な問題を解決する

  • X ポスト
  • このエントリーをはてなブックマークに追加

リソース注入

 現在のEJB 3.0ドラフト仕様では、リソースを@Resourceアノテーションで宣言し、そのリソースをコンテナからEJBに注入することができます(@Resourceアノテーションは共通アノテーション仕様の中で規定されています)。依存性注入とは、オブジェクトの依存性をオブジェクト自身が明示的に作成する代わりに、オブジェクト外部のエンティティから提供(注入)する手法です。これを茶化して「ハリウッド方式」と呼ぶこともあります。本人からのアクセスは認めず、「用があるときはこちらからお電話します」というわけです。

 サンプルのTravelAgencyServiceImplクラスを見てみましょう。このクラスでは、データの持続性を実現するために、IFlightDAOインターフェイスの実装を見つける必要があります。これは伝統的に、Factory、Singleton、Service Locatorなどのパターンを用いて実現されます。ソリューションの一例は次のような形になります。

public class TravelAgencyServiceImpl implements ITravelAgencyService
{
    public IFlightDAO flightDAO;

    public TravelAgencyServiceImpl()
    {
        flightDAO = FlightDAOFactory.getInstance().getFlightDAO();
    }

    public void bookTrip(long outboundFlightID,
                         long returnFlightID, int seats)
            throws InsufficientSeatsException
    {
        reserveSeats(outboundFlightID, seats);

        reserveSeats(returnFlightID, seats);
    }
}

 この実装では、特別なファクトリクラスが作成されます。容易に想像されるように、このクラスは、どこかに保存されている設定情報を読み取り、IFlightDAOの実装をどんな形で生成すればよいかを知ることになります。サービスがその依存性を明示的に作成する代わりにコンテナから依存性を注入する場合は、設定を含むオブジェクト生成の詳細がコンテナに委譲されます。これにより、アプリケーションに必要なコンポーネントを設定に応じて簡単に「配線」し、決まり切ったシングルトンやファクトリコードの多くを排除することが可能となります。

 このクラスが、JSR 250リソースアノテーションで宣言されたIFlightDAOの実装に対して依存性を持つ場合、クラスの実装は次のようになります。

public class TravelAgencyServiceImpl implements ITravelAgencyService
{
    @Resource(name = "flightDAO")
    public IFlightDAO flightDAO;

    public void bookTrip(long outboundFlightID,
                         long returnFlightID, int seats)
            throws InsufficientSeatsException
    {
        reserveSeats(outboundFlightID, seats);

        reserveSeats(returnFlightID, seats);
    }
}

 この例では、「flightDAO」というリソースの適切な実装が、コンテナによってサービスクラスに提供されます。ところで、EJB 3.0がまだリリースされていない現在、リソース注入を利用するにはどうすればよいのでしょう。SpringやPico Containerといった軽量コンテナを使えば依存性注入を実現できますが、筆者が知る限り、JSR 250リソースアノテーションを用いて注入条件を指定する軽量コンテナはまだ存在しません(いずれ、この場で紹介されるとは思いますが)。

 1つの方法として、アスペクトを用いて依存性注入を実装することが考えられます。@Resourceアノテーションをこの目的で使えば、その実装はEJB 3.0による方法と一貫性を持ち、EJB 3.0の実装と前方互換となります。実際、これはそれほど難しくありません。次のコードはAspectJで作成したアスペクトです。これは、@Resourceアノテーションで指定されたフィールドを注入します。

@Aspect
public class InjectionAspect
{
    private DependencyManager manager = new DependencyManager();

    @Before("get(@Resource * *.*)")
    public void beforeFieldAccesses(JoinPoint thisJoinPoint)
        throws IllegalArgumentException, IllegalAccessException
    {
        FieldSignature signature =
            (FieldSignature) thisJoinPoint.getSignature();

        Resource injectAnnotation =
            signature.getField().getAnnotation(Resource.class);

        Object dependency = manager.resolveDependency(
            signature.getFieldType(), injectAnnotation.name());

        signature.getField().set(thisJoinPoint.getThis(), dependency);
    }
}

 このアスペクトは、プロパティファイル内から実装クラスを探し(このロジックはDependencyManagerオブジェクトにカプセル化されています)、見つけた実装クラスを@Resourceアノテーションで指定されたフィールドに注入したのちに、フィールドアクセスを行います。なお、この依存性注入はJSR 250とEJB 3.0のやり方をまねています。言うまでもなく、この実装は完全なものでありませんが、JSR 250互換の方法でリソース注入を実現するとき、必ずしもEJBを使わなくてよいことが分かります。

セキュリティ

 JSR 250とEJB 3.0は、リソース注入に加え、アノテーションを用いてセキュリティメタデータを表現することも実現しています。javax.annotation.securityパッケージの中で規定されている5つのアノテーション(RunAsRolesAllowedPermitAllDenyAllRolesReferenced)をメソッドに適用することで、セキュリティ要件を定義できます。例えば、前掲のbookTripメソッドについて、呼び出し元のロールが「user」である場合のみ実行を許可すると宣言するには、次のようなアノテーションを記述してセキュリティ制限を課します。

public class TravelAgencyServiceImpl implements ITravelAgencyService
{
    @Resource(name = "flightDAO")
    public IFlightDAO flightDAO;

    @RolesAllowed("user")
    public void bookTrip(long outboundFlightID, long returnFlightID,
        int seats) throws InsufficientSeatsException
    {
        reserveSeats(outboundFlightID, seats);

        reserveSeats(returnFlightID, seats);
    }
}

 このアノテーションは、所定のロールを持つ呼び出し元にだけメソッドの実行を許可するようコンテナに指示します。次に、このセキュリティ制約をアプリケーションに課す簡単なアスペクトの例を紹介しましょう。

@Aspect
public class SecurityAspect
{
    @Around("execution(@javax.annotation.security.RolesAllowed 
* *.*(..))")
    public Object aroundSecuredMethods(
        ProceedingJoinPoint thisJoinPoint) throws Throwable
    {
        boolean callerAuthorized = false;

        RolesAllowed rolesAllowed =
            rolesAllowedForJoinPoint(thisJoinPoint);
        for (String role : rolesAllowed.value())
        {
            if (callerInRole(role))
            {
                callerAuthorized = true;
            }
        }

        if (callerAuthorized)
        {
            return thisJoinPoint.proceed();
        }
        else
        {
            throw new RuntimeException(
               "Caller not authorized to perform specified function");
        }
    }

    private RolesAllowed rolesAllowedForJoinPoint(
        ProceedingJoinPoint thisJoinPoint)
    {
        MethodSignature methodSignature =
            (MethodSignature) thisJoinPoint.getSignature();
        Method targetMethod = methodSignature.getMethod();

        return targetMethod.getAnnotation(RolesAllowed.class);
    }

    private boolean callerInRole(String role)
    {
        ...
    }
}

 このアスペクトは、@RolesAllowedアノテーションで指定されたメソッドの実行要求に対して必ず適用され、呼び出し元のロールがアノテーションで指定されたロールのいずれかに一致するかどうかによって、呼び出し元にメソッドの実行許可を与えるかどうかを判断します。もちろん、ユーザーのロールを調べて実行を許可する部分については、JAASやカスタムソリューションなど、任意のアルゴリズムを使うことができます。本稿のサンプルコードでは、簡略化のためにサーブレットコンテナに委譲しました。

次のページ
トランザクション

この記事は参考になりましたか?

  • X ポスト
  • このエントリーをはてなブックマークに追加
japan.internet.com翻訳記事連載記事一覧

もっと読む

この記事の著者

japan.internet.com(ジャパンインターネットコム)

japan.internet.com は、1999年9月にオープンした、日本初のネットビジネス専門ニュースサイト。月間2億以上のページビューを誇る米国 Jupitermedia Corporation (Nasdaq: JUPM) のニュースサイト internet.comEarthWeb.com からの最新記事を日本語に翻訳して掲載するとともに、日本独自のネットビジネス関連記事やレポートを配信。

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

Rod Coffin(Rod Coffin)

最新の開発プロセスおよびテクノロジの大規模導入のコンサルテーションを行うValtech Skill Developmentの上級顧問。主にエンタープライズJava開発およびアジャイル手法を行うチームを指導している。アスペクト指向プログラミングからEJB 3.0に至るまで各種の話題について数本の記事を...

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/365 2006/08/22 15:45

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング