Friday, November 6, 2009

Создание Java web приложения для 1С Предприятия

В даном посте я опишу свой опыт связаный с созданием веб морды для приложения основанного на 1С Предприятии 8.1.
От стандартного 1С Веб расширения было решено отказаться в виду его не гибкости и низкой производительности.

Для реализации задуманного было выбрано такие продукты и технологии:
  • Apache Tomcat
  • ICEFaces/MyFaces/Facelets
  • JasperReports
  • Spring IoC
  • Jawin
  • JDBC
  • Apache Ant
Кратко об архитектуре
Основная бизнес-логика реализована на стороне 1С. Веб приложение создается для удаленного манипулирования 1С сушьностями через Internet/intranet в удобном для пользователя виде.

Приложение будет крутиться на Tomcat(Servlet/JSP контейнер).
Для создания пользовательского интерфейса используем JSF технологию, а именно
MyFaces(имплементация от Apache) плюс IceFaces библиотеку JSF компонентов дополняя все это технологией Facelets.

JasperReports используем для создания отчетов в разных форматах(txt/rtf/xls/pdf..) на основе шаблона. Для построения шаблонов используем IReport.

Spring IoC используем для инициализации JSF бинов.

Собираем проект Ant-ом.

На персистенс слое используем два варианта доступа к 1С:
  1. через Jawin(COM-соединение)
  2. через JDBC(прямые запросы)
От варианта создания веб-сервисов на 1С отказался, в виду дополнительной затраты времени в сравнении с COM, где мы имеем доступ практически ко всем объектам конфигурации 1С.
Наше 1С Предприятие работает на MSSQL базе, потому используем JTDS драйвер. Прямые запросы используем только для чтения данных и только в тех случаях когда использование COM является невозможным или не целесообразным(к примеру орзанизация выборки для пейджинга из большого количества данных по условиям поиска с сортировкой)
При роботе с COM надо быть очень внимательным во избежание утечек памяти, об этом позже.
Все низкоуровневые манипуляции с данными(COM и прямые запросы) изолируем на уровне наших DAO(Data access object). Выделяем интерфейсы наших DAO и DAOFactory и создаем их имплементацию для 1С. Эту абстракцию делаем на случай если бизнес логика будет переходить с 1С на другую платформу. В объектах нашей доменной модели используем интерфейсы абстракции а не имплементации.
Теперь тонкости имплементации
Остановимся на связке Java-1C так как этот вопрос мение всего описан в Интернете. Далее приведу информацию по подходу использованого мной для реализации даной задачи.

Инициализация DAOFactory бина в спринговом applicationContect.xml:

<bean id="daoFactory" class="lmr.persistance.ones.DAOFactory"
scope="session">
<constructor-arg type="java.lang.String" value="${ones.hostname}" />
<constructor-arg type="java.lang.String" value="${ones.database}" />
<constructor-arg type="javax.sql.DataSource" ref="dataSource" />
</bean>

Скоуп бина - сессия. Инициализируется параметрами соединения к 1С серверу и jdbc датасорсом.
В интерфейс IDAOFactory добавляем методы

void connect(String username, String password);
void disconnect();
выполняющие в случае имплементации для 1С создание и завершение COM соединения.


public class DAOFactory implements IDAOFactory {

private DispatchPtr app;
private DispatchPtr oneSCOMConnection;
private String oneSHostname;
private String oneSDatabase;
private DataSource dataSource;

public DAOFactory(String oneSHostname, String oneSDatabase,
DataSource dataSource) {
this.oneSHostname = oneSHostname;
this.oneSDatabase = oneSDatabase;

this.dataSource = dataSource;
}

public void connect(String username, String password) throws ComException {
app = new Dispatch("V81.COMConnector");
oneSCOMConnection = (DispatchPtr) app.invoke("Connect", "Srvr=\""
+ oneSHostname + "\";Ref=\"" + oneSDatabase + "\";Usr=\""
+ username + "\";Pwd=\"" + password + "\"");

}

public void disconnect() {
close(app);
close(oneSCOMConnection);
}
...
Непосредственно методы создания DAO в DAOFactory инстанцируют нужные объекты и передают ссылки на COM-соединение и датасорс на прямые соединения

...
public ISomeDAO getSomeDAO() {

return new SomeDAO(oneSCOMConnection, dataSource);
}
...
Пример манипуляции 1С объектами через COM соединение с помошью Jawin:


//1С код
ОбъектОрганизации = Справочники.Организации.НайтиПоКоду(1).ПолучитьОбъект();
ОбъектОрганизации.Наименование = "1С Предприятие";
ОбъектОрганизации.Записать();

//выполнение 1С кода на Java
Dispatch catalogs = null;
Dispatch organisations = null;
Dispatch organisation = null;
Dispatch organisationObject = null;
try {
catalogs = (DispatchPtr) oneSCOMConnection.get("Справочники");
organisations = (DispatchPtr) catalogs.get("Организации");
organisation = (DispatchPtr) organisations.invoke("НайтиПоКоду", 1);
organisationObject = (DispatchPtr) organisation.invoke("ПолучитьОбъект");
organisationObject.put("Наименование", "1С Предприятие");
organisationObject.invoke("Записать");
} catch (ComException e) {
throw new RuntimeException(e);
} finally {
close(catalogs);
close(organisations);
close(organisation);
close(organisationObject);
}
Обратим внимание на finally блок. Здесь закрываем все использованые DispatchPtr объекты. Метод close() статический, заимпорченый из класса утилиты. Код метода приведен ниже:

public static void close(DispatchPtr dispatch) {
if (dispatch != null)
dispatch.close();
}
Конструкции на подобе ниже приведенной нельзя использовать так как небудет возможности релизнуть объект и Virtual Memory Size приложения будет наростать(утечка). В добавок к этому не будет возможности закрыть COM соединение.

organisations = (DispatchPtr) ((DispatchPtr) oneSCOMConnection.get("Справочники")).catalogs.get("Организации");
Было испробовано обвертку для DispatchPtr с его релизаньем в методе финализации, но в процессе тестирования было обнаружено слетание JVM в процессе финализации объектов обвертки с нейтив ошибкой. От ее использования отказался.

Работа через прямые запросы
С 1С Предприятием работать через прямые запросы рекомендую только в режиме чтения.
Если заглянуть в структуру MSSQL базы данных то мы обнаружим большое количество таблиц с не человекопонятными именами таблиц/полей.
Для определения соответствия объектам конфикурации таблиц и полей в базе существуют написаные на 1С приложения, которые можно найти в интернете(к примеру ПосмотрМетаданных81.epf). Однако завязываться на имена таблиц/полей не стоит, так как при создании новой конфигурации на основе существующей, соответствие для оригинальной конфигурации будет не действительно для новой.
В даной ситуации приемлимым является написание приложения которое будет подчитывать метаданные 1С конфигурации на этапе деплоя приложения и создавать файлы маппинга полей/таблиц базы сушьностям конфигурации. Следующий кусок кода илюстрирует процесс создания файлов маппинга


DispatchPtr app = null;
DispatchPtr oneSCOMConnection = null;
DispatchPtr oneSTables = null;
try {
app = new DispatchPtr("V81.COMConnector");
oneSCOMConnection = (DispatchPtr) app.invoke("Connect", "Srvr=\""
+ hostname + "\";Ref=\"" + database + "\";Usr=\""
+ username + "\";Pwd=\"" + password + "\"");
oneSTables = (DispatchPtr) oneSCOMConnection.invoke(
"ПолучитьСтруктуруХраненияБазыДанных", true, true);
Properties tables = new Properties();
Properties fields = new Properties();

for (int i = 0; i < (Integer) oneSTables.invoke("Количество"); i++) {
DispatchPtr oneSTable = null;
DispatchPtr oneSFields = null;
try {
oneSTable = (DispatchPtr) oneSTables.invoke("Получить", i);
String tableName = (String) oneSTable
.get("ИмяТаблицыХранения");
String oneSTableName = (String) oneSTable.get("ИмяТаблицы");
if (!oneSTableName.isEmpty()) {
tables.setProperty(oneSTableName, tableName);
}
oneSFields = (DispatchPtr) oneSTable.get("Поля");
for (int j = 0; j < (Integer) oneSFields
.invoke("Количество"); j++) {
DispatchPtr oneSField = null;
try {
oneSField = (DispatchPtr) oneSFields.invoke(
"Получить", j);
String fieldName = (String) oneSField
.get("ИмяПоляХранения");
String oneSFieldName = (String) oneSField
.get("ИмяПоля");
if (!fieldName.isEmpty()) {
fields.setProperty(oneSTableName + "."
+ oneSFieldName, fieldName);
}
} catch (ComException e) {
throw new RuntimeException(e);
} finally {
oneSField.close();
}
}
} catch (ComException e) {
throw new RuntimeException(e);
} finally {
if (oneSFields != null)
oneSFields.close();
if (oneSTable != null)
oneSTable.close();
}
}
storeUtf8Properties(tables, path + "/" + PACKAGE_PATH
+ "/tables.properties");
storeUtf8Properties(fields, path + "/" + PACKAGE_PATH
+ "/fields.properties");
} catch (ComException e) {
throw new RuntimeException(e);
} finally {
if (oneSTables != null)
oneSTables.close();
if (oneSCOMConnection != null)
oneSTables.close();
if (app != null)
oneSTables.close();
}


Пример файла маппинга таблиц создаваемого вышеописаным кодом:

Перечисление.ВидыРасчетовПоДоговорам=_Enum315
Перечисление.ВидыОперацийППИсходящее=_Enum292
РегистрСведений.ОтражениеВзносовФОТВРеглУчете=_InfoReg8273
Перечисление.СтатусыКонтрагентов=_Enum377
Документ.НачислениеЭлектрПоДому=_Document9636
РегистрСведений.ГрафикиАмортизацииОСБухгалтерскийУчет=_InfoReg8022
Перечисление.СпособыОценки=_Enum366

Далие внедряем создание маппинга в наш ant скрипт в процесс деплоя.
В приложении строим наши прямые запросы через этот маппинг используюя уже имена сушьностей 1С вместо не человекопонятных имен сушьностей MSSQL базы.

Подитожим
Целью даного поста является осветление одного из способов взамодействия 1С и Java и не претендует на звание единственного или лучшего варианта. Также не стану утверждать что выбор Jawin, как COM бриджа является лучшим.
Надесь все выше написанное станет полезным и сэкономит время читатилю.

6 comments:

  1. Отличная статья.

    ReplyDelete
  2. А вот API на JAVA к 7-ке
    http://j1c.ru

    ReplyDelete
  3. Java API для 1С Enterprise 8 (8.0, 8.1, 8.2)
    https://bitbucket.org/IgorKonovalov/octitbit/wiki/Home

    ReplyDelete
  4. storeUtf8Properties - ??? Откуда ???

    ReplyDelete
  5. Как мистер Бенджамин Ли предоставляет мне кредит !!!

    Привет всем, я - Леа Пейдж Маттео из Цюриха, Швейцария, и хочу использовать эту среду, чтобы выразить благодарность службе господина Бенджамина за выполнение своего обещания, предоставив мне кредит, я застрял в финансовом положении и должен был рефинансировать и оплачивать свои счета а также начать бизнес. Я пытался получить кредиты от различных кредитных фирм, как частных, так и корпоративных, но так и не смог, и большинство банков отклонили мой запрос на кредит. Но, как сказал бы Бог, я был представлен подругой по имени Лиза Райс в эту финансовую службу и прошел должный процесс получения кредита от компании, к моему величайшему удивлению в течение 5 рабочих дней, как и моя подруга Лиза, я также был предоставил кредит в размере 216 000,00 долларов США. Поэтому я советую всем, кто желает получить кредит, «если вам необходимо связаться с какой-либо фирмой со ссылкой на получение кредита онлайн с низкой процентной ставкой в ​​1,9% и лучшими планами / графиком погашения, пожалуйста, свяжитесь с этой службой финансирования. Кроме того, он не знает, что я делаю это, но из-за радости во мне, я так счастлив и хочу, чтобы люди узнали больше об этой великой компании, которая действительно дает кредиты, я молюсь, чтобы БОГ благословил их больше, поскольку они вызывают улыбки на лицах людей. Вы можете связаться с ними по электронной почте на {lfdsloans@outlook.com} или по тексту через Whatsapp + 1-989 394 3740.

    ReplyDelete
  6. Merkur 15c Safety Razor - Barber Pole - Deccasino
    Merkur 15C casinosites.one Safety Razor - Merkur - 15C for Barber titanium metal trim Pole wooricasinos.info is the 바카라 사이트 perfect introduction to the Merkur Safety Razor. https://deccasino.com/review/merit-casino/

    ReplyDelete

Followers