понедельник, 4 марта 2013 г.

Создание Timer Jobs в SharePoint 2010 ориентированных на конкретный Web-Applications

Обзор Timer Jobs в SharePoint 2010

Microsoft SharePoint 2010 timer jobs выполняют большую часть фоновых работ, которые необходимы для поддержания вашей фермы SharePoint. Они могут быть настроены работать только один раз, или на постоянной графику. SharePoint используют timer jobs для поддержания длительных рабочих процессов, например чтобы очистить старые сайты и журналы, а также для мониторинга проблем фермы. SharePoint также использует timer jobs для выполнения логики развертывания решения и функцию активации. Timer jobs имеют ряд преимуществ. Они могут работать периодически и независимо от пользователей, которые имеют доступ к вашим сайтам SharePoint.
Вы можете просмотреть задания таймера на ферме с помощью страницы Job Definitions  в SharePoint 2010 Central Administration. Чтобы получить доступ к странице Job Definitions, выберите All Programs > Microsoft SharePoint 2010 Products > SharePoint 2010 Central Administration. На сайте центра администрирования выберите Monitoring ссылке. Наконец, нажмите ссылку Review Job Definitions в разделе Timer Jobs части страницы (рисунке 1).

Рисунок 1.  

Подготовка к созданию timer jobs SharePoint

Прежде чем вы сможете создать свой SharePoint timer jobs, необходимо установить Microsoft Visual Studio 2010. SharePoint 2010 должен быть установлен ​​на компьютере разработчика.
После установки необходимого программного обеспечения, запускаем студию и создаем новый проект SharePoint Empty Project, выбрав File > New > Project. (рисунок 2). В диалоговом окне проверьте, что NET Framework 3.5 выбран в раскрывающемся списке в верхней части, а затем нажмите кнопку ОК. 

Рисунок 2.












Сразу после нажатия кнопки ОК появится диалоговое окно, как показано на рисунке 3. Введите URL вашего сайта SharePoint в текстовое поле, выберите Deploy as a farm, а затем нажмите кнопку Готово. 

Рисунок 3.














После нажатия кнопки Готово Visual Studio создает и откроет проект, как показано на рисунке 4. Теперь, когда проект создан, вы можете начать добавлять классы, которые необходимы, чтобы сформировать основу вашей работы таймера SharePoint.

Рисунок 4.













Создание рaботы Timer jobs Sharepoint

Все задания таймера, в том числе те, которые установлены с SharePoint, создаются и выполняются с помощью SPJobDefinition класса. Для создания нового таймера SharePoint работу, необходимо сначала добавить в проект класс, который наследует от SPJobDefinition класса. В следующем фрагменте кода показан пример класса, который наследует от SPJobDefinition класса.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.SharePoint.Administration;
using Microsoft.SharePoint;

namespace ArchiveFilesJob {
    public class ArchiveJob : SPJobDefinition {

        public ArchiveJob() : base() { }

        public ArchiveJob(string jobName, SPWebApplication webApp)
            : base(jobName, webApp, null, SPJobLockType.Job) {
            this.Title = jobName;
        }

        public override void Execute(Guid targetInstanceId) {
            // Put your job's code here.
        }
    }
}

Во фрагменте кода, ArchiveJob класс наследуется от SPJobDefinition класса. Когда вы создаете свой конструктор, вы должны передать значения для четырех параметров, описанных ниже в конструктор базового класса.


Имя Название работы.
WebApplication Экземпляр SPWebApplication класса, которому принадлежит эта работа.
Сервер Экземпляр класса SPServer, связанных с этой работой. Передайте ноль, если эта работа не связана с конкретным сервером.
LockType SPJobLockType значение, которое указывает на обстоятельства, при которых несколько экземпляров задания могут быть запущены одновременно.
 
В предыдущем фрагменте кода, null передается в качестве значения для сервера, поскольку эта работа не связана с конкретным сервером. SPJobLockType.Job передается в качестве значения для LockType, чтобы предотвратить SharePoint от запуска нескольких экземпляров работу одновременно. В таблице 2 перечислены возможные значения SPJobLockType и их описания.


None Блокировка отключается. Работа выполняется на всех серверах фермы, независимо от параметра который вы передаете для SPServer объекта.
ContentDatabase Работа выполняется для каждой базы данных контента, связанного с веб-приложением.
Job Только один сервер может выполнить задание за один раз.

После создания конструкторов, необходимо переопределить метод выполнения SPJobDefinition класса и заменить код этого места на свой. Если ваш код может работать без дополнительной настройки, вы закончили создание этого класса. В противном случае, вы должны добавить конфигурацию для вашего проекта, как описано ниже.

Включение конфигурации для timer jobs  SharePoint

Для хранения данных конфигурации работы, необходимо создать классы, которые будут  содержать эту конфигурацию и хранить его в SharePoint. Во-первых, этот класс должен наследовать от класса SPPersistedObject. Далее, поля в классе должы быть открытыми, отмеченными [Persisted] атрибутом, а также встроенного типа данных (например, Guid, Int, String, и так далее), наследуется от SPAutoSerializingObject, или должен представляет собой коллекцию типа, который содержит одну из встроенных типов или тип наследования от SPAutoSerializingObject. Только поля сохраняются, а не свойства. Следующий фрагмент кода показывает два класса, которые используются для настройки ArchiveJob класса.

using Microsoft.SharePoint.Administration;
public class ArchiveJobSettings : SPPersistedObject {
    public static string SettingsName = "ArchiveFilesJobSettings";

    public ArchiveJobSettings() { }
    public ArchiveJobSettings(SPPersistedObject parent, Guid id)
        : base(SettingsName, parent, id) { }

    [Persisted]
    public Dictionary<Guid, SiteArchiveSettings> SiteArchiveSettings =
        new Dictionary<Guid, SiteArchiveSettings>();
}

public class SiteArchiveSettings : SPAutoSerializingObject {
    [Persisted]
    public Guid SiteCollectionId = Guid.Empty;
    [Persisted]
    public Guid SiteId = Guid.Empty;
    [Persisted]
    public Guid SourceDocLibId = Guid.Empty;
    [Persisted]
    public Guid DestinationDocLibId = Guid.Empty;
    [Persisted]
    public int MaxDays = 30;
    [Persisted]
    public bool Enabled = true;
Во фрагменте кода, класс ArchiveJobSettings используется для настройки ArchiveJob класса. Он наследует от SPPersistedObject, и имеет одно поле с именем SiteArchiveSettings, помеченный [Persisted] атрибутом. Поле SiteArchiveSettings позволяет выполнение заданий на любом количестве сайтов в том же веб приложении. Тип данных поля SiteArchiveSettings является коллекцией SiteArchiveSettings объектов, наследуемых  от класса SPAutoSerializingObject.
Следует отметить, что класс ArchiveJobSettings имеет два конструктора - конструктор по умолчанию, который является обязательным для всех классов сериализации, и второй конструктор, который вызывает конструктор своего базового класса. Второй конструктор передает экземпляр SPPersistedObject, который действует как родительский объект ArchiveJobSettings, и Guid, который используется, чтобы назначить ему уникальный идентификатор в SharePoint. Поскольку ArchiveJob связан с конкретным объектом SPWebApplication то тот же объект SPWebApplication становится родителем объекта ArchiveJobSettings, когда он сохраняется в SharePoint.
Класс SiteArchiveSettings содержит сайт-специфические настройки для ArchiveJob класса. Он наследует от SPAutoSerializingObject, и его поля придерживаются тех же правил, как и для класса SPPersistedObject. Поля, отмеченные [Persisted] атрибутом позволяет им сохраняться в  SharePoint.

ArchiveJobSettings jobSettings =
    new ArchiveJobSettings(webApplication, Guid.NewGuid());
// Be sure to change the URL below to one appropriate for your
// environment.
using (SPSite siteCollection = new SPSite("http://local.demo.com")) {
    using (SPWeb site = siteCollection.RootWeb) {
        SiteArchiveSettings siteSettings = new SiteArchiveSettings();
        siteSettings.SiteCollectionId = site.Site.ID;
        siteSettings.SiteId = site.ID;
        siteSettings.SourceDocLibId = site.Lists["MyDocLib"].ID;
        siteSettings.DestinationDocLibId =
            site.Lists["MyDocLibArchive"].ID;
        siteSettings.MaxDays = 30;
        siteSettings.Enabled = true;
        jobSettings.SiteArchiveSettings.Add(site.ID, siteSettings);
    }
}
jobSettings.Update(true);

Во фрагменте кода, вы создаете конфигурацию ArchiveJobSettings класса, передавая экземпляр SPWebApplication, который представляет собой объект веб-приложения SharePoint и новый Guid. После этого, можно настроить поля в классе.
Для получения конфигурации, необходимо получить экземпляр SPWebApplication, вызвать его метод GetChild, а затем передать в него класс, который у вас служит хранилищем конфигурации.
public override void Execute(Guid targetInstanceId) {
    ArchiveJobSettings jobSettings =
        this.WebApplication.GetChild<ArchiveJobSettings>(ArchiveJobSettings.SettingsName);
    if (jobSettings == null) {
        return;
    }
    // Code omitted.
}

Класс ArchiveJobSettings был извлечен из SharePoint, вызвав GetChild метод родителя SPWebApplication. Если ничего не было ранее сохранено в SharePoint, вызов GetChild метод возвращает нулевое значение. 

Развертывание задания таймера

Теперь, когда вы создали задание и конфигурации классов, вы должны добавить функцию установки решения в SharePoint сервере 

Рисунок 5
 












Добавляем новую фичу, конфигурируем её при необходимости. Добавляем обработчик для включиения и выключения фичи. Для этого в контекстном меню фичи выбираем пункт Add Event Receiver. После этого, в сгенерированном коде вы должны раскомментировать FeatureActivated и FeatureDeactivating методы. Остальные можно удалить, потому что они не используются. Далее, добавляем код в FeatureActivated метод для создания таймера и код в FeatureDeactivating метод, чтобы удалить его соответственно. В следующем примере показано, как зарегистрировать и отменить регистрацию ArchiveFilesJob объекта.

public override void FeatureActivated(SPFeatureReceiverProperties properties)
{
    SPWebApplication webApplication = (SPWebApplication)properties.Feature.Parent;

    // Remove job if it exists.
    DeleteJobAndSettings(webApplication);

    // Create the job.
    ArchiveJob job = new ArchiveJob(ArchiveJob.JobName, webApplication);

    // Create the schedule so that the job runs daily, sometime between midnight and 4 A.M.
    SPDailySchedule schedule = new SPDailySchedule();
    schedule.BeginHour = 0;
    schedule.BeginMinute = 0;
    schedule.BeginSecond = 0;
    schedule.EndHour = 3;
    schedule.EndMinute = 59;
    schedule.EndSecond = 59;
    job.Schedule = schedule;
    job.Update();

    // Configure the job to archive files in a few sites in this web
    // application.
    ArchiveJobSettings jobSettings = new ArchiveJobSettings(webApplication, Guid.NewGuid());
    using (SPSite siteCollection = new SPSite("http://local.demo.com"))
    {
        SPWeb site = siteCollection.RootWeb;
        SiteArchiveSettings siteSettings = new SiteArchiveSettings();
        siteSettings.SiteCollectionId = site.Site.ID;
        siteSettings.SiteId = site.ID;
        siteSettings.SourceDocLibId = site.Lists["MyDocLib"].ID;
        siteSettings.DestinationDocLibId = site.Lists["MyDocLibArchive"].ID;
        siteSettings.MaxDays = 30;
        siteSettings.Enabled = true;
        jobSettings.SiteArchiveSettings.Add(site.ID, siteSettings);
        // Add more sites here.
    }
    jobSettings.Update(true);
}

public override void FeatureDeactivating(SPFeatureReceiverProperties properties)
{
    SPWebApplication webApplication =  (SPWebApplication)properties.Feature.Parent;
    DeleteJobAndSettings(webApplication);
}

private void DeleteJobAndSettings(SPWebApplication webApplication)
{
    foreach (SPJobDefinition job in webApplication.JobDefinitions) {
        if (job.Name == ArchiveJob.JobName)
        {
            job.Delete();
            break;
        }
    }

    // Delete the job's settings.
    ArchiveJobSettings jobSettings =
        webApplication.GetChild<ArchiveJobSettings>(ArchiveJobSettings.SettingsName);
    if (jobSettings != null)
        jobSettings.Delete();


В примере кода, FeatureActivated метод получает экземпляр SPWebApplication класса (для которых эта функция активируется). Далее, FeatureActivated метод вызывает метод DeleteJobAndSettings, чтобы удалить существующие таймеры и настройки, если они уже существуют. Далее создайм таймер. После того, настраиваем собственное расписание запуска.

SPMinuteSchedule Выполнение задания каждую минуту.
SPHourlySchedule Выполнение задания каждый час.
SPDailySchedule Выполнение задания в день.
SPWeeklySchedule Выполнение задания в неделю.
SPMonthlySchedule Выполнение задания в месяц.
SPYearlySchedule Выполнение задания в год. 

В предыдущем примере кода, SPDailySchedule класса используется для выполнения задания в день. Указываются значения самого раннего и самого позднего времени, соответственно. Таймер служба случайным образом выбирает время, в течение этого интервала, чтобы начать работу. После того как вы установите расписание для свого таймера, необходимо вызвать обновление метода таймера. Последним шагом в методе для создания экземпляра класса ArchiveJobSettings, установливаем его свойства, а затем сохраняем.
В FeatureDeactivating методе, метод DeleteJobAndSettings вызывается, чтобы удалить ранее зарегистрированные таймеры. Метод использует DeleteJobAndSetting.

Тестирование и отладка ваших SharePoint timer jobs

При отладке кода SharePoint, устанавливается Debug режим, а затем нажмите F5 для отладки проекта. Visual Studio компилирует код, в пакете SharePoint решения (WSP-файл), а затем развертывает пакет решений в SharePoint сервере.

Для отладки задания таймера

Для отладки таймера в SharePoint, необходимо подключить к процессу, выберите Debug > Attach to proccess в строке меню Visual Studio.
В открывшемся окне проверяем, что флажки в нижней части выбраны, а затем выберираем OWSTIMER.EXE из списка доступных процессов.
Рисунок 6











  
Нажмите Присоедениться, чтобы завершить присоединение к службе таймера SharePoint. Теперь вы можете добавить точки остановки в вашем кода.