すべての Web サーバーでコードを実行する

最終更新日: 2010年4月19日

適用対象: SharePoint Foundation 2010

開発シナリオでは、多くの場合、特に多数の管理機能を使用して開発する場合は、ファーム内のすべてのフロントエンド Web サーバー上で同じコードを実行する必要があります。たとえば、少なくとも HTTP 要求への応答方法に関しては、Microsoft SharePoint Foundation ファームのすべてのフロントエンド Web サーバーを同じように構成し、準備しなければなりません。このため、web.config ファイルの設定に対する変更は、すべてのフロントエンド Web サーバーに反映させる必要があります。また、ファーム ソリューションに含まれるアセンブリを、各フロントエンド Web サーバーのグローバル アセンブリ キャッシュ (GAC) に展開しなければならないケースもあります。

SharePoint Foundation には、多くの種類の構成および展開タスク用に目的別の API が用意されており、サーバーの同期状態が維持されます。たとえば、SPWebConfigModification クラスは、web.config ファイルのスタックの設定を変更するための機能を提供します。詳細については、「[方法] プログラムで Web.config 設定を追加および削除する」を参照してください。同様に、ファーム ソリューションを展開する場合は、サーバーの全体管理アプリケーションまたは SharePoint 管理シェルのどちらに展開するとしても、ソリューションに含まれるアセンブリが、すべてのフロントエンド Web サーバーの GAC に展開されます。カスタマイズされたファーム ソリューション展開機能を作成するには、SPSolution.Deploy() メソッドを使用します。

この目的別 API はすべて、SharePoint Foundation タイマー ジョブを使用してそれぞれの処理を実行します。タイマー ジョブという名前が付いているのは、指定した時間 (または作成直後) に実行されるように各ジョブを設定できるからです。ただし、このジョブは複数の Web サーバー (すべてのフロントエンド Web サーバーを含む) または特定のサーバーで実行されるように設定することもできるので、マルチサーバー ジョブとも呼ばれます。すべてのフロントエンド Web サーバーで実行する機能が必要な場合、そして、この機能に対する目的別 API がない場合は、タイマー ジョブ API を使用できます。このトピックでは、その方法について説明します。

注意

SharePoint Foundation 2010 におけるフロントエンド Web サーバーとアプリケーション サーバーの違いは、前のバージョンの製品に比べるとより概念的です。例外が 1 つありますが、SharePoint Foundation ソフトウェアはすべて、アプリケーション サーバーまたはフロントエンド Web サーバーとしてのサーバーの状態に関係なく、すべてのサーバーにインストールされます (例外は、ファームの Microsoft SQL Server データベースをホストするサーバーです。このコンピューターには、通常、SharePoint Foundation はインストールされません。このトピックで使用する "すべてのサーバー" という表現には、SharePoint Foundation がインストールされていないこのような専用データベース サーバーは含まれません)。一般的に、Microsoft SharePoint Foundation Web Application サービスが実行されている SharePoint Foundation サーバーはフロントエンド Web サーバーで、それ以外のサーバーはアプリケーション サーバーです。

タイマー ジョブを定義する

  1. Visual Studio で、空の SharePoint プロジェクトを作成し、そのプロジェクトをサンドボックス ソリューションではなくファーム ソリューションにします。

  2. [ソリューション エクスプローラー] でプロジェクト名を強調表示し、[プロパティ] ウィンドウの [パッケージにアセンブリを含める] が [True] に設定されていることを確認します。

  3. C# または Visual Basic クラス ファイルをプロジェクトに追加します。

  4. クラス ファイルを開き、Microsoft.SharePoint および Microsoft.SharePoint.Administration 名前空間の using ステートメント (Visual Basic の場合は Imports) を追加します。すべてのサーバーで実行するコードで呼び出される名前空間については、他の using ステートメントを追加しなければならない場合があります。このトピックの実行例では、System.Xml.Linq、System.Xml.XPath、System.IO、および System.Runtime.InteropServices の using ステートメントを追加します。

  5. Namespace Naming Guidelines (英語)」のガイドラインに準拠するように名前空間を変更します。たとえば、Contoso.SharePoint.Administration を使用します。

  6. クラス宣言を変更し、クラスが SPJobDefinition または SPJobDefinition の派生クラスから継承するように指定します。

  7. GuidAttribute 属性を使用して、クラス宣言を装飾します。これは、クラスが SPPersistedObject から直接または間接的に派生している場合は必須です。

  8. 既定の (パラメーターなしの) コンストラクターを、基本コンストラクターを呼び出すだけのクラスに追加します。

  9. StringSPWebApplicationSPServer、および SPJobLockType のパラメーターを持つコンストラクターを追加します。この実装により、基本コンストラクター SPJobDefinition(String, SPWebApplication, SPServer, SPJobLockType) が呼び出されます。次の例は、この時点でコードが C# でどのように表示されるかを示しています。

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    
    using Microsoft.SharePoint;
    using Microsoft.SharePoint.Administration;
    
    using System.Xml.Linq;
    using System.Xml.XPath;
    using System.IO; 
    using System.Runtime.InteropServices;
    
    namespace Contoso.SharePoint.Administration
    {
        [Guid("9573FAD9-ED89-45E8-BD8B-6A5034E03895")]
        public class MyTimerJob : SPJobDefinition
        {
            public MyTimerJob() : base() { }
    
            public MyTimerJob(String name, SPWebApplication wApp, SPServer server, SPJobLockType lockType)
                : base(name, wApp, server, lockType) { }
        }
    }
    

    注意

    アプリケーション サーバーを含め、すべてのサーバーでジョブを実行する必要がある場合は、クラスは SPServiceJobDefinition から派生していなければなりません。タイマー サービス (SPFarm.Local.TimerService) を、SPServiceJobDefinition(String, SPService) コンストラクターの SPService パラメーターとして渡します。

  10. DisplayName および Description プロパティのオーバーライドを追加します。DisplayName プロパティは、サーバーの全体管理アプリケーションのジョブ定義、スケジュールされたジョブ、およびジョブ履歴のリストに表示されるジョブのフレンドリ名です。Description はジョブの説明です。簡単にするために、以下の例では、これらのプロパティにはリテラル文字列が割り当てられています。より現実的なシナリオでは、ローカライズされた文字列をそれぞれに割り当てることを検討してください。

    public override string DisplayName
    {
        get
        {
            // TODO: return a localized name
            return "Add Contoso Mobile Adapter";
        }
    }
    
    public override string Description
    {
        get
        {
            // TODO: return a localized description
            return "Adds a mobile adapter for Contoso's voting Web Part.";
        }
    }
    
  11. Execute(Guid) メソッドのオーバーライドをクラスに追加します。

    public override void Execute(Guid targetInstanceId)
    {
        // INSERT HERE CODE THAT SHOULD RUN ON ALL SERVERS.
    }
    
  12. メソッドの実装は、すべてのフロントエンド Web サーバーで実行するコードに存在します。このコードは、ファームのすべての SharePoint Foundation サーバーで実行されている SharePoint 2010 Timer サービスによって呼び出されます。Execute(Guid) メソッドはジョブの実行時にタイマー サービスによって呼び出され、タイマー サービスのユーザー コンテキストで実行されます。単一サーバー SharePoint Foundation インストールでは、このユーザーは、通常、Network Serviceですが、興味深いのはマルチサーバー ファームの場合です。この場合、タイマー サービスは、ファームがコンテンツおよび構成データベースの読み取りと書き込みで使用するものと同じドメイン ユーザー アカウントのコンテキストで実行されます。このユーザーは、どのサーバーにおいてもコンピューター管理者ではありませんが、すべてのサーバーの WSS_ADMIN_WPG ユーザー グループのメンバーで、%ProgramFiles%\Common Files\Microsoft Shared\web server extensions\14\ ツリーおよび c:\Inetpub\wwwroot\wss ツリーのフォルダーに対する読み取り、書き込み、および実行権限があります。

    このトピックでは、モバイル バージョンの Web パーツ ページで使用するモバイル Web パーツ アダプターを作成した場合のシナリオについて考えます。このアダプターは、Web アプリケーションの compat.browser ファイルに登録する必要があります。このファイルは C:\Inetpub\wwwroot\wss\VirtualDirectories\ポート番号\App_Browsers フォルダーにあります。ポート番号には、Web アプリケーションのポート番号 ("80" など) が入ります。各フロントエンド Web サーバーにはファイルの独自のコピーがあり、すべてを同じように編集する必要があります。次のコードでは、タイマー ジョブが実行されるすべてのサーバーの compat.browser ファイルにアダプターを登録しています (すべてのサーバーで確実にジョブを実行する方法については、このトピックの以降の部分で説明します)。

    ヒントヒント

    簡単にするために、この例では、既定の URL 領域の compat.browser のみを変更します。より現実的なシナリオでは、IisSettings のすべてのメンバーを反復処理することを検討してください。

    public override void Execute(Guid targetInstanceId)
    {
        // Set the path to the file. The code that creates the MyTimerJob object associates
        // the job with a Web application.
        String pathToCompatBrowser = this.WebApplication.IisSettings[SPUrlZone.Default].Path 
                                   + @"\App_Browsers\compat.browser";
    
        XElement compatBrowser = XElement.Load(pathToCompatBrowser);
    
        // Get the node for the default browser.
        XElement controlAdapters = compatBrowser.XPathSelectElement("./browser[@refID = \"default\"]/controlAdapters");
    
        // Create and add the markup.
        XElement newAdapter = new XElement("adapter");
        newAdapter.SetAttributeValue("controlType", 
            "Contoso.SharePoint.WebPartPages.VotingWebPart, Contoso, Version=1.0.0.0, Culture=neutral, PublicKeyToken=ffec2e2af2b4c675");
        newAdapter.SetAttributeValue("adapterType",
            "Contoso.SharePoint.WebPartPages.VotingWebPartMobileAdapter, Contoso, Version=1.0.0.0, Culture=neutral, PublicKeyToken=ffec2e2af2b4c675");
        controlAdapters.Add(newAdapter);
    
        // Overwrite the old version of compat.browser with your new version.
        compatBrowser.Save(pathToCompatBrowser);
    }
    
  13. Visual Studio の [ビルド] メニューの [ソリューションの展開] を選択します。Visual Studio の空の SharePoiont プロジェクトの [サイトの URL] プロパティで単一サーバー SharePoint Foundation ファームが指定されている場合、この操作により、アセンブリがコンパイルされ SharePoint Foundation Solution (wsp) ファイルにパッケージ化されます。そして、そのパッケージがファームのソリューション ギャラリーにアップロードされ、アセンブリが GAC に展開されます。ただし、このトピックで興味深いのは、マルチサーバー ファームに展開する場合です。こうしたファームを指定するように [サイトの URL] プロパティを設定できますが、この状態で、ソリューションをファームのソリューション ギャラリーにアップロードし、[ソリューションの展開] をクリックしても何も起こりません。そこで、ソリューションを展開する必要があります。たとえば、サーバーの全体管理アプリケーションでギャラリーから直接展開します。SharePoint 管理シェルを使用することもできます。Visual Studio の [ビルド] メニューから [パッケージ] を選択するのも 1 つの方法です。これにより、アセンブリがコンパイルされパッケージ化されます。その後、SharePoint 管理シェルを使用してパッケージをファームのソリューション ギャラリーにインストールして展開できます。使用する手法にかかわらず、マルチサーバー ファームでは、展開手順を実行することにより、すべてのフロントエンド Web サーバーの GAC にアセンブリがインストールされます。

タイマー ジョブのインスタンスを作成する

タイマー ジョブの種類を定義するアセンブリがすべてのサーバーに展開されたら、プログラムによってそのジョブのインスタンスを作成およびスケジュールできます。この処理を行うコードはさまざまな開発コンテキストに含めることができます。展開するソリューションにカスタム タイマー ジョブが SharePoint Foundation 機能として含まれている場合は、ジョブ作成コードを機能レシーバーの FeatureActivated(SPFeatureReceiverProperties) メソッドのオーバーライドに含めることができます (この機能はファームまたは Web アプリケーションを範囲とする必要があります)。また、サーバーの全体管理アプリケーションをジョブ作成のカスタム アクションで拡張したり、SharePoint 管理シェルで実行できるカスタム PowerShell コマンドレットを作成したりすることもできます。このトピックでは、簡単なコンソール アプリケーションを使用します。これは、ジョブ作成コードの場所にかかわらず、ファーム管理者のユーザー コンテキストで実行する必要があります。

コードで実行する必要があるのは、主にカスタム タイマー ジョブの種類のオブジェクトを作成し、これを Web アプリケーションに関連付けて、その Schedule プロパティを設定するという処理です。

タイマー ジョブを作成およびスケジュールするには

  1. Visual Studio でコンソール アプリケーション プロジェクトを開始します。これは、タイマー ジョブ プロジェクトと同じ Visual Studio ソリューション内の 2 番目のオブジェクト、またはまったく別の Visual Studio ソリューションにできます。両方のアプローチに利点と欠点があります。このトピックでは、まったく別の Visual Studio ソリューションが使用されている場合について考えます。

  2. [ソリューション エクスプローラー] でプロジェクト名を右クリックし、[プロパティ] を選択します。

  3. [アプリケーション] タブで、[ターゲット フレームワーク] が .NET Framework 3.5 であることを確認します。

  4. [ビルド] タブで、[プラットフォーム ターゲット] が [x64] または [Any CPU] のどちらかであることを確認します。この選択については、「[方法] 適切なターゲット フレームワークおよび CPU を設定する」を参照してください。

  5. メニューの [すべてを保存] をクリックします。

  6. [ソリューション エクスプローラー] でプロジェクト名を右クリックし、[参照の追加] を選択します。[プロジェクト] または [参照] タブを使用して、上記の手順で作成したアセンブリに参照を追加します。

  7. 参照を Microsoft.SharePoint および Microsoft.SharePoint.Security アセンブリに追加します。

  8. コード ファイルを開き、Microsoft.SharePoint および Microsoft.SharePoint.Administration 名前空間の using ステートメント (Visual Basic の場合は Imports) を追加します。

  9. 上記の手順でカスタム タイマー ジョブに対して使用した名前空間と一致するように名前空間を変更するか、異なる名前空間を使用して、カスタム タイマー ジョブが宣言されている名前空間の using ステートメントを追加します。

  10. Main メソッドに次のコードを追加します。

    // Get a reference to the Web application for which you want to register the mobile Web Part adapter.
    SPWebApplication webApp = SPWebApplication.Lookup(new Uri("https://localhost/"));
    

    これは、Web アプリケーションへの参照を取得する唯一の方法です。詳細については、「サイト、Web アプリケーション、およびその他の主要オブジェクトへの参照を取得する」を参照してください。

  11. Main メソッドに次のコードを追加します。

    // Create the timer job.
    MyTimerJob myTJ = new MyTimerJob("contoso-job-add-mobile-adapter", webApp, null, SPJobLockType.None);
    

    このコードでは、次の点に注意してください。

    • MyTimerJob のコンストラクターの最初のパラメーターはジョブの内部名です。慣例により、内部ジョブ名はハイフンでつながったアルファベットの小文字の単語で構成され、"job" で始まっています。この例で示すように、内部ジョブ名の先頭には会社名を追加することを検討してください。

    • 2 番目のパラメーターでは、ジョブを適用する必要がある Web アプリケーションを指定します。

    • 3 番目のパラメーターを使用すると、ジョブを実行する必要がある特定のサーバーを指定できます。ジョブをすべてのフロントエンド Web サーバーで実行する必要がある場合、これは null になります。

    • 4 番目のパラメーターは、すべてのフロントエンド Web サーバーでジョブを実行するかどうかを決定します。SPJobLockType.None を渡すと、Microsoft SharePoint Foundation Web Application サービスが実行されているすべてのサーバーでジョブが実行されます。一方、SPJobLockType.Job を渡すと、ジョブは、Microsoft SharePoint Foundation Web Application サービスが実行されていて、最初に使用可能なサーバーでのみ実行されます (渡すことができる値はもう 1 つあります。詳細については、「SPJobDefinition」およびそのコンストラクターと他のメンバーに関するトピックを参照してください)。

  12. Main メソッドに次のコードを追加します。

    // Schedule the job.
    myTJ.Schedule = new SPOneTimeSchedule(DateTime.Now.AddSeconds(30.0));
    

    SPOneTimeSchedule は、SPSchedule クラスから派生した複数のクラスの中の 1 つで、Schedule プロパティの値として使用できます。直ちにジョブを実行するように設定するには、Now をコンストラクターに渡します。ただし、多少の時間 (この例では 30 秒) を追加すると、開発段階では役に立つ場合があります。これにより、サーバーの全体管理アプリケーションのジョブ定義リストおよびスケジュールされたジョブのリストに表示されるジョブを確認する時間ができるからです。完了したジョブは、ジョブ履歴リストに表示されます。定期的なジョブはジョブ定義リストにいつまでも残りますが、ワンタイム ジョブ、つまり、Schedule プロパティの値として SPOneTimeSchedule オブジェクトが指定されているジョブは、そのジョブが作成されてから実行されるまでの間だけ (この例では 30 秒) 存在し、ジョブ定義リストに表示されます。実行後、そのジョブは自動的に削除されます。

  13. Main メソッドに次のコードを追加します。

    // Save the scheduled job instance to the configuration database.
    myTJ.Update();
    

コードのコンパイル

  1. Visual Studio で、プロジェクトをビルドします。

  2. ファーム管理者のユーザー コンテキストで、ファーム内の任意のサーバー上で実行可能ファイルを実行します。

    ジョブは、実行がスケジュールされるまで、サーバーの全体管理アプリケーションのジョブ定義リストとスケジュールされたジョブ リストの両方に表示されます。この例では、この時間は 30 秒です。スケジュールされた時間に、すべてのサーバーでジョブが実行されます。実行後、このジョブはサーバーの全体管理アプリケーションのジョブ履歴リストに表示され、スケジュールされたジョブ リストからは消えます。また、これはワンタイム ジョブなので、正常に実行された後は、ジョブ定義リストからも消えます。

  3. ジョブがすべてのサーバーで実行されたことを確認します。この例でこれを行うには、2 つの属性を持つ controlAdapter 要素が compat.browser ファイルに追加されていることを確認します。このファイルは、C:\Inetpub\wwwroot\wss\VirtualDirectories\ポート番号\App_Browsers フォルダーにあります。ポート番号には、各サーバーの Web アプリケーションのポート番号が入ります。