При разработке проектов с использованием OAF и JDeveloper возникла необходимость частого переноса (или развертывания — deploy) приложений с одного экземпляра системы на другой.
Операция переноса состоит из пяти шагов:
- Компиляция java классов
- Перенос java классов на сервер приложений (в каталог $JAVA_TOP)
- Перенос xml файлов Entity Objects, View Objects, Application Modules ($JAVA_TOP)
- Импорт xml файлов Pages, Regions с помощью утилиты import
- Импорт xliff файлов
Проделав эту операцию пару раз вручную, решил все это дело автоматизировать.
Вся автоматизация построена на известном продукте Apache Ant.
Состоит из четырех файлов
- <project>.bat
- <project>.xml
- build.xml
- <project>.properties
, где <project> имя Вашего проекта.
В общих словах:
<project>.bat запускает ant, <project>.xml содержит то, что будет обрабатываться в build.xml (java файлы, xml файлы, xliff файлы, sql-команды)
build.xml содержит операции которые будут выполняться. Состоит из target’ов, которые производят операции над файлами из файла <project>.xml
<project>.properties содержит параметры для определенного экземпляра системы такие как пути, адреса хостов, имена пользователей, пароли
Детально:
Содержание файла .bat
-----Начало------------ ant -f build.xml -Dinstance=testinstance -Dproject=<project> -----Конец-------------
Как видите запускается ant с build.xml файлом, параметром instance который равен например, «testinstance» и project который равен <project> в именах файлов. Параметр instance необходим для импорта в build.xml файла со значениями параметров данного экземпляра системы (файл под номером 4). Параметр project необходим так же для импорта в build.xml параметров данного проекта. Далее идет содержание файлов с пояснениями. Содержимое фалов относится к небольшому реальному проекту.
Содержание файла <project>.xml
---------------------------------------------------------Начало-------------------------------------------------------
<project name="concurrent" default="all">
<property name="project.name" value = "concurrent"/>
<property name="root.package" value="mycompany/oracle/apps/fnd/concurrent"/>
<!--SQL-команды для выполнения на сервере БД.-->
<property name="sql.1" value="insert into APPLSYS.FND_MENU_ENTRIES_TL (LANGUAGE, MENU_ID, ENTRY_SEQUENCE, LAST_UPDATE_DATE, LAST_UPDATED_BY, LAST_UPDATE_LOGIN, CREATION_DATE, CREATED_BY, PROMPT, DESCRIPTION, SOURCE_LANG)
values ('US', 67852, 7, to_date('09-04-2012 18:40:49', 'dd-mm-yyyy hh24:mi:ss'), 1090, 78995104, to_date('09-04-2012 18:40:49', 'dd-mm-yyyy hh24:mi:ss'), 1090, 'КЗ: Параллельные программы', 'Работа с параллельными программами', 'RU')"/>
<property name="sql.2" value="insert into APPLSYS.FND_MENU_ENTRIES_TL (LANGUAGE, MENU_ID, ENTRY_SEQUENCE, LAST_UPDATE_DATE, LAST_UPDATED_BY, LAST_UPDATE_LOGIN, CREATION_DATE, CREATED_BY, PROMPT, DESCRIPTION, SOURCE_LANG)
values ('RU', 67852, 7, to_date('09-04-2012 18:40:49', 'dd-mm-yyyy hh24:mi:ss'), 1090, 78995104, to_date('09-04-2012 18:40:49', 'dd-mm-yyyy hh24:mi:ss'), 1090, 'КЗ: Параллельные программы', 'Работа с параллельными программами', 'RU')"/>
<property name="sql.3" value="insert into APPLSYS.FND_MENU_ENTRIES (MENU_ID, ENTRY_SEQUENCE, LAST_UPDATE_DATE, LAST_UPDATED_BY, LAST_UPDATE_LOGIN, CREATION_DATE, CREATED_BY, SUB_MENU_ID, FUNCTION_ID, GRANT_FLAG)
values (67852, 7, to_date('09-04-2012 18:40:49', 'dd-mm-yyyy hh24:mi:ss'), 1090, 78995104, to_date('09-04-2012 18:40:49', 'dd-mm-yyyy hh24:mi:ss'), 1090, null, 40492, 'Y')"/>
<property name="sql.4" value="insert into APPLSYS.FND_FORM_FUNCTIONS (FUNCTION_ID, FUNCTION_NAME, LAST_UPDATE_DATE, CREATION_DATE, LAST_UPDATED_BY, CREATED_BY, LAST_UPDATE_LOGIN, TYPE, WEB_SECURED, WEB_ENCRYPT_PARAMETERS, MAINTENANCE_MODE_SUPPORT, WEB_HTML_CALL, CONTEXT_DEPENDENCE)
values (40492, 'KZT_FND_CONCURRENTS', to_date('09.04.2012 18:39', 'dd.mm.rrrr hh24:mi'), to_date('09.04.2012 18:39', 'dd.mm.rrrr hh24:mi'), 1090, 1090, 78995104, 'JSP', 'N', 'N', 'NONE', 'OA.jsp?page=/mycompany/oracle/apps/fnd/concurrent/webui/ConcurrentPG', 'RESP')"/>
<property name="sql.5" value="insert into APPLSYS.FND_PROFILE_OPTION_VALUES (APPLICATION_ID, PROFILE_OPTION_ID, LEVEL_ID, LEVEL_VALUE, LAST_UPDATE_DATE, LAST_UPDATED_BY, CREATION_DATE, CREATED_BY, LAST_UPDATE_LOGIN, PROFILE_OPTION_VALUE, LEVEL_VALUE_APPLICATION_ID, LEVEL_VALUE2)
values (1, 10941, 10001, 0, to_date('11-04-2012 10:55:53', 'dd-mm-yyyy hh24:mi:ss'), 1090, to_date('11-04-2012 10:25:19', 'dd-mm-yyyy hh24:mi:ss'), 1090, 78996469, '/appclone/appclone/CLONE_inst/apps/CLONE_appclone/logs/appl/conc/out', null, null)"/>
<property name="sql.6" value="insert into APPLSYS.FND_PROFILE_OPTIONS (APPLICATION_ID, PROFILE_OPTION_ID, PROFILE_OPTION_NAME, LAST_UPDATE_DATE, LAST_UPDATED_BY, CREATION_DATE, CREATED_BY, LAST_UPDATE_LOGIN, WRITE_ALLOWED_FLAG, READ_ALLOWED_FLAG, USER_CHANGEABLE_FLAG, USER_VISIBLE_FLAG, SITE_ENABLED_FLAG, SITE_UPDATE_ALLOWED_FLAG, APP_ENABLED_FLAG, APP_UPDATE_ALLOWED_FLAG, RESP_ENABLED_FLAG, RESP_UPDATE_ALLOWED_FLAG, USER_ENABLED_FLAG, USER_UPDATE_ALLOWED_FLAG, START_DATE_ACTIVE, SQL_VALIDATION, END_DATE_ACTIVE, HIERARCHY_TYPE, SERVER_ENABLED_FLAG, ORG_ENABLED_FLAG, SERVER_UPDATE_ALLOWED_FLAG, ORG_UPDATE_ALLOWED_FLAG, SERVERRESP_ENABLED_FLAG, SERVERRESP_UPDATE_ALLOWED_FLAG)
values (1, 10941, 'KZT_XML_TMP_PATH', to_date('11-04-2012 10:24:00', 'dd-mm-yyyy hh24:mi:ss'), 1090, to_date('11-04-2012 10:24:00', 'dd-mm-yyyy hh24:mi:ss'), 1090, 78996444, 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', to_date('11-04-2012', 'dd-mm-yyyy'), null, null, 'SECURITY', 'N', 'N', 'N', 'N', 'N', 'N')"/>
<property name="sql.7" value="insert into APPLSYS.FND_NEW_MESSAGES (APPLICATION_ID, LANGUAGE_CODE, MESSAGE_NUMBER, MESSAGE_NAME, MESSAGE_TEXT, CREATION_DATE, CREATED_BY, LAST_UPDATE_DATE, LAST_UPDATED_BY, LAST_UPDATE_LOGIN, DESCRIPTION, TYPE, MAX_LENGTH, CATEGORY, SEVERITY, FND_LOG_SEVERITY)
values (1, 'RU', null, 'KZT_XML_PATH_NOT_FOUND', 'Значение профиля "КЗ: Путь для выгрузки/загрузки временных файлов" не задано. Задайте значение', to_date('11-04-2012 10:37:30', 'dd-mm-yyyy hh24:mi:ss'), 1090, to_date('11-04-2012 10:37:30', 'dd-mm-yyyy hh24:mi:ss'), 1090, 78996444, null, '30_PCT_EXPANSION_PROMPT', null, null, null, null)"/>
<!--Java файлы проекта-->
<fileset id="java.files" dir="${src.dir}/${root.package}">
<include name="server/ConcurrentAMImpl.java"/>
<include name="server/ConcurrentVOImpl.java"/>
<include name="server/ConcurrentVORowImpl.java"/>
<include name="server/XmlFileVOImpl.java"/>
<include name="server/XmlFileVORowImpl.java"/>
<include name="server/UploadFileVOImpl.java"/>
<include name="server/UploadFileVORowImpl.java"/>
<include name="server/CommonUtils.java"/>
<include name="server/DataTable.java"/>
<include name="server/ReportData.java"/>
<include name="server/XMLReportData.java"/>
<include name="server/DOMSerializer.java"/>
<include name="webui/ConcurrentCO.java"/>
</fileset>
<!--Class файлы проекта-->
<fileset id="class.files" dir="${classes.dir}">
<include name="${root.package}/server/ConcurrentAMImpl.class"/>
<include name="${root.package}/server/ConcurrentVOImpl.class"/>
<include name="${root.package}/server/ConcurrentVORowImpl.class"/>
<include name="${root.package}/server/XmlFileVOImpl.class"/>
<include name="${root.package}/server/XmlFileVORowImpl.class"/>
<include name="${root.package}/server/UploadFileVOImpl.class"/>
<include name="${root.package}/server/UploadFileVORowImpl.class"/>
<include name="${root.package}/server/CommonUtils.class"/>
<include name="${root.package}/server/DataTable.class"/>
<include name="${root.package}/server/ReportData.class"/>
<include name="${root.package}/server/XMLReportData.class"/>
<include name="${root.package}/server/ReportData$QueryTable.class"/>
<include name="${root.package}/server/DOMSerializer.class"/>
<include name="${root.package}/webui/ConcurrentCO.class"/>
</fileset>
<!--xml файлы проекта такие как (AM, VO, EO)-->
<fileset id="server.files" dir="${src.dir}">
<include name="${root.package}/server/ConcurrentAM.xml"/>
<include name="${root.package}/server/ConcurrentVO.xml"/>
<include name="${root.package}/server/XmlFileVO.xml"/>
<include name="${root.package}/server/UploadFileVO.xml"/>
</fileset>
<!--xml файлы проекта такие как (PG, RN)-->
<fileset id="webui.files" dir="${src.dir}">
<include name="${root.package}/webui/ConcurrentPG.xml"/>
</fileset>
<!--xliff файлы проекта-->
<fileset id="xliff.files" dir="${src.dir}">
<include name="${root.package}/webui/ConcurrentPG.xlf"/>
</fileset>
</project>
---------------------------------------------------------Конец---------------------------------------------------------
Как видите в <project>.xml нет никаких операций, только перечисление файлов.
Содержание файла build.xml
---------------------------------------------------------Начало---------------------------------------------------------
<project name="oaf.builder" default="all">
<taskdef resource="net/sf/antcontrib/antlib.xml"/>
<!--Импорт параметров экземпляра системы, на который хотим переносить свое приложение (файл №4)-->
<property file="${instance}.properties"/>
<property name="project.name" value = "concurrent"/>
<!--Необходимые пути для ссылки на библиотеки и т.п.-->
<property name="jdev.dir" value="C:/jdev"/>
<property name="base.dir" value="C:/jdev/jdevhome/jdev"/>
<property name="src.dir" value="${base.dir}/myprojects"/>
<property name="classes.dir" value="${base.dir}/myclasses"/>
<property name="jdbc.lib.dir" value="${jdev.dir}/jdevbin/jdbc/lib"/>
<property name="bc4j.lib.dir" value="${jdev.dir}/jdevbin/bc4j/lib"/>
<property name="uix2.lib.dir" value="${jdev.dir}/jdevbin/jdev/appslibrt"/>
<!--Импорт параметров проекта (файл №2)-->
<import file="${project}.xml"/>
<!--Определяем библиотеки OAF-->
<path id="class.path">
<pathelement location="${uix2.lib.dir}\fwk.zip"/>
<pathelement location="${uix2.lib.dir}\fwkjbo.zip"/>
<pathelement location="${uix2.lib.dir}\aolj.jar"/>
<pathelement location="${uix2.lib.dir}\uix2.jar"/>
<pathelement location="${uix2.lib.dir}\svc.zip"/>
<pathelement location="${bc4j.lib.dir}\bc4jmt.jar"/>
<pathelement location="${bc4j.lib.dir}\bc4jdomorcl.jar"/>
<pathelement location="${jdbc.lib.dir}\ojdbc14.jar"/>
<pathelement location="${jdbc.lib.dir}\ojdbc14dms.jar"/>
<pathelement location="${jdbc.lib.dir}\orai18n.jar"/>
</path>
<!--Если instance="prod" т.е. у меня это прзнак того, что переносим на "боевой" сервер, то устаналиваем is.prod="prod"
для дальнейшего использования, иначе этот параметр остается пустым-->
<target name="init" description="Initialize">
<condition property="is.prod">
<equals arg1="${instance}" arg2="prod"/>
</condition>
</target>
<!--И здесь смотрим, если параметр is.prod инициализирован, то просим ввести пароли для APPS и APPLMGR, иначе пароли берутся из файла instance.xml-->
<target name="set.password" depends="init" if="is.prod" description="Set production password">
<input message="Please enter APPS password:" addproperty="db.password">
<handler classname="org.apache.tools.ant.input.SecureInputHandler"/>
</input>
<input message="Please enter APPLMGR password:" addproperty="appl.password">
<handler classname="org.apache.tools.ant.input.SecureInputHandler"/>
</input>
</target>
<!--Компилируем Java файлы, указанные в файле <project>.xml в параметре java.files-->
<target name="compile" description="Compile source java files">
<!--Удаляем предыдущие версии class файлов-->
<delete dir="${classes.dir}/${root.package}"/>
<!--Конвертируем список Java файлов в строку с разделителем ","-->
<pathconvert targetos="unix" pathsep="," property="java.files" refid="java.files">
<map from="${src.dir}/" to=""/>
</pathconvert>
<!--Компилируем-->
<javac includes="${java.files}" destdir="${classes.dir}" target="1.5" encoding="UTF-8" includeantruntime="false" includejavaruntime="false">
<classpath refid="class.path"/>
<src path="${src.dir}"/>
</javac>
</target>
<!--Переносим все необходимое на сервер-->
<target name="deploy" depends="compile, set.password" description="Upload files to OA Server">
<!--Копируем файлы из каталога исходников в каталог "типа" out (на клиентской машине пока)-->
<copy todir="${classes.dir}" flatten="false">
<fileset refid="server.files"/>
<fileset refid="webui.files"/>
<fileset refid="xliff.files"/>
</copy>
<!--Копируем class файлы и xml файлы типа AM, VO, EO на сервер. Параметры подключения и каталог-приемник указаны в файле <project>.properties-->
<scp todir="${appl.user}:${appl.password}@${appl.host}:${java.top}" trust="true">
<fileset refid="class.files"/>
<fileset refid="server.files"/>
</scp>
<!--Импортируем xml файлы типа PG, RN. Параметры подключения указаны в файле <project>.properties-->
<for param="file">
<path>
<fileset refid="webui.files"/>
</path>
<sequential>
<echo message="Importing XML file"/>
<exec executable="${jdev.dir}\jdevbin\oaext\bin\import.bat">
<arg value="@{file}"/>
<arg value="-rootdir"/>
<arg value="${src.dir}"/>
<arg value="-username"/>
<arg value="${db.user}"/>
<arg value="-password"/>
<arg value="${db.password}"/>
<arg value="-dbconnection"/>
<arg value="${db.host}:${db.port}:${db.sid}"/>
<arg value="-rootPackage"/>
<arg value="/"/>
</exec>
</sequential>
</for>
<!--Импортируем xliff файлы. Параметры подключения указаны в файле <project>.properties-->
<for param="file">
<path>
<fileset refid="xliff.files"/>
</path>
<sequential>
<echo message="Importing XLIFF file"/>
<exec executable="${jdev.dir}\jdevbin\oaext\bin\xliffimport.bat">
<arg value="@{file}"/>
<arg value="-username"/>
<arg value="${db.user}"/>
<arg value="-password"/>
<arg value="${db.password}"/>
<arg value="-dbconnection"/>
<arg value="${db.host}:${db.port}:${db.sid}"/>
</exec>
</sequential>
</for>
<!--Выделяем все параметры у которых имя начинается на "sql."-->
<propertyselector property="sql" match="sql.*"/>
<!--И подставляем их содержимое в команду sql в цикле-->
<for list="${sql}" param="sql.property">
<sequential>
<sql driver="oracle.jdbc.driver.OracleDriver" url="jdbc:oracle:thin:@${db.host}:${db.port}:${db.sid}" userid="${db.user}" password="${db.password}" onerror="continue" classpathref="class.path">
${@{sql.property}}
</sql>
</sequential>
</for>
</target>
<!--"Передергиваем" Apache OACore для вступления в силу перенесенных файлов-->
<target name="bounce.apache" depends="deploy, bounce.apache.only" description="Bounce apache server">
</target>
<!--Отдельное задание для "передергивания" только Apache OACore без параметра depends-->
<target name="bounce.apache.only" description="Bounce apache server">
<delete file="${src.dir}/bounceapach.sh"/>
<!--echo file="bounceapach.sh" append="true" message="sh ${admin.scripts.home}/adapcctl.sh stop${line.separator}"/-->
<echo file="bounceapach.sh" append="true" message="sh ${admin.scripts.home}/adoacorectl.sh stop${line.separator}"/>
<!--echo file="bounceapach.sh" append="true" message="sh ${admin.scripts.home}/adapcctl.sh start${line.separator}"/-->
<echo file="bounceapach.sh" append="true" message="sh ${admin.scripts.home}/adoacorectl.sh start${line.separator}"/>
<sshexec host="${appl.host}" username="${appl.user}" password="${appl.password}" commandResource="bounceapach.sh" trust="true"/>
<delete file="${src.dir}/bounceapach.sh"/>
</target>
<target name="all" depends="bounce.apache"/>
</project>
---------------------------------------------------------Конец---------------------------------------------------------
Содержание файла <instance>.properties
---------------------------------------------------------Начало--------------------------------------------------------- db.host=192.168.192.192 db.user=apps db.password=apps db.port=1538 db.sid=test appl.host=192.168.192.193 appl.user=applmgr appl.password=applmgr java.top=Необходимо прописать путь $JAVA_TOP appl.top=Необходимо прописать путь $APPL_TOP admin.scripts.home=Необходимо прописать путь $ADMIN_SCRIPTS_HOME ---------------------------------------------------------Конец--------------------------------------------------------
В этом файле все просто. Можно было бы его тоже оформить как ant’овский xml файл, но пока у меня так.
Типа того.

Подскажите, почему необходим «Импорт xml файлов Pages, Regions с помощью утилиты import»? Почему нельзя просто перенести их в соответствующий каталог на сервере?
@profik777
Через XMLImporter происходит запись данных в MDS репозиторий (заполняются таблицы JDR_ATTRIBUTES, JDR_COMPONENTS, JDR_PATHS, JDR_ATTRIBUTES_TRANS).
Огромное Вам спасибо за неоценимую помощь! :-)