Интеграция SAP NetWeaver Portal 7.3 и MS Exchange 2010/2013
Многие заказчики при внедрении корпоративного портала выдвигают в числе обязательных требований удобную интеграцию с существующей корпоративной информационной структурой. В состав продукта SAP NetWeaver Portal с самого начала входили инструменты интеграции рабочей среды сотрудничества (Collaboration) с MS Exchange. Однако, почти все поставляемые инструменты (Collaboration) не поддерживают протоколы продуктов Microsoft, начиная с версии MS Exchange 2010. В статье рассматривается реализация интеграции SAP Portal с новыми версиями MS Exchange, приводится рабочий код.
История вопроса
Одним из требований, возникающих при внедрении корпоративных порталов, является их тесная интеграция с уже развернутой структурой организации – электронной почтой, персональными средствами планирования и управления рабочим графиком. В первую очередь это наиболее распространенные системы – Microsoft Exchange и Lotus Notes. Пользовательские функции взаимодействия с персональной почтой и календарем поставляются в составе рабочей среды сотрудничества (пакет Collaboration) в виде набора готовых iView и страниц. Для обеспечения универсальности разработанных визуальных интерфейсов в архитектуру NetWeaver был добавлен дополнительный транспортный слой. То есть взаимодействие с конечными интерфейсами корпоративных почтовых систем происходит через выделенный программный объект, преобразующий вызовы набора стандартных методов библиотеки SAP NetWeaver в специфичные протоколы и стандарты, поддерживаемые целевыми системами.
Для интеграции SAP Portal с MS Exchange существовали следующие возможности:
- Взаимодействие с интерфейсом Outlook Web Access (OWA)
- Использование CDO (Collabarative Data Objects);
- Использование протокола WebDAV.
Первый способ исторически появился еще в SAP Portal 6.0 и не использует каких-либо транспортных слоев. Однако его использование сопряжено с рядом неудобств (например, сложностями сквозной авторизации) и фактически является внедрением интерфейса OWA внутрь портала. Для более полной интеграции и контроля были разработаны новые средства, указанные выше – интеграция посредством CDO и WebDAV, реализованная через транспортный слой. Наиболее часто из двух указанных применялась интеграция через WebDAV, поскольку она требовала меньшего количества действий по настройке и конфигурированию на стороне Exchange.
Однако, начиная с версии MS Exchange 2010, компания Microsoft объявила об отказе от поддержки этих средств коммуникации, введя единый интерфейсный стандарт на основе веб-сервисов – EWS (Exchange Web Services). В результате интеграция стандартными средствами SAP Portal с версиями MS Exchange 2010/2013 стала невозможна. Наша проектная команда столкнулась именно с такой ситуацией, потребовавшей разработки собственного интерфейса интеграции с использованием EWS. Далее будут описаны основные шаги по реализации собственного транспортного объекта.
Реализация объекта транспорта
Имеющиеся способы подключения портала (CDO, WebDAV) использовали механизм транспорта. Это определило техническую архитектуру – объект транспорта и формат файла – .sda. Дополнительное внутреннее требование – использование DC и NWDI.
Для работы с Exchange используется код из внешнего проекта «EWS JAVA API» (http://archive.msdn.microsoft.com/ewsjavaapi). На момент написания статьи проект имел версию 1.2. Для работы этого проекта необходимы дополнительные файлы, о чем прямо говорится в файле «Compiling the EWS Java API.RTF». Мы использовали commons-codec-1.8.jar, commons-httpclient-3.1.jar, commons-logging-1.1.3.jar, jcifs-1.3.17.jar, поместив их в DC типа «library». Основной проект использовал public part типа «compilation» и «assembly». В итоге эти файлы вошли в состав конечного sda-пакета.
Описание вспомогательного сервиса
Для работы транспорта нужно, чтобы .jar с ним был постоянно загружен в память и таким образом являлся доступным загрузчику классов. Это обеспечивается запуском вспомогательного портального сервиса. Вот как выглядит его описание в portalapp.xml:
<service name="ExchangeTransportService">
<service-config>
<property name="className" value="ru.energodata.ExchangeTransportService"/>
<property name="classNameFactory" value=""/>
<property name="classNameManager" value=""/>
<property name="poolFactory" value="0"/>
<property name="startup" value="true"/>
</service-config>
<service-profile>
<property name="generic_service_key" value="ru.energodata.ExchangeTransportService"/>
<property name="generic_classloader_registration" value="yes"/>
<property name="generic_so_registration" value="no"/>
<property name="proxy" value=""/>
</service-profile>
</service>
Ключевыми являются следующие моменты:
Сервис стартует автоматически. Ручной запуск не работает. Это неудобно для отладки, но вызвано архитектурой установки сервиса, о чем будет идти речь ниже, в описании файла конфигурации.
Свойство «generic_classloader_registration» установлено в «yes», что позволяет избежать написания кода регистрации, воспользовавшись уже готовым функционалом. Подробнее об этом также далее по тексту.
Сам транспорт – это класс, созданный соответствующим мастером. Вот как выглядит интерфейсная часть:
package ru.energodata;
import com.sapportals.portal.prt.service.IService;
public interface IExchangeTransportService extends IService
{
public static final String KEY = "ru.energodata~exchtransp.ExchangeTransportService";
public String getProxy();
}
Вот так выглядит реализация:
package ru.energodata;
import com.sap.ip.collaboration.core.api.fwk.portal.GenericService;
import com.sapportals.portal.prt.service.IServiceContext;
public class ExchangeTransportService extends GenericService implements IExchangeTransportService
{
// сервис нужен для регистрации ClassLoader-а
// регистрация происходит в super, т.к. включен параметр
//"generic_classloader_registration"
private IServiceContext _context;
@Override
public String getProxy()
{
return _context.getServiceProfile().getProperty("proxy");
}
@Override
public void init(IServiceContext ctxt)
{
_context = ctxt;
super.init(ctxt);
}
}
Важный момент – класс наследуется от GenericService. Именно в нем находится код регистрации, который вкупе со свойством «generic_classloader_registration» позволяет не писать код этот код у себя.
Свойство «proxy» и возвращающий его метод «getProxy» необходим, если выход на веб-сервис Exchange идет через прокси-сервер. В нашем случае подключение прямое, поэтому свойство пустое.
- Описание программного интерфейса транспорта
Портал при запуске создает экземпляр класса средствами reflection. Транспорт – класс, реализующий ряд интерфейсов и унаследованный от класса «AbstractTransport»:
package ru.energodata;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import com.sap.ip.collaboration.gw.api.enums.GroupwareItemType;
import com.sap.ip.collaboration.gw.api.enums.TransportType;
import com.sap.ip.collaboration.gw.api.framework.groupware.AbstractTransport;
import com.sap.ip.collaboration.gw.api.framework.groupware.GroupwareException;
import com.sap.ip.collaboration.gw.api.framework.groupware.IAttachment;
import com.sap.ip.collaboration.gw.api.framework.groupware.ICalendarItem;
import com.sap.ip.collaboration.gw.api.framework.groupware.ICalendarReadTransport;
import com.sap.ip.collaboration.gw.api.framework.groupware.ICalendarSendTransport;
import com.sap.ip.collaboration.gw.api.framework.groupware.IDateRange;
import com.sap.ip.collaboration.gw.api.framework.groupware.IGWResourceManager;
import com.sap.ip.collaboration.gw.api.framework.groupware.IGroupwareCredentials;
import com.sap.ip.collaboration.gw.api.framework.groupware.IGroupwareItem;
import com.sap.ip.collaboration.gw.api.framework.groupware.ISupportability;
@SuppressWarnings("unchecked")
public class ExchangeCalendarTransport extends AbstractTransport implements ICalendarReadTransport, ICalendarSendTransport, ISupportability
{
private static String transportId = "ru.energodata.ExchangeCalendarTransport";
private IGWResourceManager _manager;
private List _configurations;
private String _alias = "TransportErrorSeeLog";
@Override
public String getServerAlias()
{
return _alias;
}
@Override
public GroupwareItemType getSupportedItemTypeEnum()
{
return GroupwareItemType.CALENDAR;
}
@Override
public String getTransportDescription(Locale locale)
{
return "EWS Microsoft Exchange";
}
@Override
public String getTransportName()
{
return transportId;
}
@Override
public TransportType getTransportTypeEnum()
{
return TransportType.BOTH;
}
@Override
public void initialize(IGWResourceManager manager, List configurations) throws GroupwareException
{
_manager = manager;
_configurations = configurations;
_alias = ExchangeCore.getAlias(configurations);
}
@Override
public void terminate() throws GroupwareException
{
}
@Override
public Map getAvailabilityInfo(IDateRange range, int timeInterval, List addresses, IGroupwareCredentials credential) throws GroupwareException
{
ExchangeCore core = null;
try
{
HashMap res = new HashMap();
for (int i = 0; i < addresses.size(); i++)
{
String address = (String)addresses.get(i);
core = new ExchangeCore(_manager, _configurations, credential, address);
res.put(address, core.getAvailabilityInfo(range, timeInterval));
}
return res;
}
catch (Exception e)
{
throw Exceptions.processing(e, core);
}
}
@Override
public IAttachment getAttachmentContent(String itemId, String attachmentId, IGroupwareCredentials credential) throws GroupwareException
{
throw new UnsupportedOperationException("Method getAttachmentContent() not yet implemented.");
}
@Override
public String getContent(String arg0, IGroupwareCredentials arg1) throws GroupwareException
{
throw new UnsupportedOperationException("Method getContent() not yet implemented.");
}
@Override
public IGroupwareItem getItem(String itemId, IGroupwareCredentials credential) throws GroupwareException
{
ExchangeCore core = null;
try
{
core = new ExchangeCore(_manager, _configurations, credential);
IGroupwareItem res = core.getItem(itemId);
if (res != null)
return res;
throw new Exception("getItem вернул null");
}
catch (Exception e)
{
throw Exceptions.processing(e, core);
}
}
@Override
public List getItemList(IDateRange range, IGroupwareCredentials credential) throws GroupwareException
{
ExchangeCore core = null;
try
{
core = new ExchangeCore(_manager, _configurations, credential);
return core.getItemList(range);
}
catch (Exception e)
{
throw Exceptions.processing(e, core);
}
}
@Override
public List getItemList(IDateRange range, Properties searchCriteria, IGroupwareCredentials credential, int nCount) throws GroupwareException
{
return new ArrayList();
}
@Override
public GroupwareItemType getSupportedItemType()
{
return getSupportedItemTypeEnum();
}
@Override
public TransportType getTransportType()
{
return getTransportTypeEnum();
}
@Override
public void remove(String id, IGroupwareCredentials credential, boolean isSeries) throws GroupwareException
{
}
@Override
public void remove(String id, IGroupwareCredentials credential) throws GroupwareException
{
}
@Override
public String save(IGroupwareItem item, IGroupwareCredentials credential) throws GroupwareException
{
return "";
}
@Override
public String send(IGroupwareItem item, IGroupwareCredentials credential) throws GroupwareException
{
ExchangeCore core = null;
try
{
core = new ExchangeCore(_manager, _configurations, credential);
if (item instanceof ICalendarItem)
core.send ((ICalendarItem) item);
else
throw new Exception("item не ICalendarItem");
}
catch (Exception e)
Если хотите прочитать статью полностью и оставить свои комментарии присоединяйтесь к sapland
ЗарегистрироватьсяУ вас уже есть учетная запись?
Войти