12 September 2013

Customizing, Exporting, Importing and Templating AM Pool Configuration for ADF applications

Warning: Please note that the following steps are not explicitly documented in the Oracle documentation. As such it's not known for sure whether this is the way to customize and template bc4j.xcfg (AM Pool) configuration - or whether there are simpler or better supported ways to do this.

Each (root) AM Pool has an associated configuration pertaining to number of AM instances allowed, life time, passivation timeouts etc. This configuration is typically stored in bc4j.xcfg files, which are generally generated by developers (sometimes unbeknownst to them) and littered throughout the EAR file - if the EAR file is modularised using JAR files for the BC4J components then the bc4j.xcfg files are located inside the JAR files, which are inside the EAR file. This does not make customization of the AM pool configuration very attractive from an operations perspective. In our case there are 6 root AM pools and their default settings quickly blow the size of the JDBC connection pool that is provided to the application, which results in application errors.
So, it is necessary to customize the settings in such a way that the JDBC connection pool is not exhausted - and that during peak periods the AM instances are passivated to the database and JDBC connections recycled instead. After all, the overhead of passivation would be preferable to users getting spurious JDBC errors pertaining to the inability to get a JDBC connection from the connection pool.

The Java system properties can be used to control some of these settings - but are overridden by the presence of bc4j.xcfg files for a given root AM pool. Also, the system parameters cannot be set on an individual root AM pool basis - and so, are simply not granular enough. E.g. some root AM pools are shared to cache static application configuration and don't need 10's or 1000's of (some times pre-allocated) instances - this also wastes JVM heap space. Other root AM pools do. The default upper bound for a root AM pool is 4096 instances - so with 6 root AM pools we're looking at a theoretical max of 24576 instances - all of which (when used) require a JDBC connection in our case, which could be persisted (AM instance to JDBC connection affinity). The database and hardware simply cannot sanely support that number of JDBC connections - and frankly it doesn't make sense (think concurrently active database sessions versus CPU cores or SMT threads or Hyperthreading or whatever).

In our case we have multiple application environments (dev, test, UAT etc) and want to be able to customize the 6 root AM pools. This can be achieved - but as stated previously, isn't documented explicitly by Oracle anywhere.

When bc4j.xcfg configuration for root AM pools need to be customized in a given environment, the Enterprise Manager Fusion Middleware Control can be used for this - assuming that an MDS store (file or database-based) has been set-up and the underlying store (directory or JNDI-based data source) has been correctly set-up. Simply login to /em of the WebLogic server, Navigate to: Farm_<farm> -> Application Deployments -> <application name>,right-click on <application name> and choose: ADF -> Configure ADF Business Components. Any customizations (parameter changes) are stored in the MDS repository as XML files - more about this later.

The caveat here is that ADF Business Components Configuration page lists all AM pools - not just the root pools. I have found no easy way to filter this to just root AM pools. In our case the application has nearly 60 AM pools - and so the list is rather unwieldy. Furthermore, it is not clearly identified which AM pools are root. I have found no simple, deterministic way of identifying the pools inside the EAR file - except for running the application and using the /dms/Spy application to determine the pools. To list the root AM Pools navigate to WebLogic using http://<weblogic host>:<admin port>/dms/Spy - login as the weblogic user. 

In the Metric Tables find and click on ampool. Statistics concerning the root AM Pools (that have been initialized) will appear. Use this list to determine what pools to customise in the EM ADF Business Components Configuration page. As will quickly become obvious this is a very laborious effort that needs to be repeated for each deployment of the application - and so really needs to be automated with a set of bc4j.xcfg-based templates, if you will.

The wlst script and metadata repository commands to the rescue... Note that not all wlst.(sh|cmd) scripts are created equal in terms of JAR files and class paths. The examples below I run in wlst.sh from a SOA Suite installation - we're also using SOA Suite - in order to ensure that commands like exportMetadata, importMetadata​, getMDSArchiveConfig and setAppMetadataRepository exist. The location of the wlst.sh script in the SOA Suite that contains the right set of JAR files and class paths is: /u01/app/oracle/Middleware/<SOA Suite Oracle Home>/common/bin/wlst.sh. Be advised that as long as you got the right wlst.sh for the job somewhere on your LAN with access to your ADF application environments you don't need to go off and install SOA Suite everywhere (also mind licensing!) - you can connect remotely to WebLogic and run the repository commands - using the "remote" option for commands involving files - the files must then exist on the host from which you run wlst.

The process to be able to export/import the configuration across envrionments is:
1. Use the ADF Business Components Configuration page in one environment (say, development) to customize the root AM pools
2. Export the bc4j.xcfg metadata from the environment (say, development)
3. Import the bc4j.xcfg metadata into another environment (say, test)

Once wlst is started, connect to the AdminServer of the development environment:

connect("weblogic", "password", "t3://dev:7001")

If, at this stage, you were to run an unqualified (default: "/**") export using the command below you would export all ADF customizations stored in the MDS - which is likely not what you want - worse, you wouldn't be exporting the bc4j.xcfg configuration that you are really after. For some strange reason these xml files are not included per default.
exportMetadata(application="<Development ADF application name>", server="<Development Managed Server Name>", toLocation="/tmp/development_mds_settings.mds", remote=true, docs="/**")
So, the question is then how to identify what xml files to export and how to export them explicitly. This required a bit of investigation and in the end I found that the easiest way was (in my case) to connect to the database containing the MDS repository and run:
select distinct path_fullname
  from <mds prefix>_mds.mds_paths
 where lower(path_fullname) like '%bc4j%' and path_partition_id = ( select distinct PARTITION_ID from <mds prefix>_mds.mds_partitions where PARTITION_NAME='<partition name>');

/xx/xxx/xxxxx/xxxx/common/service/common/mdssys/cust/adfshare/adfshare/bc4j.xcfg.xml
/xx/xxx/xxxxx/xxxx/xxxx/model/service/common/mdssys/cust/adfshare/adfshare/bc4j.xcfg.xml
/xx/xxx/xxxxx/xxxx/xxxxxxxx/service/common/mdssys/cust/adfshare/adfshare/bc4j.xcfg.xml
/xx/xxx/xxxxx/xxxx/xxxxx/service/common/mdssys/cust/adfshare/adfshare/bc4j.xcfg.xml
/xx/xxx/xxxxx/xxxx/xxxxx/xxxx/model/service/common/mdssys/cust/adfshare/adfshare/bc4j.xcfg.xml
/xx/xxx/xxxxx/xxxx/xxxxx/model/service/common/mdssys/cust/adfshare/adfshare/bc4j.xcfg.xml
The X'es simply mask the real names. You will need to provide the correct partition name for this (a list of them exist in _mds.mds_partitions).
Now, to export the configuration for all these files in a single file do the following (comma-separated list of xml files):
exportMetadata(application="<Development ADF application name>", server="<Development Managed Server Name>", toLocation="/tmp/development_mds_settings.mds", remote=true,
docs="/xx/xxx/xxxxx/xxxx/common/service/common/mdssys/cust/adfshare/adfshare/bc4j.xcfg.xml,
/xx/xxx/xxxxx/xxxx/xxxx/model/service/common/mdssys/cust/adfshare/adfshare/bc4j.xcfg.xml,
/xx/xxx/xxxxx/xxxx/xxxxxxxx/service/common/mdssys/cust/adfshare/adfshare/bc4j.xcfg.xml,
/xx/xxx/xxxxx/xxxx/xxxxx/service/common/mdssys/cust/adfshare/adfshare/bc4j.xcfg.xml,
/xx/xxx/xxxxx/xxxx/xxxxx/xxxx/model/service/common/mdssys/cust/adfshare/adfshare/bc4j.xcfg.xml,
/xx/xxx/xxxxx/xxxx/xxxxx/model/service/common/mdssys/cust/adfshare/adfshare/bc4j.xcfg.xml?")

Executing operation: exportMetadata.
Metadata transfer operation started
Exporting metadata from repository . . . . .
Metadata transfer operation completed

Operation "exportMetadata" completed. Summary of "exportMetadata" operation is:
6 documents successfully transferred.
List of documents successfully transferred:

/xx/xxx/xxxxx/xxxx/common/service/common/mdssys/cust/adfshare/adfshare/bc4j.xcfg.xml
/xx/xxx/xxxxx/xxxx/xxxx/model/service/common/mdssys/cust/adfshare/adfshare/bc4j.xcfg.xml
/xx/xxx/xxxxx/xxxx/xxxxxxxx/service/common/mdssys/cust/adfshare/adfshare/bc4j.xcfg.xml
/xx/xxx/xxxxx/xxxx/xxxxx/service/common/mdssys/cust/adfshare/adfshare/bc4j.xcfg.xml
/xx/xxx/xxxxx/xxxx/xxxxx/xxxx/model/service/common/mdssys/cust/adfshare/adfshare/bc4j.xcfg.xml
/xx/xxx/xxxxx/xxxx/xxxxx/model/service/common/mdssys/cust/adfshare/adfshare/bc4j.xcfg.xml
Exit wlst, rerun wlst and then connect to the test environment and issue:
importMetadata(application="<Test ADF application name>", server="<Test Managed Server Name>", fromLocation="/tmp/development_mds_settings.mds", remote=true,
docs="/xx/xxx/xxxxx/xxxx/common/service/common/mdssys/cust/adfshare/adfshare/bc4j.xcfg.xml,
/xx/xxx/xxxxx/xxxx/xxxx/model/service/common/mdssys/cust/adfshare/adfshare/bc4j.xcfg.xml,
/xx/xxx/xxxxx/xxxx/xxxxxxxx/service/common/mdssys/cust/adfshare/adfshare/bc4j.xcfg.xml,
/xx/xxx/xxxxx/xxxx/xxxxx/service/common/mdssys/cust/adfshare/adfshare/bc4j.xcfg.xml,
/xx/xxx/xxxxx/xxxx/xxxxx/xxxx/model/service/common/mdssys/cust/adfshare/adfshare/bc4j.xcfg.xml,
/xx/xxx/xxxxx/xxxx/xxxxx/model/service/common/mdssys/cust/adfshare/adfshare/bc4j.xcfg.xml?")

Executing operation: importMetadata.
Metadata transfer operation started
Importing metadata into repository . . . . . . .
Metadata transfer operation completed

Operation "importMetadata" completed. Summary of "importMetadata" operation is:
6 documents successfully transferred.
List of documents successfully transferred:

/xx/xxx/xxxxx/xxxx/common/service/common/mdssys/cust/adfshare/adfshare/bc4j.xcfg.xml
/xx/xxx/xxxxx/xxxx/xxxx/model/service/common/mdssys/cust/adfshare/adfshare/bc4j.xcfg.xml
/xx/xxx/xxxxx/xxxx/xxxxxxxx/service/common/mdssys/cust/adfshare/adfshare/bc4j.xcfg.xml
/xx/xxx/xxxxx/xxxx/xxxxx/service/common/mdssys/cust/adfshare/adfshare/bc4j.xcfg.xml
/xx/xxx/xxxxx/xxxx/xxxxx/xxxx/model/service/common/mdssys/cust/adfshare/adfshare/bc4j.xcfg.xml
/xx/xxx/xxxxx/xxxx/xxxxx/model/service/common/mdssys/cust/adfshare/adfshare/bc4j.xcfg.xml

Btw. it may at times be a good idea to take a snapshot of the (customized) settings. This could be achieved by querying the table <mds prefix>_mds.mds_attributes from SQLPLUS using e.g.:

select * from <mds prefix>_mds.mds_attributes 
where att_contentid in (select path_contentid  from _mds.mds_paths where lower(path_fullname) like '%bc4j%')
and att_partition_id = ( select distinct PARTITION_ID  from _mds.mds_partitions where PARTITION_NAME='<partition name>');

However, this is not particularly readable - instead the XML can be generated using an internal MDS function for this (output using DBMS_OUTPUT so make sure server output is enabled):
set serveroutput on
begin
  <mds prefix>_mds.mds_internal_utils.printDocument('<partition name>', '/xx/xxx/xxxxx/xxxx/common/service/common/mdssys/cust/adfshare/adfshare/bc4j.xcfg.xml');
end;
/

<?xml version='1.0' encoding='UTF-8'?>
<mds:customization version="11.1.1.64.93" xmlns:mds="http://xmlns.oracle.com/mds" motype_local_name="BC4JConfig" motype_nsuri="http://xmlns.oracle.com/bc4j/configuration">
   <mds:modify 
               element="(xmlns(mds_ns1=http://xmlns.oracle.com/bc4j/configuration))/mds_ns1:BC4JConfig/mds_ns1:AppModuleConfigBag[@ApplicationName='.xx.xxx.xxxxx.xxxx.common.service.CommonUIModelService']/mds_ns1:AppModuleConfig[@name='CommonUIModelServiceLocal']/mds_ns1:AM-Pooling">
      <mds:attribute name="jbo.ampool.minavailablesize" value="0"/>
      <mds:attribute name="jbo.ampool.initpoolsize" value="0"/>
      <mds:attribute name="jbo.ampool.maxpoolsize" value="40"/>
      <mds:attribute name="jbo.ampool.maxavailablesize" value="40"/>
      <mds:attribute name="jbo.recyclethreshold" value="30"/>
   </mds:modify>
</mds:customization>

No comments: