0 Replies Latest reply: Jan 9, 2013 11:40 AM by Christian Erlinger RSS

    Forms 11gR1: deploy forms servlets on the AdminServer

    Christian Erlinger
      First of all I boldly have to mention this: the below procedure is NOT supported by Oracle, it isn't now nor will it be in the near future. All in all it is a hack so decide yourself if you want to try it or not. At least I found it somehow interesting to get to know the architecture of weblogic and some of the tools around it.
      The whole post is a bit long; I post it because Oracle Support showed interest in the procedure (though as said not offering Support for it) and the MOS portal doesn't allow to post such long threads; and as for now I don't have any other place to put it. As I am in kind of a rush right now I may add some things that are not fully automated yet later (and post them here).

      The following instructions are intended to minimize the Forms installation by deploying the Forms/Reports and Discoverer Servlets on the AdminServer (just like it is done in 11gR2 development mode). This can be useful if you are using Forms 11gR1 in development or are wondering why on earth one would need 3 weblogic instances consuming 1GB memory each for a small (say 20-30 concurrent Forms Sessions) Forms Installation in Production. As said this isn't supported by oracle, but the whole process will leave the default configuration intact wich means if you run into troubles you can revert to a supported config quite fast. The steps are taken after you successfully installed Forms 11g; in short we will repack the ear files of the forms/reports/discoverer Application with all the dependendend jar files and deploy them on the AdminServer (or any Managed Server if you like).

      If you take a look at the startManagedWeblogic.cmd, startWeblogic.cmd and setDomainEnv.cmd scripts the AdminServer as well as the managed Servers don't differ much except for the JAVA_OPTIONS and the CLASSPATH (and the Web Applications they have deployed such as the Enterprise Manager or the Forms Servlets). The CLASSPATH variables and the JAVA_OPTIONS are just built differently for each Server. My first attempt was to unite the JAVA_OPTIONS and the CLASSPATH variables which was successful for the JAVA_OPTIONS however for the CLASSPATH this was futile as I inevitably ended up in the Jar File Hell, as at least Discoverer and Reports used some Java Classes of the same name but of different Versions. The solution to this problem is to create ear files packed with the jar files in the classpath of each weblogic managed server. The forms/reports/discoverer applications are already packed as ear files; however you will notice that they don't contain any dependend libraries at all (that's why the managed servers have set a classpath).

      The Forms ear file is located under $ORACLE_HOME/forms/j2ee/formsapp.ear you can view it with any zip program such as 7zip and see what's inside.

      First off we start locating the jar files in the classpath of the different weblogic servers; as said the classpath is stuffed together in the startscripts; to get it simply add
      echo %CLASSPATH% > c:\wls_classpath\classpath_%SERVER_NAME%.txt
      echo %JAVA_OPTIONS% > c:\wls_classpath\javaoptions_%SERVER_NAME%.txt
      in startWeblogic.cmd just before the weblogic class is invoked; it is quite at the end, just search for
      echo starting weblogic with Java version:
      now I simply start each weblogic server and take a look at the generated files.

      Now for forms we simply would have to
      - extract the formsapp.ear to say c:\wls_classpath
      - create a lib directory within c:\wls_classpath
      - copy all files mentioned in c:\wls_classpath\classpath_WLS_FORMS.txt into c:\wls_classpath\lib
      - edit the application.xml in c:\wls_classpath\lib\META-INF and add a library-directory tag:
      <?xml version="1.0" encoding="UTF-8" standalone="no"?>
      <application [...]>
        <module>
          <web>
            <web-uri>formsweb.war</web-uri>
            <context-root>forms</context-root>
          </web>
        </module>
        <!-- this line is added -->
      <library-directory>lib</library-directory>
      </application>
      - repack a new formsapp.ear (of course you should keep the original one in place where it is)

      Sounds like a lot of work? Well it is if you are doing it by hand. It isn't when you let ANT do the job.
      I created a little ANT script which takes the above created txt files, copies over all the jar files to a sub directory, extracts the ear files, edits the application.xml file and repacks the ear file with the dependend Libraries. All you'd need is

      - a JDK (needed for ANT); you could use the JDK installed with weblogic
      - ANT: http://ant.apache.org
      - ANT contrib: http://ant-contrib.sourceforge.net/
      - ANT XML Task: http://www.oopsconsultancy.com/software/xmltask/

      Just see in the installguides of ANT and the respective tasks for information on how to install them if you are not familiar with ANT. It is pretty simple so I guess there is no need to discuss this here ;-).

      Now the following ANT script will generate you the respective ear files. First off the properties file where you need to adjust the paths of your oracle home and the directory where the textfiles containing the classpath from above are located:
      #build.properties file; set here your real paths
      oracle.home=C:/oracle/oracledevs11gR2/frm11gR2
      classpathfiles.dir=c:/wls_classpath
      (just replace the paths with your variables)

      Save it as say build.properties. Now to the build file itself; unfortunately this forum interprets XML text which results that some text I escaped is interpreded and is not visible. I'll provide an unformatted Version (unfortunately when using the noformat tag the code tag get's ignored as well resulting in some lines of unreadable code) which you'd need to copy/paste in a new file.
      <?xml version="1.0" encoding="windows-1252" ?>
      <!-- build.xml buildfile -->
      <project name="11gR1_ear_files" default="all" basedir=".">
        <!-- taskdef for ant.contrib 
             see http://ant-contrib.sourceforge.net/ -->
        <taskdef resource="net/sf/antcontrib/antlib.xml"/>
        <!-- taskdef for the xmltask used to edit the application.xml file 
             see http://www.oopsconsultancy.com/software/xmltask -->
        <taskdef name="xmltask" classname="com.oopsconsultancy.xmltask.ant.XmlTask"/>
        <!-- our properties file for all the variables needed -->
        <property file="build.properties"/>
        <!-- macro doing all the stuff -->
        <macrodef name="macro.recreateear" description="recreate a ear file with dependend jar files">
          <attribute name="recreateear.srcfile"/> <!-- the source ear file -->
          <attribute name="recreateear.targetname"/> <!-- the name of the target ear file -->
          <attribute name="recreateear.jarfiles"/> <!-- a CSV file containing absolute paths for the jar files in the classpath -->
          <sequential>
            <!-- delete the whole thing from previous runs and recreate it -->
            <delete dir="./@{recreateear.targetname}"/>
            <mkdir dir="./@{recreateear.targetname}"/>
            <mkdir dir="./@{recreateear.targetname}/lib"/>
            <!-- now we unjar the ear file we want to recreate -->
            <unjar src="@{recreateear.srcfile}" dest="./@{recreateear.targetname}"/>
            <!-- we need to move the original application.xml out of the META-INF folder as we need to add the library
                 directory plus the ear task fails if there is already an application.xml file in the META-INF folder -->
            <move file="./@{recreateear.targetname}/META-INF/application.xml" tofile="@{recreateear.targetname}_application_orig.xml"/>
            <!-- now we add the library directory to the application.xml file -->
            <xmltask source="@{recreateear.targetname}_application_orig.xml" dest="@{recreateear.targetname}_application.xml">
              <insert path="/*[local-name()='application']/*[local-name()='module'][last()]" position="after">
                <![CDATA[
                  <library-directory>lib</library-directory>
                ]]>
              </insert>
            </xmltask>      
            <!-- load the contents of the file containing the classpath -->
            <loadfile property="classpath.entries" srcFile="@{recreateear.jarfiles}"/>
            <!-- now we loop through each entry of the classpath and copy them to the lib directory; the jar files are seperated by semicolons
                 due to a problem with Paths like C:\\oracle\\ORACLE~4\\WLSERV~1.3 which seems to be a problem in some cases when invoked in a copy
                 in the loop (but not by a direct copy call) we will write a temporary buildfile which invokes copy for each file 
                 and invoke this buildfile via antfetch -->
            <!-- here in the echo task the forum interprets my escaped characters; either you hit "reply", quote and copy the text, or 
                  do the replacement yourself between the <echo> tags; 
                  see e.g. http://stackoverflow.com/questions/1091945/where-can-i-get-a-list-of-the-xml-document-escape-characters for what you need to escape  -->
            <echo file="copyjarfiles.xml" append="false">
              &lt;project name=&quot;copy_jar_files&quot; default=&quot;all&quot; basedir=&quot;.&quot;&gt;
                &lt;property file=&quot;build.properties&quot;/&gt;
                &lt;target name=&quot;all&quot;&gt;
            </echo>
            <for delimiter=";" param="filename" list="${classpath.entries}">
              <sequential>
                <!-- same applies here => escape the copy tag -->
                <echo file="copyjarfiles.xml" append="true">
                  &lt;copy file=&quot;@{filename}&quot; todir=&quot;./@{recreateear.targetname}/lib&quot; failonerror=&quot;false&quot;/&gt;
                </echo>
              </sequential>
            </for>
            <!-- and once more the escaped chars need to be replaced -->
            <echo file="copyjarfiles.xml" append="true">
              &lt;/target&gt;
              &lt;/project&gt;
            </echo>
            <antfetch antfile="copyjarfiles.xml" target="all"/>
            <!-- now we recreate the ear file -->
            <ear destfile="@{recreateear.targetname}.ear" appxml="@{recreateear.targetname}_application.xml" basedir="./@{recreateear.targetname}"/>
          </sequential>
        </macrodef>
           <target name="all" depends="frmservlet,rwservlet,discoverer">
           </target>
        <target name="frmservlet">
          <macro.recreateear recreateear.srcfile="${oracle.home}/forms/j2ee/formsapp.ear" recreateear.targetname="frmwebapp" recreateear.jarfiles="${classpathfiles.dir}/classpath_WLS_FORMS.txt"/>
           </target>
           <target name="rwservlet">
                <macro.recreateear recreateear.srcfile="${oracle.home}/reports/j2ee/reports.ear" recreateear.targetname="reports" recreateear.jarfiles="${classpathfiles.dir}/classpath_WLS_REPORTS.txt"/>
           </target>
        <target name="discoverer">
           <macro.recreateear recreateear.srcfile="${oracle.home}/discoverer/deploy/discoverer.ear" recreateear.targetname="discoverer" recreateear.jarfiles="${classpathfiles.dir}/classpath_WLS_DISCO.txt"/>
        </target>
        <target name="test">
          <copy file="C:\\oracle\\ORACLE~4\\WLSERV~1.3\\server\\lib\\xqrl.jar" todir="."/>
        </target>
      
      </project>
      Save the first file as build.properties, and the second as build.xml in the very same directory. After that open a shell in the directory where your buildfile is located, and simply type
      %ANT_HOME%/bin/ant frmservlet
      as the buildfile is named build.xml the frmservlet target is invoked, and shortly after you should have a forms ear file. If you call the default target by simply invoking ant without any parameters the forms/reports/discoverer ear files are built.

      After that we need to unite the java options of all weblogic servers; You can do that with whatever tool you like; I built an external table upon the javaoptions_%SERVER_NAME%.txt files (records delimited by blanks), did a select distinct and had my java options. The below version should work for forms 11.1.1.6; just paste before the weblogic.jar file is invoked in the startWeblogic.cmd script (where you outputted the classpath and the java_options to text files in the first place)
      if "%SERVER_NAME%"=="AdminServer" (
        set MIDDLEWARE_HOME=c:\oracle\frm
        set INSTANCE_NAME=asinst_1
        set ORACLE_HOME_NAME=as_1
        set DOMAIN_NAME=ClassicDomain
        set ORACLE_HOME=%MIDDLEWARE_HOME%\%ORACLE_HOME_NAME%
        set INSTANCE_HOME=%MIDDLEWARE_HOME%\%INSTANCE_NAME%
        set ORACLE_COMMON_HOME=%MIDDLEWARE_HOME%\oracle_common
        set DOMAIN_HOME=%MIDDLEWARE_HOME%\user_projects\domains\%DOMAIN_NAME%
        set WEBLOGIC_HOME=%MIDDLEWARE_HOME%\wlserver_10.3
      
        set JAVA_OPTIONS=-Dclassic.oracle.home=%ORACLE_HOME% -Dcommon.components.home=%ORACLE_COMMON_HOME% -Ddomain.home=%DOMAIN_HOME% -Ddomain.name=ClassicDomain -Dem.oracle.home=%ORACLE_COMMON_HOME% -Digf.arisidbeans.carmlloc=%DOMAIN_HOME%\config\FMWCON~1\carml -Digf.arisidstack.home=%DOMAIN_HOME%\config\FMWCON~1\arisidprovider -Djava.awt.headless=true -Djava.ext.dirs=%ORACLE_HOME%\jdk\jre\lib\ext -Djava.protocol.handler.pkgs=oracle.mds.net.protocol -Djava.rmi.server.randomIDs=true -Djrf.version=11.1.1 -Djrockit.optfile=%ORACLE_COMMON_HOME%\modules\oracle.jrf_11.1.1\jrocket_optfile.txt -Doracle.deployed.app.dir=%DOMAIN_HOME%\servers\AdminServer\tmp\_WL_user -Doracle.deployed.app.ext=\- -Doracle.domain.config.dir=%DOMAIN_HOME%\config\FMWCON~1 -Doracle.forms.weblogic=1 -Doracle.home=%ORACLE_HOME% -Doracle.instance.name=%INSTANCE_NAME% -Doracle.instance=%INSTANCE_HOME% -Doracle.security.jps.config=%DOMAIN_HOME%\config\fmwconfig\jps-config.xml -Doracle.server.config.dir=%DOMAIN_HOME%\config\FMWCON~1\servers\AdminServer -Dorg.apache.commons.logging.Log=org.apache.commons.logging.impl.Jdk14Logger -Dplatform.home=%WEBLOGIC_HOME% -Dwc.oracle.home=%ORACLE_HOME% -Dweblogic.ProductionModeEnabled=true -Dweblogic.alternateTypesDirectory=%ORACLE_COMMON_HOME%\modules\oracle.ossoiap_11.1.1 -Dweblogic.ext.dirs=%MIDDLEWARE_HOME%\patch_wls1036\profiles\default\sysext_manifest_classpath -Dweblogic.home=%WEBLOGIC_HOME%\server -Dweblogic.jdbc.remoteEnabled=false -Dweblogic.management.discover=false -Dweblogic.security.SSL.trustedCAKeyStore="%WEBLOGIC_HOME%\server\lib\cacerts" -Dwls.home=%WEBLOGIC_HOME%\server -Dwlw.iterativeDev=false -Dwlw.logErrorsToConsole=false -Dwlw.testConsole=false -Xms512m -Xmx1024m -da
      )
      I am working on a simple way to automate this as well; as for now you either use above line for forms 11.1.1.6 or do it however you like it to do.

      Now we can start the AdminServer with the new java_options and see if it comes up.

      After that we need to deploy our newly created ear file on the AdminServer. Logon to the weblogic console (http://<your_server>:7001/console) by default), and navigate to "Deployments" (should be located on the left side). Now lock the config, and click on "install". The whole Process is somehow straight foreward; you choose the newly created ear file, install it as an Application, choose the AdminServer as deployment target and deploy the whole thing. Note that you need to give the newly deployed application a new name as there is already a Application called "frmwebapp" installed on WLS_FORMS; just name it as you like; the name will reflect in the path to the formsweb.cfg though so be sure to choose it wisely ;-).
      You can of course use the weblogic scripting tool (WLST) to deploy the ear file automatically as well; I used this script to deploy my forms ear file.

      First of all the properties file where you can adjust all the settings
      #install_servlets.properties file with the properties for deploying the newly created ear file on the AdminServer
      files.location=c:/wls_classpath #location of the newly created ear files
      admin.user=weblogic
      admin.pwd=oracle11
      admin.host=localhost
      admin.port=7001
      in WLST basically Python is used (a Language I didn't know before but found very interesting and useful to learn); the interpreter WLST uses is basically JPython or Jython. This script reads some properties and deploys the ear file on the AdminServer under the name "formsapp_1":
      #install_servlets.py will deploy the forms servlets on the AdminServer
      import sys
      from java.lang import System
      import getopt
      from java.io import FileInputStream
      
      propInputStream = FileInputStream("install_servlets.properties")
      props = Properties()
      props.load(propInputStream)
      
      location = props.get('files.location')
      username = props.get('admin.user')
      pwd = props.get('admin.pwd')
      server = props.get('admin.host')
      port = props.get('admin.port')
      domainroot = ''
      
      
      connect(username, pwd, 't3://'+server+':'+port)#connect to AdminServer
      domainroot=get('RootDirectory') #get the domain home
      print 'deploying '+location+'/frmwebapp.ear'
      deploy('formsapp_1', location+'/frmwebapp.ear', targets='AdminServer', planPath = domainroot+'/deploymentplans/formsapp/11.1.1/plan.xml')
      now we can call WLST with our script
      set currdir=%CD%
      call %DOMAIN_HOME%/bin/setDomainEnv.cmd
      cd %currdir%
      java weblogic.WLST install_servlets.py
      After that you will notice that there is a formsweb.cfg located under
      %DOMAIN_HOME%\config\fmwconfig\servers\AdminServer\applications\<formsapp_name>\config
      however; the default.env lacks the INSTANCE_HOME and ORACLE_HOME variables, so forms won't come up. So either you copy the original files over to this directory, or if you are on server 2008 you could also use mklink (http://technet.microsoft.com/en-us/library/cc753194%28v=ws.10%29.aspx) to create a symbolic link from the original config folder under WLS_FORMS to the new one under the AdminServer directory. I guess in order to have the certified config up and running in no time this isn't a bad idea.

      Now simply invoke

      http://<server>:7001/forms/frmservlet

      and the testform should successfully come up. For the use with Apache you'd need to adjust the port in the weblogic module config files located under %INSTANCE_HOME%/config\OHS\ohs1\moduleconf; e.g. the forms.conf to
      <Location /forms>
              SetHandler weblogic-handler
              WebLogicHost <serverhost>
              WebLogicPort 7001
      </Location>
      so Apache also recognizes that the forms application is now available on the AdminServer.

      Now you have the forms servlets deployed on the AdminServer. The same process applies to reports and discoverer (the buildfile will generate the ear files for them as well if chosen). The whole process can be reverted quite easily; if you start e.g. WLS_FORMS forms will be available via this weblogic server too, all you'd need to do is to change the apache config. If you insist you might delete the deployed forms application and revert the changes made to the startWeblogic.cmd script as well but beware if you are using the symbolic link because if memory serves the config files are deleted with the application as well and you would delete the config files for WLS_FORMS by deleting the forms application deployed on the AdminServer.

      And last but not least here is the unformatted version of the buildfile; just copy/paste it and maybe format it with a XML formater.

      {noformat}
      <?xml version="1.0" encoding="windows-1252" ?>
      <project name="11gR1_ear_files" default="all" basedir=".">
      <!-- taskdef for ant.contrib
      see http://ant-contrib.sourceforge.net/ -->
      <taskdef resource="net/sf/antcontrib/antlib.xml"/>
      <!-- taskdef for the xmltask used to edit the application.xml file
      see http://www.oopsconsultancy.com/software/xmltask -->
      <taskdef name="xmltask" classname="com.oopsconsultancy.xmltask.ant.XmlTask"/>
      <!-- our properties file for all the variables needed -->
      <property file="build.properties"/>
      <!-- macro doing all the stuff -->
      <macrodef name="macro.recreateear" description="recreate a ear file with dependend jar files">
      <attribute name="recreateear.srcfile"/> <!-- the source ear file -->
      <attribute name="recreateear.targetname"/> <!-- the name of the target ear file -->
      <attribute name="recreateear.jarfiles"/> <!-- a CSV file containing absolute paths for the jar files in the classpath -->
      <sequential>
      <!-- delete the whole thing from previous runs and recreate it -->
      <delete dir="./@{recreateear.targetname}"/>
      <mkdir dir="./@{recreateear.targetname}"/>
      <mkdir dir="./@{recreateear.targetname}/lib"/>
      <!-- now we unjar the ear file we want to recreate -->
      <unjar src="@{recreateear.srcfile}" dest="./@{recreateear.targetname}"/>
      <!-- we need to move the original application.xml out of the META-INF folder as we need to add the library
      directory plus the ear task fails if there is already an application.xml file in the META-INF folder -->
      <move file="./@{recreateear.targetname}/META-INF/application.xml" tofile="@{recreateear.targetname}_application_orig.xml"/>
      <!-- now we add the library directory to the application.xml file -->
      <xmltask source="@{recreateear.targetname}_application_orig.xml" dest="@{recreateear.targetname}_application.xml">
      <insert path="/*[local-name()='application']/*[local-name()='module'][last()]" position="after">
      <![CDATA[
      <library-directory>lib</library-directory>
      ]]>
      </insert>
      </xmltask>
      <!-- load the contents of the file containing the classpath -->
      <loadfile property="classpath.entries" srcFile="@{recreateear.jarfiles}"/>
      <!-- now we loop through each entry of the classpath and copy them to the lib directory; the jar files are seperated by semicolons
      due to a problem with Paths like C:\\oracle\\ORACLE~4\\WLSERV~1.3 which seems to be a problem in some cases when invoked in a copy
      in the loop (but not by a direct copy call) we will write a temporary buildfile which invokes copy for each file
      and invoke this buildfile via antfetch -->
      <echo file="copyjarfiles.xml" append="false">
      &lt;project name=&quot;copy_jar_files&quot; default=&quot;all&quot; basedir=&quot;.&quot;&gt;
      &lt;property file=&quot;build.properties&quot;/&gt;
      &lt;target name=&quot;all&quot;&gt;
      </echo>
      <for delimiter=";" param="filename" list="${classpath.entries}">
      <sequential>
      <echo file="copyjarfiles.xml" append="true">
      &lt;copy file=&quot;@{filename}&quot; todir=&quot;./@{recreateear.targetname}/lib&quot; failonerror=&quot;false&quot;/&gt;
      </echo>
      </sequential>
      </for>
      <echo file="copyjarfiles.xml" append="true">
      &lt;/target&gt;
      &lt;/project&gt;
      </echo>
      <antfetch antfile="copyjarfiles.xml" target="all"/>
      <!-- now we recreate the ear file -->
      <ear destfile="@{recreateear.targetname}.ear" appxml="@{recreateear.targetname}_application.xml" basedir="./@{recreateear.targetname}"/>
      </sequential>
      </macrodef>
           <target name="all" depends="frmservlet,rwservlet,discoverer">
           </target>
      <target name="frmservlet">
      <macro.recreateear recreateear.srcfile="${oracle.home}/forms/j2ee/formsapp.ear" recreateear.targetname="frmwebapp" recreateear.jarfiles="${classpathfiles.dir}/classpath_WLS_FORMS.txt"/>
           </target>
           <target name="rwservlet">
                <macro.recreateear recreateear.srcfile="${oracle.home}/reports/j2ee/reports.ear" recreateear.targetname="reports" recreateear.jarfiles="${classpathfiles.dir}/classpath_WLS_REPORTS.txt"/>
           </target>
      <target name="discoverer">
           <macro.recreateear recreateear.srcfile="${oracle.home}/discoverer/deploy/discoverer.ear" recreateear.targetname="discoverer" recreateear.jarfiles="${classpathfiles.dir}/classpath_WLS_DISCO.txt"/>
      </target>
      <target name="test">
      <copy file="C:\\oracle\\ORACLE~4\\WLSERV~1.3\\server\\lib\\xqrl.jar" todir="."/>
      </target>

      </project>
      {noformat}

      cheers
      Christian