Artezio LLC Address: 3G Gubkina Str., suite 504, Moscow, Russia, Phone: +7 (495) Fax: +7 (495) The art of technology Реализация интегрированных с Seam конвертеров для jBPM
Artezio LLC Address: 3G Gubkina Str., suite 504, Moscow, Russia, Phone: +7 (495) Fax: +7 (495) The art of technology Цели исследования Проанализировать стандартную реализацию конверторов типов в jBPM Определить ограничения накладываемые стандартной реализацией и найти пути для их обхода
Artezio LLC Address: 3G Gubkina Str., suite 504, Moscow, Russia, Phone: +7 (495) Fax: +7 (495) The art of technology Используемые версии фреймворков JBoss Seam: версия GA jBPM: версия (так как используется по умолчанию в Seam, с которой данный фреймворк интегрирован)
Artezio LLC Address: 3G Gubkina Str., suite 504, Moscow, Russia, Phone: +7 (495) Fax: +7 (495) The art of technology Стандартное использование Bijection для переменных с BUSINESS_PROCESS контекст Пример использования: public class TodoList implements Todo { = ScopeType.BUSINESS_PROCESS, required = false) private Payload payload; = "todo") public void createTodo() { payload = new Payload(); payload.setName(UUID.randomUUID().toString()); payload.setNumber(new Random().nextInt(1000)); entityManager.persist(payload); } … } В данном случае, после выполнения метода createTodo(), выполняется Outjection поля payload, которую Seam сохраняет в BUSINESS_PROCESS контекст (поскольку Seam интегрирован с jBPM, то технически это реализуется как запись значения этого поля в виде переменной, прикрепленной к задаче(TaskInstance), в базу данных jBPM).
Artezio LLC Address: 3G Gubkina Str., suite 504, Moscow, Russia, Phone: +7 (495) Fax: +7 (495) The art of technology Способ хранения переменных, прикрепленных к задаче, в базе данных jBPM Переменные, прикрепленные к задаче jBPM, хранятся в таблице JBPM_VARIABLEINSTANCE, которая имеет следующую структуру: Field TypeNullKeyDefaultExtra ID_ bigint(20)NoPRINULLauto_increment CLASS_ char(1)No VERSION_ int(11)NoNULL NAME_ varchar(255)YesNULL CONVERTER_ varchar(255)YesNULL TOKEN_ bigint(20)YesMULNULL TOKENVARIABLEMAP_ bigint(20)YesMULNULL PROCESSINSTANCE_ bigint(20)YesMULNULL BYTEARRAYVALUE_ bigint(20)YesMULNULL DATEVALUE_ datetimeYesNULL DOUBLEVALUE_ doubleYesNULL LONGIDCLASS_ varchar(255)YesNULL LONGVALUE_ bigint(20)YesNULL STRINGIDCLASS_ varchar(255)YesNULL STRINGVALUE_ varchar(255)YesNULL TASKINSTANCE_ bigint(20)YesMULNULL
Artezio LLC Address: 3G Gubkina Str., suite 504, Moscow, Russia, Phone: +7 (495) Fax: +7 (495) The art of technology Типы данных для переменных, поддерживаемые jBPM, и стандартные конвертеры для приведения к данным типам Исходя из предыдущей таблицы, типы данных, которые могут хранится в таблице переменных процесса, это byte[], Date, double, long, string, при этом значения переменных записываются в одно из полей таблицы: BYTEARRAYVALUE_, DATEVALUE_, DOUBLEVALUE_, LONGVALUE_ и STRINGVALUE_, соответственно. (Для объектов, которые сохраняются с использованием Hibernate предусмотрены также поля LONGIDCLASS_ и STRINGIDCLASS_, для ID с типом Long или String, соответственно). Для преобразования объектов произвольных Java-классов к вышеуказанным типам данных в jBPM реализованы следующие стандартные конвертеры: BooleanToStringConverter BytesToByteArrayConverter ByteToLongConverter CharacterToStringConverter DateToLongConverter DoubleToStringConverter FloatToDoubleConverter FloatToStringConverter IntegerToLongConverter SerializableToByteArrayConverter ShortToLongConverter
Artezio LLC Address: 3G Gubkina Str., suite 504, Moscow, Russia, Phone: +7 (495) Fax: +7 (495) The art of technology Конфигурирование конвертеров в jBPM Конфигурация конвертеров в jBPM производится в конфигурационном файле jbpm.varmapping.xml следующим образом: где matcher содержит описание класса для проверки соответствия типа данных переменной типу данных, который преобразует конвертер, указанный в теге converter, а variable-instance содержит тип данных после преобразования (один из предопределенных в jBPM), который будет сохранятся в базу данных.
Artezio LLC Address: 3G Gubkina Str., suite 504, Moscow, Russia, Phone: +7 (495) Fax: +7 (495) The art of technology Необходимость в реализации дополнительных конвертеров, постановка задачи Как следует из рассмотренного выше списка конвертеров, в стандартной реализации jBPM существует возможность для конвертации только ограниченного числа стандартных типов Java. В случае если переменная представляет собой объект некоторого произвольного класса остается возможность для использования конвертера SerializableToByteArrayConverter для сохранения объекта в базе данных (для этого данный класс должен реализовывать интерфейс Serializable). Минус использования конвертера SerializableToByteArrayConverter заключается в том, что в базу данных сохраняются данные в бинарном виде, что вызовет проблемы в случае необходимости в дальнейшем производить поиск и обработку сохраненных данных. Таким образом возникает задача: реализовать дополнительные конвертеры jBPM для преобразования произвольных объектов Java к виду, удобному для их поиска по какому-либо фильтру и последующей обработки, с их интеграцией с фреймворком Seam.
Artezio LLC Address: 3G Gubkina Str., suite 504, Moscow, Russia, Phone: +7 (495) Fax: +7 (495) The art of technology Реализация аннотации для конвертера jBPM Дополнительные конвертеров jBPM будем реализовывать по аналогии с конвертерами JSF, когда для регистрации и использования конвертеров в Seam достаточно проставить аннотацию для класса, реализующего конвертер. Аннотация для конвертера jBPM реализована следующим @Install(dependencies = "org.jboss.seam.bpm.jbpm") JbpmConverter { String jbpmConverterId() default "0"; Class forClass() default void.class; Class toJbpmVariableClass() default VariableInstance.class; } Таким образом, для реализации конвертера jBPM необходимо и достаточно, чтобы для этого класса была указана данная аннотация, а также класс конвертера реализовывал интерфейс org.jbpm.context.exe.Converter.
Artezio LLC Address: 3G Gubkina Str., suite 504, Moscow, Russia, Phone: +7 (495) Fax: +7 (495) The art of technology Реализация конвертера jBPM Пример реализации конвертера jBPM, исходя из указанных на предыдущем слайде = "Z", forClass = Payload.class, toJbpmVariableClass = LongInstance.class) public class PayloadToLongConverter implements Converter {... public boolean supports(Object value) { if (value == null) { return true; } return (value.getClass() == Payload.class); } public Object convert(Object o) { return ((Payload) o).getId(); } public Object revert(Object o) { EntityManager entityManager = (EntityManager) Component.getInstance("entityManager"); return entityManager.find(Payload.class, (Long) o); }
Artezio LLC Address: 3G Gubkina Str., suite 504, Moscow, Russia, Phone: +7 (495) Fax: +7 (495) The art of technology Обработка аннотации и регистрация конвертера в jBPM во время инициализации приложения В предыдущем примере элементы аннотации конвертера имеют следующее значение: jbpmConverterId – id конвертера, с которым конвертер зарегистрирован в jBPM, forClass – класс, для которого должен применятся этот конвертер, toJbpmVariableClass – класс, в который производится преобразование (является стандартным классом jBPM и представляет собой один из типов данных, который сохраняется в таблицу JBPM_VARIABLEINSTANCE базы данных jBPM). Для использования конвертера необходимо зарегистрировать его в jBPM, для этого используется обработка стандартного события Seam - org.jboss.seam.postInitialization, которое генерируется при старте приложения, после инициализации всех компонентов Seam. (Обработка компонентов Seam c аннотацией и реализация регистрации конвертеров jBPM находится в классе JbpmConvertersLoader).
Artezio LLC Address: 3G Gubkina Str., suite 504, Moscow, Russia, Phone: +7 (495) Fax: +7 (495) The art of technology Реализация класса JbpmConvertersLoader Получение имен всех компонентов Seam в @Install(dependencies = "org.jboss.seam.bpm.jbpm") public class JbpmConvertersLoader public void registerJbpmConverters() { if (Contexts.isApplicationContextActive()) { String[] names = Contexts.getApplicationContext().getNames(); if (names != null) { for (String name : names) { processComponent(name); } … }
Artezio LLC Address: 3G Gubkina Str., suite 504, Moscow, Russia, Phone: +7 (495) Fax: +7 (495) The art of technology Реализация класса JbpmConvertersLoader (часть 2) Определение среди всех компонентов Seam конвертеров jBPM и @Install(dependencies = "org.jboss.seam.bpm.jbpm") public class JbpmConvertersLoader {... private void processComponent(String name) { Object obj = Contexts.getApplicationContext().get(name); if (obj instanceof Component && ((Component) obj).beanClassHasAnnotation(JbpmConverter.class)) { Class beanClass = ((Component) obj).getBeanClass(); JbpmConverter annotation = beanClass.getAnnotation(JbpmConverter.class); registerConverter(beanClass, annotation); } … }
Artezio LLC Address: 3G Gubkina Str., suite 504, Moscow, Russia, Phone: +7 (495) Fax: +7 (495) The art of technology Реализация класса JbpmConvertersLoader (часть 3) Регистрация @Install(dependencies = "org.jboss.seam.bpm.jbpm") public class JbpmConvertersLoader {... private void registerConverter(Class beanClass, JbpmConverter annotation) { Object converter = Component.getInstance(beanClass); String id = annotation.jbpmConverterId(); Class initialClass = annotation.forClass(); Class variableClass = annotation.toJbpmVariableClass(); if (converter instanceof Converter) { addConverter(id, (Converter) converter); addJbpmType(initialClass, (Converter) converter, variableClass); } else { new IllegalArgumentException( "jBPM Converter "+ beanClass.getName() + " must implements org.jbpm.context.exe.Converter interface"); } … }
Artezio LLC Address: 3G Gubkina Str., suite 504, Moscow, Russia, Phone: +7 (495) Fax: +7 (495) The art of technology Реализация класса JbpmConvertersLoader (часть 4) Добавление конвертера jBPM в коллекции конвертеров (необходимо для обратной конвертации из объекта VariableInstance в @Install(dependencies = "org.jboss.seam.bpm.jbpm") public class JbpmConvertersLoader {... private void addConverter(String id, Converter jbpmConverter) { Map convertersByClassNames = ConvertersStorage.getConvertersByClassNames(); Map convertersByDatabaseId = ConvertersStorage.getConvertersByDatabaseId(); Map convertersIds = ConvertersStorage.getConvertersIds(); if (id == null || id.length() != 1) { throw new JbpmException("converter-ids must be of length 1 (to be stored in a char)"); } if (convertersByDatabaseId.containsKey(id)) { throw new JbpmException("duplicate converter id : '" + id + "'"); } convertersByClassNames.put(jbpmConverter.getClass().getName(),jbpmConverter); convertersByDatabaseId.put(id, jbpmConverter); convertersIds.put(jbpmConverter, id); } … }
Artezio LLC Address: 3G Gubkina Str., suite 504, Moscow, Russia, Phone: +7 (495) Fax: +7 (495) The art of technology Реализация класса JbpmConvertersLoader (часть 5) Добавление типа jBPM (необходимо для прямой конвертации из исходного объекта в @Install(dependencies = "org.jboss.seam.bpm.jbpm") public class JbpmConvertersLoader private void addJbpmType(Class initialClass, Converter jbpmConverter, Class variableClass) { JbpmType newType = new JbpmType(new ClassNameMatcher(initialClass), jbpmConverter, variableClass); List types = (List ) JbpmType.getJbpmTypes(); if (types != null) { types.add(0, newType); } … }
Artezio LLC Address: 3G Gubkina Str., suite 504, Moscow, Russia, Phone: +7 (495) Fax: +7 (495) The art of technology Реализация класса ClassNameMatcher Реализация класса ClassNameMatcher (необходим для определения соответствующего конвертера для определенного класса):... import org.jbpm.context.exe.JbpmTypeMatcher; public class ClassNameMatcher implements JbpmTypeMatcher { private static final long serialVersionUID = L; private Class classType; public ClassNameMatcher(Class classType) { this.classType = classType; } public boolean matches(Object value) { return classType == value.getClass(); }
Artezio LLC Address: 3G Gubkina Str., suite 504, Moscow, Russia, Phone: +7 (495) Fax: +7 (495) The art of technology Реализация класса ConvertersStorage Реализация класса ConvertersStorage (необходим для доступа к коллекциям конвертеров jBPM): package org.jbpm.db.hibernate; … public class ConvertersStorage public static Map getConvertersByClassNames() { return Converters.getConverterMaps()[Converters.CONVERTERS_BY_CLASS_NAMES]; public static Map getConvertersByDatabaseId() { return Converters.getConverterMaps()[Converters.CONVERTERS_BY_DATABASE_ID]; public static Map getConvertersIds() { return Converters.getConverterMaps()[Converters.CONVERTERS_IDS]; }
Artezio LLC Address: 3G Gubkina Str., suite 504, Moscow, Russia, Phone: +7 (495) Fax: +7 (495) The art of technology Результат реализации и возможности использования дополнительных конвертеров jBPM Таким образом, была рассмотрена задача добавления дополнительных конвертеров jBPM, интегрированных с фреймворком Seam. В результате проведенной работы, была реализована возможность добавления конвертеров jBPM для произвольных объектов Java c помощью аннотаций. Данная дополнительная функциональность позволяет избежать проблем, связанных с необходимостью обработки сериализованных объектов, хранящихся в базе данных в бинарном виде, позволяя сохранять объекты в базу данных jBPM в произвольном, удобном для обработки виде. При этом, реализация новых конвертеров jBPM, реализованным способом, практически не требует дополнительных трудозатрат.
Artezio LLC Address: 3G Gubkina Str., suite 504, Moscow, Russia, Phone: +7 (495) Fax: +7 (495) The art of technology Ваши вопросы