Статьи

Разработка API на платформе NetBeans (часть 0): API метаданных

С этой статьей я начинаю новую серию публикаций о платформе NetBeans . Речь идет о том, как я разработал (и все еще разрабатываю) конкретный API проекта blueMarine, который является полностью автономным (таким образом, это хороший вопрос для обучения) и продуман в контексте высокомодульной концепции. Эта серия будет идти параллельно с темой «Идиомы для платформы NetBeans» и, конечно, дополняет ее. Я буду иметь дело с платформой NetBeans, но многие вещи, вероятно, могут быть повторно использованы за ее пределами. Кроме того, мы говорим о дизайне, который является общей темой.

Для справки, можно скачать все исходные коды API:

svn co https://bluemarine.dev.java.net/svn/bluemarine/trunk/src/Metadata --username guest

Самый последний Javadoc (автоматически созданный Hudson ) доступен по адресу https://bluemarine.dev.java.net/nonav/javadoc/Metadata/it-tidalwave-metadata/index.html.

Проект управляется CI ( http://www.tidalwave.it/hudson/job/blueMarine%20Metadata%20-%20nightly%20build/ ), и он получил хорошее покрытие (на данный момент 70 +%), поэтому он готов для включения в другие проекты.

Чтобы этот первый пост был простым, сегодня я просто представляю API и его спецификации и описываю первый класс.

BlueMarine — это, помимо прочего, DAM — менеджер цифровых активов, который включает в себя возможность манипулирования метаданными. Манипулирование означает извлечение их из файлов и в конечном итоге их сохранение в базе данных для быстрых запросов. Обратите внимание, что «в конце концов»: идея заключается в том, что blueMarine — это не только настольное приложение, но и набор модулей, которые можно перекомпоновывать и использовать для других целей (например, blueOcean — его версия на стороне сервера); требования могут быть разными в разных сценариях, и база данных может быть или нет. Также база данных может быть традиционной СУБД или «чем-то другим». 

Кроме того, есть много разных видов данных для управления. BlueMarine начал с фотографий (= растеризованные неподвижные изображения), но инкубатор поддерживает файлы PDF и некоторые типы фильмов; в будущем будет добавлено больше типов, таких как звуки и, возможно, векторные изображения. Чтобы сохранить общий дизайн, API метаданных (но на самом деле весь blueMarine) зависит не от конкретного класса, а только от общего объекта DataObject, который поступает из API платформы NetBeans ( API DataSystems ). DataObject представляет элемент данных, описываемый файлом (или набором файлов) со связанным MIME-типом: это правильный тип абстракции, который мне нужен.

Учитывая широкую гамму типов медиа, которые могут поддерживаться, у нас есть много разных типов метаданных. Например, фотографии имеют EXIF, TIFF, IPTC, а также некоторые проприетарные вещи (метаданные для форматов «необработанное изображение»). PDF-файлы и фильмы имеют разные метаданные. Поскольку вещь должна быть модульной, в некоторых случаях мне может потребоваться поддержка всех типов метаданных, в других — только подмножество. Это означает, что поддержка метаданных определенного вида также должна быть модульной, то есть ее необходимо реализовывать в автономном модуле, который можно добавлять или не добавлять в проект. С фасолью обязательнаэто хорошая вещь, API метаданных требует, чтобы каждый элемент метаданных моделировался полнофункциональным JavaBean, то есть классом с геттерами и сеттерами и поддержкой изменения свойств. Кроме того, API метаданных должен учитывать существующие реализации сторонних библиотек (например, класс EXIF), поэтому должна быть возможность автоматического добавления поддержки свойств, если в исходной библиотеке ее нет.

Это грубая, но достойная картина требований; Теперь мы можем кратко представить первые классы API.

Основной модуль — это «Метаданные» (it.tidalwave.metadata) — он просто содержит интерфейсы и в основном абстрактные классы и является отправной точкой для всего, что связано с метаданными. Самая важная вещь — это интерфейс Metadata — его экземпляр должен быть связан с каждым экземпляром DataObject, чтобы начать манипулировать метаданными для объекта, который вы делаете:

DataObject dataObject = ...
Metadata metadata = dataObject.getLookup().lookup(Metadata.class);

Метаданные действуют как глобальная фабрика / хранилище отдельных элементов метаданных, которые могут быть получены с помощью кода, такого как:

EXIF exif = metadata.findOrCreateItem(EXIF.class).get();
IPTC iptc = metadata.findOrCreateItem(IPTC.class).get();

Такие классы, как EXIF ​​или IPTC (так называемые «элементы метаданных»), не являются частью API метаданных, а напрямую поступают из сторонней библиотеки. Например, EXIF ​​и IPTC происходят из Mistral , который является моим простым проектом JSE. Metadata API гарантирует, что элементы метаданных поддерживают связанные свойства, поэтому exif и iptc могут напрямую использоваться с привязкой Beans.

Если в вашем проекте установлен API-интерфейс метаданных, гарантируется, что непустой объект всегда будет возвращен, даже если для этого объекта DataObject нет метаданных; это же утверждение справедливо для любого элемента метаданных (то есть экземпляры exif и iptc в приведенном выше примере всегда гарантированно не равны NULL). Этот подход называется шаблоном « Нулевой объект» или «Особый случай» (они не одно и то же, но служат одной и той же цели), и я принял его в качестве передового опыта для всего API, поскольку он позволяет избавиться от него. из множества тестов «если (что-то! = ноль)»: в двух словах, API-интерфейсы метаданных никогда не возвращают ноль, а скорее особый экземпляр объекта, который обеспечивает поведение «ноль». 

Метод findOrCreateItem () поддерживает ряд необязательных параметров: например, можно указать, откуда следует получать метаданные:

import static it.tidalwave.metadata.Metadata.StorageType.*;

EXIF exif = metadata.findOrCreateItem(EXIF.class, EXTERNAL).get();
IPTC iptc = metadata.findOrCreateItem(IPTC.class, INTERNAL).get();

Метаданные API не определяют строго, что означают «ВНЕШНИЙ» и «ВНУТРЕННИЙ», оставляя ответственность за конкретные реализации; в общем случае внешний источник будет означать внешний файл (например, вы хотите извлечь данные из фотографии), тогда как «ВНУТРЕННИЙ» будет означать внутреннюю базу данных (которая в этом случае предоставит возможность извлекать данные, даже если файл недоступен, например, потому что он находится на автономном диске). Есть некоторые другие необязательные поддерживаемые параметры, которые будут обсуждаться в следующей части — как вы можете видеть, они не реализованы с помощью классического шаблона интерфейса Fluent (который я ценю в других случаях, но он слишком многословен для простых вещей, подобных этому),скорее с другим подходом, который я описал в своем блоге .

Вариант метода find поддерживает несколько экземпляров (в одном файле может быть несколько экземпляров одного и того же элемента метаданных, и вы можете запросить как EXTERNAL, так и INTERNAL — это действительно происходит, если вы не указали StorageType):

List<EXIF> exifs = metadata.findOrCreateItems(EXIF.class).get();
List<IPTC> iptcs = metadata.findOrCreateItems(IPTC.class).get();

Можно динамически запрашивать поддерживаемые элементы метаданных, вызывая:

Set<Class<?>> itemClasses = metadata.getItemClasses();

Например, itemClasses может содержать такие вещи, как EXIF.class или IPTC.class. В общем случае вы получаете другой набор классов в соответствии с типом DataObject (фотография, фильм и т. Д.) И набором модулей реализации в пути к классам. Вы можете запросить, доступен ли элемент метаданных, не извлекая его, вызвав:

if (metadata.isItemAvailable(EXIF.class, EXTERNAL))
{
...
}

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