Библиотека API-интерфейса для Python, соответствующая спецификации CMIS: Часть 2. Построение ECM-инструментов для решения реальных задач с помощью языка Python и библиотеки cmislib

Создание учебного приложения

Во второй части данной серии статей, посвященной CMIS и Python, описывается построение xcopy-подобного инструмента для заполнения данными/миграции данных с использованием Python-библиотеки cmislib. Этот инструмент не только копирует xcopy-подобным образом локальные файловые системы в любой CMIS-совместимый репозитарий, но также понимает Exif-данные формата JPG и сохраняет их при копировании, если это возможно. Исследуйте шаг за шагом исходный код и узнайте, как использовать этот инструмент в командной строке. Кроме того, вы можете загрузить полнофункциональный исходный код.

Джей Браун, старший инженер, IBM

Джей БраунДжей Браун (Jay Brown) является старшим инженером подразделения Software Group корпорации IBM. Он занимался разработкой программного обеспечения в компаниях Shearson Lehman, General Electric и FileNet на протяжении двадцати одного года, последние одиннадцать из которых он участвовал в построении и проектировании ECM-систем для компаний FileNet и IBM. Для корпорации IBM Дж. Браун спроектировал и сконструировал генераторы кода для обоих API-интерфейсов продукта P8 4.x Content Engine (Java и .Net). Кроме того, на протяжении двух лет он был участником группы по архитектуре P8, а также занимался проектированием и конструированием CMIS-серверов. В настоящее время Дж. Браун руководит разработкой для CMIS-серверов.



13.12.2010

Совместное использование Python и CMIS

Другие статьи данной серии

Python и CMIS отлично сочетаются между собой. В первой части данной серии статей приведен обзор спецификации CMIS (Oasis Content Management Interoperability Services) и библиотеки cmislib (соответствующая ссылка приведена в разделе «Ресурсы»).

Базовые сведения

Данная статья является второй частью серии и состоит из трех основных разделов:

Часто используемые сокращения

  • API: Application program interface (Интерфейс прикладного программирования, API-интерфейс)
  • ECM: Enterprise content management (Управление корпоративным контентом)
  • IDE: Integrated development environment (Интегрированная среда разработки)
  • OASIS: Organization for the Advancement of Structured Information Standards (Организация по продвижению стандартов для структурированной информации)
  • PDF: Portable Document Format (Формат PDF)
  • REST: Representational State Transfer (Архитектурный подход, основанный на передаче состояния представления)
  • URL: Uniform Resource Locator (Унифицированный указатель информационного ресурса, URL-адрес)
  • XML: Extensible Markup Language (Расширяемый язык разметки)
  1. Python и CMIS — введение в статью и объяснение, почему Python естественным образом подходит для написания CMIS-инструментов.
  2. Последовательное рассмотрение программного кода — просмотр программного кода и объяснение его работы, чтобы читатель смог легко распространить его на другие типы метаданных и источников.
  3. Исполнение инструмента — анализ различных аспектов этапа исполнения инструмента, а также настройка зависимостей. Если вас интересует только загрузка и использование этого инструмента без объяснения того, зачем он нужен и как он работает, сразу переходите к разделу Исполнение инструмента.

Изучение нового языка в ходе построения инструмента

За месяц до того, как я начал писать эту статью, я искал небольшой проект, который можно было бы использовать для изучения языка Python. Когда речь идет об изучении нового языка, я могу прочитать учебник от корки до корки, но через неделю все забываю. Мне никогда не удается ощутить, как этот язык и связанные с ним инструменты работают в реальных условиях. Если я не делаю с помощью изучаемого языка что-либо полезное, то его концепции совершенно не закрепляются в моей памяти. Кроме того, недавно я вместе с Джеффом Поттсом занимался устранением некоторых проблем совместимости между его новой CMIS-библиотекой Python (cmislib) и серверами IBM, предназначенными для предварительной демонстрации технологии CMIS. Это привело меня к мысли об инструментарии для т.н. «долговечных» систем.

Инструменты для долговечных систем

Для начала поясню, что я подразумеваю под «долговечными» системами. В качестве примера я воспользуюсь областью, с которой я имею дело уже много лет. ECM-система, развернутая в организации или подразделении, может функционировать на протяжении очень долгого времени. ECM-системы могут быть большими и сложными, а также очень дорогостоящими с точки зрения их замены. Все это является серьезным основанием для того, чтобы оставить их в покое, если они продолжают работать достаточно хорошо. Вследствие вышеуказанных причин такие системы за время своей службы способны переживать частые и многократные консолидации и приобретения. Таким образом, когда я говорю о CMIS-репозитариях, то «по определению» я также говорю и об этих системах. За время своей службы долговечные системы накапливают совокупность весьма развитых инструментов, созданных пользователями и администраторами. Я думаю, что скрипты на языке Python с библиотекой cmislib потенциально способны существовать подобным образом. Я надеюсь, что вы согласитесь со мной, когда увидите мощный и в то же время простой пример того, на что эти две технологии способны при совместном использовании.

Преимущества стандартных интерпретируемых языков

Что касается инструментария для вышеописанных систем, применение интерпретируемых скриптов имеет ряд преимуществ. Для меня это чувство управляемости. Если ваш инструмент написан на языке Java™ или C ++, и вам необходимо что-либо исправить или изменить, вы не всегда можете сделать это достаточно легко даже при наличии исходного кода. Сколько раз при попытке перекомпилировать какую-либо программу, которую вы сами написали пару лет назад, вы обнаруживали, что для ее сборки необходимы специальная среда, библиотеки или настройки, след которых давно потерян? Конечно, большинство разработчиков сопровождает свой исходный код, однако сопровождение среды сборки является гораздо более сложным делом и обычно осуществляется только в средах со строгой дисциплиной. На инструменты это обычно не распространяется, несмотря на всю их важность. При использовании скриптов среда сборки может состоять из обычного текстового редактора и стандартной среды исполнения. (Примечание. Я не рекомендую писать большие программы на Python без хорошей интегрированной среды разработки для Python, такой как Eclipse с Pydev).

«Чувство управляемости», о котором я упоминал выше, становится еще глубже, если я знаю, что впоследствии смогу взять и перенести свой скрипт в операционную систему другого типа, в которой он будет вести себя прежним образом. Если я буду уверен, что смогу сделать это, то с гораздо большей вероятностью я выделю время на написание более качественного инструмента, поскольку буду знать, что мне не придется писать его снова для другой платформы. До тех пор, пока я могу получить интерпретатор для заданной платформы, я нахожусь в безопасности. И пока язык пользуется значительной общественной поддержкой, как Python, доступность не является проблемой. В последнее время в процессе разработки мне часто приходится переходить от Microsoft® Windows® к Linux® и обратно. Я совершенно не представляю, какую ОС буду использовать через два года. Все это заставило меня задуматься и послужило причиной того, что с недавних пор я стал горячим приверженцем Python.

Выбор проблемы для решения

В процессе работы над совершенствованием CMIS-сервера в корпорации IBM мне нужен был хороший инструмент для наполнения репозитария. Конечно, ассортимент данных в CMIS-репозитарии не ограничивается только документами. Репозитарий также может содержать большой набор метаданных, связанных с соответствующими документами. Один из распространенных типов документов с метаданными, с которым разработчикам регулярно приходится иметь дело, - это изображения в формате JPG. Мне нравится пользоваться для тестирования JPG-файлами, поскольку в их заголовках обычно присутствует много интересных метаданных. Это избавляет меня от необходимости написания дополнительного кода для генерации вымышленных значений. Эти данные в формате Exif (EXchangeable Image File Format) хорошо знакомы тем, кто занимается цифровой фотографией. Если вы еще не знакомы с этим форматом, я предлагаю вам начать с соответствующей статьи в Википедии (ссылка приведена в разделе «Ресурсы»).

Требования к инструменту

Инструмент, который мы собираемся создать, должен выполнять следующие функции:

  1. Копирование иерархии файлов в локальной файловой системе в любой заданный CMIS-совместимый репозитарий и сохранение имени копируемого файла в имени нового CMIS-документа в виде cmis:name.
  2. Если в ходе выполнения команды xcopy этот инструмент встретится с файлами типа JPG, то он также должен будет дублировать в репозитарий все Exif-данные, ассоциированные с изображениями (в предположении, что репозитарий содержит совместимые определения свойств). Это по-настоящему интересная часть создаваемого инструмента. Одни лишь функциональные возможности команды xcopy, хотя и весьма полезные как таковые, не представляют достаточного интереса для написания специально посвященной им статьи (однако вы сможете использовать наш инструмент только для копирования в CMIS – как в простой старой файловой системе – если это единственная цель, для которой вам нужен этот инструмент).

Рассмотрение программного кода

Теперь мы готовы рассмотреть параметры и код нашего инструмента.

Входные данные

Теперь определим, как этот инструмент будет использоваться. Во-первых, в качестве образца я использую почтенную операцию xcopy, поэтому это будет инструмент командной строки со следующими параметрами:

  • -s Исходный каталог для копирования.
  • -f Фильтр файлов (напр., *.doc, *.jpg, *.* и т.д.).
  • -t target path Полный путь к целевому каталогу для операции копирования (например: /pictures/Fiji_August_2010/). Инструмент будет исходить из допущения, что этот целевой путь существует, и создаст все дочерние папки автоматически.
  • serviceURL Полный URL-адрес сервисного XML-документа для целевого CMIS-репозитария (например: http://localhost:9080/cmis/service).
  • targetClassName (необязательный параметр) Тип класса для специфицирования подкласса cmis:document, который вы создадите для новых документов.

    Пример. Система управления контентом для фотографа имеет класс с именем CmisJpg. Класс CmisJpg содержит определения свойств для некоторых общих Exif-значений, которые эта система может запрашивать в процессе поиска в своем каталоге изображений. Если эти значения не будут получены, инструмент будет создавать все документы с типом cmis:document.

  • debug Режим Debug (необязательный параметр).

    Если этот параметр имеет значение true, то режим Debug пытается только воссоздать целевую структуру каталогов, но не будет копировать документы.

    Если этот параметр пропущен, то по умолчанию он имеет значение false (копировать все данные).

Код

Теперь я пройду по основным разделам кода и попутно опишу, как они работают. Имейте в виду, что с целью упрощения статьи я не буду рассматривать весь код с таким уровнем детализации, однако пропускаемые фрагменты не нуждаются в особых пояснениях. Для получения всего файла (с комментариями) обратитесь к разделу Загрузка. Вот перечень основных пунктов, которые я собираюсь рассмотреть.

  • Чтение в инструмент шести параметров этапа исполнения.
  • Инициализация библиотеки cmislib и получение объекта репозитария, который будет использоваться в качестве корневого каталога при любом обмене информацией со CMIS-репозитарием.
  • Проверка целевой папки и определения целевого класса на допустимость и существование в целевой библиотеке.
  • Просмотр базовой логики xcopy.
  • Чтение данных из Exif-заголовков JPG-файлов.
  • Использование cmislib для создания документа с метаданными.
  • Динамическое согласование Exif-данных со свойствами целевого класса на основе типов.

Шаг 1. Разбор параметров

Сначала необходимо передать шесть вышеперечисленных параметров (см. параграф Задание входов) в инструмент на этапе его исполнения. Я принял решение разделить эти параметры на две категории. В первую категорию входят значения, которые я собираюсь вводить с помощью командной строки. (Я хочу избавиться от необходимости специфицировать все шесть параметров с помощью командной строки, поскольку для данного репозитария половина из этих параметров не будет изменяться очень часто). Поскольку в качестве образца я использую операцию xcopy, таким образом будут вводиться только первые три параметра, а именно source (источник), target (цель) и filter (фильтр). Что относительно остальных параметров? Для них я воспользуюсь текстовым файлом .cfg, поскольку для данного репозитария эти параметры в любом случае будут статическими. Поместите этот конфигурационный файл в тот же каталог, где находится скрипт, и дайте ему имя cmisxcopy.cfg.

Листинг 1. Учебный файл cmisxcopy.cfg
[cmis_repository]
# сервисный url-адрес репозитария, в который будет осуществляться копирование
serviceURL=http://localhost:8080/p8cmis/resources/DaphneA/Service

# ЦЕЛЕВОЙ КЛАСС
# тип класса cmis:objectTypeId, который мы хотим создать
targetClassName=cmis:document
# РЕЖИМ DEBUG (ОТЛАДКА)
debug=false

# УЧЕТНЫЕ ДАННЫЕ ПОЛЬЗОВАТЕЛЯ
user_id=admin
password=password

Стандартный парсер ConfigParser из библиотеки Python отлично подойдет для чтения данных из этого конфигурационного файла (см. листинг 2.

Листинг 2. Использование парсера ConfigParser для извлечения значений из конфигурационного файла
import ConfigParser

# константы, имеющие отношение к конфигурационному файлу
configFileName = 'cmisxcopy.cfg'
cmisConfigSectionName = 'cmis_repository'

# считывание конфигурационных значений
config = ConfigParser.RawConfigParser()
config.read(configFileName)
try:
    UrlCmisService = config.get(cmisConfigSectionName, "serviceURL")
    targetClassName = config.get(cmisConfigSectionName, "targetClassName")
    user_id = config.get(cmisConfigSectionName, "user_id")
    password = config.get(cmisConfigSectionName, "password")
    debugMode = config.get(cmisConfigSectionName, "debug")
except:
    print "There was a problem finding the config file:" + configFileName + \

    " or one of the settings in the [" + cmisConfigSectionName + "] section ."
    sys.exit()

Для получения этих трех параметров существует еще более простой способ – использовать дополнительный файл config.py, содержащий только эти три константы. После этого основной скрипт сможет импортировать только файл config.py и использовать эти значения непосредственно. Далее я буду говорить о разборе командной строки.

В листинге 3 демонстрируется использование optparse из библиотеки Python для разбора командной строки с целью ввода трех остальных параметров. Настройте строку использования таким образом, чтобы она демонстрировала подсказку при предоставлении недействительных параметров. С помощью метода add_option() добавьте параметры –s,-t и –f для источника, цели и фильтра соответственно. И, наконец, выполните parse_args() для последовательного ввода значений в свой объект параметров.

Листинг 3. Использование optparse для сбора параметров командной строки
from optparse import OptionParser

usage = "usage: %prog -s sourcePathToCopy -t targetPathOnRepository 
    -f fileFilter(default=*.*)"
parser = OptionParser(usage=usage)

## получение значений источника и цели из командной строки
parser.add_option("-s", "--source", action="store", type="string", dest="source", 
    help="Top level of local source directory tree to copy")
parser.add_option("-t", "--target", action="store", type="string", dest="target", 
    help="path to (existing) target CMIS folder. All children will be created 
        during copy.")
parser.add_option("-f", "--filter", action="store", type="string", dest="filter", 
    default="*.*", help="File filter. e.g. *.jpg or *.* ")

(options, args) = parser.parse_args()
startingSourceFolderForCopy = options.source
targetCmisFolderStartingPath = options.target

Шаг 2. Инициализация соединения CMIS-репозитария с библиотекой cmislib

В листинге 4 , наконец, начинает выполняться реальная работа в области CMIS. Сначала необходимо импортировать модуль cmislib. Для загрузки новейшей версии этой библиотеки воспользуйтесь ссылкой на cmislib в разделе «Ресурсы». Сначала инициализируйте клиентский объект с помощью значений, которые вы извлекли ранее (в листингах 2 и 3), а затем извлеките объект defaultRepository как репозитарий repo. Затем попытайтесь извлечь целевую папку с помощью вызова getObjectByPath(path). (Имейте в виду, что cmislib делает получение этого объекта таким же простым, как получение объекта локальный папки из стандартной библиотеки Python). Если по каким-либо причинам эта попытка потерпит неудачу (например, заданная папка не существует), то задача не станет выполняться и выведет соответствующее сообщение. Затем с помощью вызова getTypeDefinition() осуществите аналогичную проверку определения типа цели.

После получения допустимых значений для двух вышеуказанных объектов cmislib вы будете уверены в хорошем качестве информации, имеющей отношение к целевой системе, и сможете продолжить обработку. Обратите внимание на строку, в которой инициализируется объект словаря folderCacheDict dictionary. Если мне впоследствии снова потребуется этот объект папки, я смогу получить его из этого кэша, а не предпринимать еще одно полномасштабное извлечение. Имейте в виду, что в действительности для конкретного алгоритма прохождения, который вы используете, кэширование не требуется, однако я оставил его, чтобы показать, как оно реализуется – на случай, если при последующем расширении возможностей этого инструмента у вас возникнет необходимость в кэшировании.

Листинг 4. Инициализация CmisClient и извлечение объектов целевой папки и целевого класса
from cmislib.model import CmisClient

# инициализация клиентского объекта на основе переданных значений
client = CmisClient(UrlCmisService, user_id, password)
repo = client.defaultRepository

# тестирование целевой папки на допустимость
targetCmisLibFolder = None
try:
    targetCmisLibFolder = repo.getObjectByPath(targetCmisFolderStartingPath)
except: 
    # terminate if we can't get a folder object
    print "The target folder specified can not be found:" + targetCmisFolderStartingPath
    sys.exit()

# инициализация кэша папки с помощью начальной папки
folderCacheDict = {targetCmisFolderStartingPath : targetCmisLibFolder} 

# тестирование типа целевого класса на допустимость
targetTypeDef = None
try:
    targetTypeDef = repo.getTypeDefinition(targetClassName)
except: 
    # завершение работы при невозможности получить объект описания типа целевого класса
    print "The target class type specified can not be found:" + targetClassName
    sys.exit()

Шаг 3. Реализация базовой логики xcopy

На этом шаге осуществляется прохождение по дереву файловой системы источника. Мы будем искать файлы, которые должны быть скопированы, и создавать дочерние подкаталоги, необходимые в целевой системе для обеспечения соответствия иерархий после копирования. Для прохождения по структуре каталогов источника используется метод walk() в Python-модуле os. Для каждого каталога в исходном дереве возвращается 3 группы (имя каталога, каталоги, файлы), которые вы будете передавать в свой метод processDirectory (см. полный листинг по ссылке в разделе Загрузка). Затем функция processDirectory() создаст целевой каталог (если он отсутствует) и передаст управление нашему методу copyFilesToCmis() для реального копирования файлов в недавно созданную целевую папку. Этот метод пройдет по каждому переданному ему файлу, при этом он будет отфильтровывать ненужные файлы и извлекать Exif-данные из файлов, имеющих тип .jpg. Вы познакомитесь с методом copyFilesToCmis() более подробно позднее, когда я буду обсуждать метаданные.

Шаг 4. Чтение Exif-данных

Из каждого файла, который имеет тип JPG, мы должны извлечь все значения Exif, чтобы они могли быть сохранены в целевом объекте. Таким образом, при возникновении потребности в чтении заголовков JPG-данных (Exif) будет существовать множество доступных способов для решения этой задачи. Для этой статьи я не собирался писать собственный способ, поскольку уже имеется несколько подходящих библиотек. Я выбрал библиотеку exif-py, поскольку она возвращает теги весьма распространенным способом: в виде словаря пар ключ-значение, где все значения представляют собой строки (см. раздел «Ресурсы», поскольку для исполнения своего скрипта вам потребуется загрузить библиотеку exif-py). Если вы захотите заменить ее чем-либо более экзотическим (или более специальным), это будет очень легко сделать – насколько я знаю, большинство библиотек использует словарь для представления совокупности свойств, а если какая-либо библиотека не делает этого, то соответствующая адаптация представляет собой тривиальную задачу. Реальный код для этого оказался необычайно простым. Вы можете лично убедиться в этом с помощью листинга 5.

Листинг 5. Чтение Exif-данных из JPG-файла
import EXIF
def getExifTagsForFile(filename):
    f = open(filename, 'rb')
    tags = EXIF.process_file(f)
    return tags

Шаг 5. Создание документа с метаданными

На этом шаге необходимо в целевом репозитарии создать документ со списком свойств, которые должны быть заданы для CMIS-репозитария, чтобы он точно знал, что следует создавать. Например, когда в CMIS: вы отправляете (POST) новый документ в какую-либо папку (что с логической точки зрения означает создание документа), именно список свойств объекта говорит CMIS, что нужно создавать. Что вы хотите создать – экземпляр cmis:document или подкласс документа с именем CmisJpg? Эта информация сообщается именно списком свойств.

Я предполагал, что реализация работы с метаданными с помощью рассматриваемого инструмента будет сложным делом (и потребует написания сложного кода). Я был приятно удивлен тем, что смог реализовать этот тип динамического отображения посредством столь небольшого объема программного кода. Снимаю шляпу перед Джеффом Поттсом (автором cmislib), который настолько облегчил эту задачу.

Внешний метод createCMISDoc(), показанный в листинге 6, вызывается для каждого документа, который должен быть создан в целевой папке. Последний передаваемый внутрь параметр propBag представляет собой список Exif-тегов, полученных от метода getExifTagsForFile. Вызов метода createPropertyBag() производится для задания свойства cmis:objectTypeId (специфицирующего тип создаваемого объекта) и для преобразования всех тегов в объекты, которые соответствовали бы содержащемуся в целевом репозитарии определению для этого конкретного свойства. В конце фрагмента производится фактическое создание документа в объекте целевой папки – посредством одной строки кода: newDoc = folder.createDocument(…).

Листинг 6. Метод createCMISDoc
def createCMISDoc(folder, targetClass, docLocalPath, docName, propBag):
    """
    Создание документа в заданной папке CMIS-репозитария.  
    Создание документа с типом targetClass
    Извлечение потока для этого документа из docLocalPath
    Присвоение этому документу имени docName 
    Задание свойств объекта с помощью параметра propBag
    """
    
    def createPropertyBag(sourceProps, targetClassObj):
        """
        Извлечение exif-тегов и возвращение совокупности свойств для
		представления в метод для создания документа
		"""
        
        # сначала задайте идентификатор (id) объекта класса. 
        propsForCreate =  {'cmis:objectTypeId':targetClassObj.id}
        for sourceProp in sourceProps:
            # проверьте по отображаемому имени наличие совпадающего свойства 
            if (sourceProp in targetClassObj.propsKeyedByDisplayName):
                # в типе класса в репозитарии есть совпадающее свойство!
                print "Found matching metadata: " + sourceProp
                # теперь осуществите согласование данных
                addPropertyOfTheCorrectTypeToPropbag(propsForCreate, 
                       targetClassObj.propsKeyedByDisplayName[sourceProp],
                         sourceProps[sourceProp] )

        return propsForCreate

    props = createPropertyBag(propBag, targetClass)
    f = open(docLocalPath, 'rb')
    newDoc = folder.createDocument(docName, props, contentFile=f)
    print "Cmislib create returned id=" + newDoc.id
    f.close()

Шаг 6. Динамическое отображение метаданных на целевой документ

В листинге 7 показан последний метод из рассматриваемых в данной статье. Этот метод осуществляет динамическое отображение метаданных для любых свойств, присутствующих в Exif-данных, в соответствии с любыми потребностями класса целевого документа. Это метод имеет имя addPropetyOfThecorrectTypeToPropbag(); что сказать, мне нравятся описательные имена функций. Этот метод выполняет самую сложную работу во всем скрипте, но как можно увидеть, благодаря библиотеке cmislib эту работу довольно просто проследить. Например, если переменная valueToAdd (это всегда строка, поступающая из exif-py) содержит значение 56, а свойство typeObj имеет тип int, то она преобразуется в объект типа int с соответствующим значением. Если целевой репозитарий говорит, что это должна быть строка, то никаких преобразований не осуществляется. Если преобразование не работает (к примеру, переменная содержит значение f2.0), то преобразование терпит неудачу. Это свойство пропускается, однако создание документа продолжается. Поэтому как бы вы ни задали определения свойств в своем целевом CMIS-репозитарии, этот код попытается выполнить свою работу.

Листинг 7. Метод addPropertyOfTheCorrectTypeToPropbag
def addPropertyOfTheCorrectTypeToPropbag(targetProps, typeObj, valueToAdd):
    """
    Определение типа 'typeObj', затем преобразование 'valueToAdd' в этот тип и
	установка его в targetProps, если это свойство является обновляемым. 
    На данный момент поддерживаются только следующие три типа:  
	string, integer и datetime
    """
    cmisUpdateability = typeObj.getUpdatability()
    cmisPropType = typeObj.getPropertyType()
    cmisId = typeObj.id
    if (cmisUpdateability == "readwrite"):   
        # first lets handle string types
        if (cmisPropType == 'string'):
            # this will be easy
            targetProps[cmisId] = valueToAdd
        if (cmisPropType == 'integer'):
            try:
                intValue = int(valueToAdd.values[0])
                targetProps[cmisId] = intValue
            except: print "error converting int property id:" + cmisId 
        if (cmisPropType == 'datetime'):
            try:
                dateValue = valueToAdd.values
                dtVal = datetime.datetime.strptime(dateValue ,
                         "%Y:%m:%d %H:%M:%S")

                targetProps[cmisId] = dtVal
            except: print "error converting datetime property id:" \
                 + cmisId

На рис. 1 показан инструмент FileNet® Enterprise Manager, демонстрирующий недавно созданный класс CmisJpg с некоторыми «тренировочными» Exif-свойствами, которые я задал для тестирования. Не забывайте, что для работы рассматриваемого кода «ключом» является отображаемое имя свойства. Для начала работы этого кода достаточно, чтобы в целевом классе существовало какое-либо свойство, отображаемое имя которого точно соответствует имени свойства в Exif-тегах.

Рисунок 1. Снимок экрана инструмента администратора FileNet P8 Content Engine, демонстрирующий свойства класса CmisJpg
Рисунок 1. Снимок экрана инструмента администратора FileNet P8 Content Engine, демонстрирующий свойства класса CmisJpg

Примечание об определениях и идентификаторах типов: Определения типов объектов в CMIS содержат уникальный идентификатор (ID), который задает тип, а также более «дружественное к пользователю» отображаемое имя. Скрипт cmisxcopy пытается отобразить имена Exif-свойств на идентичные имена на стороне CMIS. Причина, по которой я решил использовать отображаемое имя типа CMIS-объекта, а не идентификатор, состоит в том, идентификатор не обязан точно совпадать с отображаемым именем. В некоторых репозитариях он может совпадать, а в других репозитариях он может иметь совершенно иной вид, например, GUID. Подобные нюансы хорошо выявляются при тестировании с несколькими репозитариями. CMIS допускает любое значение идентификатора ID; проблема состоит в том, что если необходимо обеспечить соответствие клиентов спецификации, то они не должны базироваться на каких-либо допущениях об идентификаторах. Поскольку я должен убедиться в получении надлежащего свойства, я проверяю на соответствие отображаемые имена, поскольку они имеет более высокую вероятность соответствия.


Исполнение инструмента

Теперь пришло время настроить инструмент и опробовать его.

Предварительные условия

Сначала необходимо загрузить соответствующие файлы (см. раздел «Ресурсы»).

Установка библиотеки cmislib с помощью easy_install

  1. Загрузите Python-инструменты установки (см. раздел «Ресурсы») и установите их согласно инструкциям для своей платформы. Эти инструменты будут полезны и для других библиотек.
  2. Перейдите в свой каталог Scripts и выполните следующую команду: easy_install cmislib
  3. И это все! Процедура установки easy install выйдет в Интернет, найдет и загрузит библиотеку cmislib, а затем установит ее в вашу среду Python.
  • Установите Python 2.6.4 (новейший выпуск версии 2.x).
  • Загрузите полный исходный код для этой статьи и поместите его в каталог, из которого он будет запускаться на исполнение (я дал этому каталогу имя src).
  • Загрузите версию cmislib tar.gz и поместите каталог cmislib под src в своем исходном каталоге. Теперь в своем каталоге src вы должны увидеть подкаталог cmislib, содержащий четыре файла с расширением .py (init, exceptions, model и net). Я использую здесь ручную установку (вместо setup_tools), чтобы у вас был переносимый скрипт, который будет исполняться без какой-либо модификации локальной среды Python. Это имеет непосредственное отношение к вопросам долговечности, которые я рассматривал выше.
  • Загрузите библиотеку exif-py и поместите файл exif.py в свой каталог src.

Чтобы подготовиться к исполнению инструмента, сначала отредактируйте файл cmisxcopy.cfg (см. ссылку на загружаемый файл в разделе «Ресурсы») в том же самом каталоге src, где находится ваш скрипт cmisxcopy.py. В своем первом прогоне вы, скорее всего, ограничитесь копированием некоторых файлов без каких-либо специальных метаданных. В этом случае параметру targetClassName следует присвоить значение cmis:document («всегда присутствует»). Теперь присвойте параметру serviceURL допустимое значение, а затем задайте идентификатор пользователя (user_id) и пароль (password), после чего вы будете готовы к прогону.

Листинг 8. Файл cmisxcopy.cfg
          serviceURL=<your service url here>
          targetClassName=cmis:document
          # РЕЖИМ DEBUG (ОТЛАДКА)
          debug=false
          # УЧЕТНЫЕ ДАННЫЕ ПОЛЬЗОВАТЕЛЯ
          user_id=<user name to use for authentication>
          password=<password here>

Теперь убедитесь в том, что ваши библиотеки exif-py и cmislib находятся в указанном вами месте или в том же каталоге, как описано в предыдущем разделе. В частности, файл Exif.py должен быть в том же каталоге, где находятся остальные ваши исходные файлы (.py). Каталог cmislib, содержащий пять файлов библиотеки cmislib, должен находиться в том же каталоге, что и ваш исходный код. В качестве альтернативного варианта вы можете установить cmislib в свою среду Python с помощью метода easy_install. Инструкции по использованию метода easy_install приведены на врезке Установка библиотеки cmislib с помощью easy_install.

И, наконец, убедитесь в том, что Python находится в указанном вами месте, для чего введите с клавиатуры команду:

python -V

Эта команда должна возвратить что-либо подобное:

Python 2.6.4

Примечание. Я тестировал этот код с Python версии 2.6.4.

Пользовательский пример

Предположим, что вы хотите скопировать фотографии о своей поездке на Гавайские острова в свой репозитарий для последующей публикации. Ваши файлы находятся на вашем локальном диске (C:\photos\hawaiiVacationTree). В своем CMIS-репозитарии вы создали целевой каталог, который собираетесь использовать с помощью пути /photos/Hawaii. Кроме того, в этом каталоге имеется несколько видеофрагментов, которое вы не хотите включать, поэтому вы будете применять фильтр *.jpg, чтобы получать только фотографии. Вам необходимо ввести следующую команду:

python cmisxcopy.py -s C:\photos\hawaiiVacationTree -t /photos/hawaii -f *.jpg

Позднее, после того, как в своем CMIS-репозитарии вы настроите класс под именем Jpgs с определениями свойств, соответствующих необходимым вам Exif-значениям, вы сможете отредактировать свой файл cmisxcopy.cfg и изменить параметр targetClassName следующим образом:

targetClassName=Jpgs

Затем запустите повторно эту же команду – или измените целевой каталог, если не хотите иметь дубликаты в одинаковых папках. При копировании изображений в ваш CMIS-репозитарий все метаданные в ваших исходных Exif-тегах сохранятся.

Рисунок 2. Коннектор FireFox, указывающий на целевой каталог на публичном сервере Alfresco после выполнения операции cmisxcopy
Рисунок 2. Коннектор FireFox, указывающий на целевой каталог на публичном сервере Alfresco после выполнения операции cmisxcopy

Примечание относительно тестирования CMIS

При построении CMIS-клиента или CMIS-инструмента никогда не забывайте, что создать действительно совместимый CMIS-клиент практически невозможно, если вы тестируете его только с одним репозитарием. Если вы тестировали клиент только с одним репозитарием, значит, вы создали клиент только для одного, именно этого репозитария, а не истинный CMIS-клиент. Всегда выполняйте тестирование не менее чем с двумя совместимыми репозитариями. Это позволит убедиться в том, что ваш продукт соответствует спецификации CMIS от организации Oasis. В соответствии с этими принципами я протестировал этот код с серверами IBM и с публичным сервером Alfresco™. Моя задача в значительной степени упростилась благодаря тому, что Джефф Поттс уже выполнил работу по обеспечению совместимости cmislib со спецификацией, а не только с одним репозитарием. В результате после того, как я добился работы своего прототипа с CMIS-сервером от IBM, мой код начал работать с сервером Alfresco с первой же попытки.

Предложения для дальнейшей разработки.

Я оставляю несколько вполне очевидных расширений описываемого инструмента в качестве упражнения для читателя. Первое и самое очевидное расширение – позволить файловой структуре источника также быть CMIS-системой. Это небольшое изменение реально превратит этот инструмент в универсальное средство для миграции между репозитариями. Второе, менее очевидное, расширение состоит в поддержке метаданных для файлов других типов, таких как PDF, MP3 или документы Microsoft® Office. Например, если в локальной файловой системе имеется документ Word, который содержит свойство с именем invoiceNumber и значением US900201292339, а в целевом CMIS-классе есть строковое свойство с именем invoiceNumber, то при копировании этого документа в CMIS-репозитарий с помощью инструмента CmisXcopy он должен правильно обработать эти дополнительные данные.

Успехов в программировании!


Заключение

Другие статьи данной серии

Итак, вы увидели, насколько легко представить сложные операции с ECM-репозитариями в виде скриптов, совместимых со всеми репозитариями, которые соответствуют спецификации CMIS. Хотелось бы надеяться, что это побудит вас обратиться к CMIS – если вы до сих пор не сделали этого или если вам потребуется рассмотреть возможность применение cmislib и Python в ситуации, когда в вашей CMIS-совместимой ECM-системе нужно будет выполнить некоторую работу, допускающую представление в виде скриптов.


Загрузка

ОписаниеИмяРазмер
Sample configuration and Python scriptsourceForArticle.zip5KB

Ресурсы

Научиться

Получить продукты и технологии

Обсудить

Комментарии

developerWorks: Войти

Обязательные поля отмечены звездочкой (*).


Нужен IBM ID?
Забыли Ваш IBM ID?


Забыли Ваш пароль?
Изменить пароль

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Профиль создается, когда вы первый раз заходите в developerWorks. Информация в вашем профиле (имя, страна / регион, название компании) отображается для всех пользователей и будет сопровождать любой опубликованный вами контент пока вы специально не укажите скрыть название вашей компании. Вы можете обновить ваш IBM аккаунт в любое время.

Вся введенная информация защищена.

Выберите имя, которое будет отображаться на экране



При первом входе в developerWorks для Вас будет создан профиль и Вам нужно будет выбрать Отображаемое имя. Оно будет выводиться рядом с контентом, опубликованным Вами в developerWorks.

Отображаемое имя должно иметь длину от 3 символов до 31 символа. Ваше Имя в системе должно быть уникальным. В качестве имени по соображениям приватности нельзя использовать контактный e-mail.

Обязательные поля отмечены звездочкой (*).

(Отображаемое имя должно иметь длину от 3 символов до 31 символа.)

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Вся введенная информация защищена.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=XML, Open source, SOA и web-сервисы
ArticleID=600577
ArticleTitle=Библиотека API-интерфейса для Python, соответствующая спецификации CMIS: Часть 2. Построение ECM-инструментов для решения реальных задач с помощью языка Python и библиотеки cmislib
publish-date=12132010