При разработке проектов с использованием 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).
Огромное Вам спасибо за неоценимую помощь! :-)