Thursday, December 5, 2013

How to Prevent Your Application from Being Blocked by Java

Ever since we updated our Java to JRE 7.0_45, we have been seeing following security warning when starting an application with JAR file:

This application will be blocked in a future Java security update because the JAR file manifest does not contain the Permission attribute.

In this post, I will present steps to create a .jar file that has proper permission in its manifest. I will then sign the JAR file with a self-signed certificate. If you have purchased a certificate from a certificate authority, you don't need to add the certificate to key store on client's computers.

Prerequisite: you have installed JDK on the server where you perform following steps and $JDK_HOME/bin is in your PATH.

1. Create a key store containing a self-signed certificate, which will be valid for four years:

keytool -genkey -validity 1460 -alias danzheng -keystore /home/oraias/dan/sign_jar/TESTstore

What is your first and last name?
  [Unknown]: you organization_name 

(note: It is probably a good idea to use your organization’s name instead of your name, as this will be shown as the publisher of the certificate.)
What is the name of your organizational unit?
  [Unknown]:  IT Department
What is the name of your organization?
  [Unknown]:  My University
What is the name of your City or Locality?
  [Unknown]:  New York
What is the name of your State or Province?
  [Unknown]:  New York
What is the two-letter country code for this unit?
  [Unknown]:  NY
Is CN=my_school_name_here, OU=IT Department, O=My University, L=New York, ST=New York, C=NY correct?
  [no]:  yes

Enter key password for
        (RETURN if same as keystore password):


2. Put a file in a JAR file so we can sign it. Here I will create my_instatitution.jar from my school's logo, my_institution.gif:

jar cvfm my_institution.jar permission.txt my_institution.gif

Note that permission.txt is a text file with one line and ended with carriage return:

Permissions: all-permissions


3. Sign the JAR file with self-signed certificate:

jarsigner -keystore TESTstore -signedjar smy_institution.jar my_institution.jar danzheng

4. Save above signed JAR file in the directory on the webserver. For example, save smy_institution.jar in $ORACLE_HOME/forms/java on an 11g Fusion Middleware server.

5. Extract the certificate from the key store created above:

keytool -export -keystore TESTstore -alias danzheng -file teststore.cer

6. Transfer the certificate teststore.cer to the client's computer.

7. Add the certificate to the key store on the client's computer, in my case a Windows 7 64-bit PC:

Right click Command Prompt from Start --> All Programs --> Accessories and click Run as administrator
At command prompt type:

C:\PROGRA~2\Java\jre7\bin\keytool -importcert -file C:\temp\teststore.cer -keystore C:\PROGRA~2\Java\jre7\lib\security\cacerts

Note the default password of the key store cacerts is changeit.

8. You can use following command to see the list of certificates in the key store:

C:\PROGRA~2\Java\jre7\bin\keytool -list -keystore C:\PROGRA~2\Java\jre7\lib\security\cacerts

The certificate you just imported should be shown as:

mykey, Oct 31, 2013, trustedCertEntry,
Certificate fingerprint (SHA1): ........


You now can run your application containing the JAR file and it will not be blocked by java plug-in.
 

Update:


Starting with java 7 update 51 which came out January 14th. 2014, I have seen these in trace file in java console when running the Oracle forms:

Missing Application-Name manifest attribute for: http://my.school.edu:8888/forms/java/smy_institution.jar
security: Missing Codebase manifest attribute for: http://my.school.edu:8888/forms/java/smy_institution.jar
security: Missing Application-Library-Allowable-Codebase manifest attribute for: http://my.school.edu:8888/forms/java/smy_institution.jar

According to:

https://blogs.oracle.com/java-platform-group/entry/new_security_requirements_for_rias

only the Permissions attribute is required. But if you prefer not to see above messages, you could update the permission.txt file with following lines, recreate the JAR file and re-sign your jar file:

Permissions: all-permissions
Codebase: *
Application-Name: OracleForms
Application-Library-Allowable-Codebase: *


Again, the last line of the manifest file, permission.txt must end in a new line or carriage return.

References:


http://download.oracle.com/javase/1.3/docs/tooldocs/win32/keytool.html
http://download-llnw.oracle.com/javase/6/docs/technotes/tools/windows/jarsigner.html
http://docs.oracle.com/javase/7/docs/technotes/guides/jweb/manifest.html#permissions
http://docs.oracle.com/javase/tutorial/deployment/jar/modman.html
http://docs.oracle.com/javase/tutorial/deployment/jar/secman.html



How to Capture Additional Information when Your Temporary Tablespace was full with ORA-1652?

In the alert log file of one of the production Oracle databases I manage, we often see ORA-1652 error. The database is primarily an OLTP database. But it also serves application that allows database users to run ad-hoc Brio queries. When a query that access large tables without join condition or has incorrect join predicates, the query can easily take large chunk of free space in temporary tablespace.

There are two possible outcomes when it happens: the query itself could fail with ora-1652 error; or at the worst, many other transactions in the database could fail with ORA-1652 error.

Using event attribute functions of AFTER SERVERERROR database event, we can capture information about the database sessions that received the ORA-1652 error and save them in a table for later review. But what if the session that got error was not the one using large amount of space in temp tablespace? Luckily since Oracle version 11gR2, a new column: TEMP_SPACE_ALLOCATED in v$active_session_history has the information we are looking for.

%%%%%%%%%
% Setup:
%%%%%%%%%

sqlplus '/as sysdba'

drop sequence system.temp_mon_sequence;

create sequence system.temp_mon_sequence
  START WITH 100
  INCREMENT BY 1
  NOCYCLE
  NOCACHE;

create or replace table system.temp_monitor
(id number
constraint PK_TEMP_MONITOR primary key,
error_date varchar2(50),
login_user varchar2(100),
msg varchar2(4000))
tablespace users
/

create or replace trigger system.failed_to_extend_temp
  after servererror on database
   declare
      sql_text ora_name_list_t;
      error_msg varchar2(4000);
    begin
        if ( ora_is_servererror(1652) )
        then
 for i in 1 .. ora_sql_txt(sql_text) loop
     error_msg := error_msg || sql_text(i);
  end loop;
           insert into SYSTEM.temp_monitor(id,error_date,login_user,msg) values
          (SYSTEM.TEMP_MON_SEQUENCE.nextval,to_char(sysdate, 'dd-MON-yyyy,hh24:mi:ss'),ora_login_user,error_msg);
       end if;
end;
/

grant select on system.temp_monitor to public;

create public synonym temp_monitor for system.temp_monitor;

%%%%%%%%%%%%%%%%%%%%%%%%%%
% Run a test case:
%%%%%%%%%%%%%%%%%%%%%%%%%%

create temporary tablespace test_temp
tempfile '/tmp/test_temp.dbf'
size 512k reuse
extent management local
uniform size 64k
/

create user dan identified by dan
default tablespace users
temporary tablespace test_temp
/

grant connect,resource to dan;

grant select any table to dan;

Now find the largest table in the database and do a select * with order by:

connect dan/dan@orcl
SQL> select * from my_big_table
order by 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27;
select * from my_big_table
                      *
ERROR at line 1:
ORA-01652: unable to extend temp segment by 8 in tablespace TEST_TEMP
sqlplus '/as sysdba'
select * from temp_monitor;
        ID
----------
ERROR_DATE
--------------------------------------------------------------------------------
LOGIN_USER
--------------------------------------------------------------------------------
MSG
-------------------------------------------------------------------------------------------------------------------------------------------------------------
       100
07-AUG-2013,21:28:15
DAN

select * from MY_BIG_TABLE
order by 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27

Following query can be used to show sessions that have used large amount of space in temporary tablespace in last one hour. You can adjust minimum value of temp_space_allocated and sample_time to suit your needs:


SQL> select distinct session_id,sample_time,temp_space_allocated from v$active_session_history
where temp_space_allocated=(select max(temp_space_allocated) from v$active_session_history
where to_char(sample_time,'hh24') >= to_char(sysdate, 'hh24')-1)
and temp_space_allocated >1024
and to_char(sample_time,'hh24') >= to_char(sysdate, 'hh24')-1
/
SESSION_ID
----------
SAMPLE_TIME
---------------------------------------------------------------------------
TEMP_SPACE_ALLOCATED
--------------------
        18
06-AUG-13 10.00.12.331 PM
             2097152
        18
06-AUG-13 10.00.11.331 PM
             2097152

%%%%%%%%%%%%%%%%
% Automation:
%%%%%%%%%%%%%%%%

By updating the shell script presented here:

http://danthedba.blogspot.com/2009_06_01_archive.html

we can automate the process. Here is updated version of that shell script:

#!/bin/sh
#
# Author: Dan Zheng, danthedba.blogspot.com
# June 2009
# This script checks database alert/log.xml file and email ORA error.
# It can be used on a Oracle database version 11g and higher.
#
# Before running this script for the first time, please create directory:
# $ORACLE_BASE/admin/cronjob_scripts/dbmonitor for files used by this script.
#
# August 2013: updated to collect additional information related to ORA-1652.
# Please make sure following database objects are created and proper privileges granted:
# Sequence: system.temp_mon_sequence
# Table:    system.temp_monitor
# Trigger:  system.failed_to_extend_temp
# Set up environment.
# Make necessary change on following line based on your Unix flavor:
#set -x
if [ $# != 1 ]
then
echo run this script as following:
echo $0 ORACLE_SID
exit 1
fi
. ~oracle/.bash_profile
export ORACLE_SID=$1
export TIMESTAMP=`date +%M%H%d%m%y`;
export mon_dir=/u01/app/oracle/dan
export fn=$mon_dir/email_body_${ORACLE_SID}_2.tmp
export alertck=$mon_dir/alertck_${ORACLE_SID}_2.log
touch $alertck
touch $fn
touch $mon_dir/donot_delete_${ORACLE_SID}_2.log
EMAIL='dan_zheng@danthedba.blogspot.com'

ORACLE_BASE
oracle_sid=$( echo "${ORACLE_SID}" |tr '[:upper:]' '[:lower:]')
adrci <spool $mon_dir/alert_${TIMESTAMP}_${ORACLE_SID}.log
set termout off
set homepath diag/rdbms/$oracle_sid/${ORACLE_SID}
SHOW ALERT -P "MESSAGE_TEXT LIKE '%ORA-%'" -term
spool off
exit
EOF

export c_log=`wc -l $mon_dir/alert_${TIMESTAMP}_${ORACLE_SID}.log | awk '{ print $1 }'`
export c_tmp=`wc -l $mon_dir/donot_delete_${ORACLE_SID}_2.log | awk '{ print $1 }'`
echo $c_log
echo $c_tmp
if (($(($c_log)) > $(($c_tmp)))); then
comm -23 $mon_dir/alert_${TIMESTAMP}_${ORACLE_SID}.log $mon_dir/donot_delete_${ORACLE_SID}_2.log | grep ORA- |sort -u > $alertck
exec 3< $alertck
# read until the end of the file
 until [ $done ]
 do
   read <&3 ERR_LINE
   if [ $? != 0 ]
   then done=1
    continue
   fi
  echo $ERR_LINE >> $fn
  export ERR_NO=`echo $ERR_LINE |awk '{ print $1 }'|awk -F- '{ print $2 }'| awk -F: '{ print $1 }'`
 echo " Oracle error is : ORA-$ERR_NO"
  oerr ora $ERR_NO >> $fn
  echo "  " >> $fn
 if [ $ERR_NO -eq 1652 ]
 then
sqlplus -s "/as sysdba" <>$fn
column ora_login_user format a100 head 'Person who runs the offending query that caused ORA-1652 error:'
column msg format a150 head 'Offending query:'
column program format a30
set linesize 200


select ora_login_user from system.temp_monitor
where id=(select max(id) from system.temp_monitor);
select msg from system.temp_monitor
where id=(select max(id) from system.temp_monitor);
set heading off
select 'Users who are using more than 1GB of temporary tablespace in the last one hour:' from dual;
set heading on
select a.sid,a.serial#,a.username,a.program,b.temp_space_allocated/1024/1024 "temp space used (MB)",b.sample_time
from v\$active_session_history b,v\$session a
where b.temp_space_allocated=(select max(temp_space_allocated) from v\$active_session_history
where to_char(sample_time,'hh24') >= to_char(sysdate, 'hh24')-1)
and b.temp_space_allocated/1024/1024 >1000
and to_char(b.sample_time,'hh24') >= to_char(sysdate, 'hh24')-1
and b.session_id=a.sid
and b.session_serial#=a.serial#;
exit
EOF
 fi

  echo "-----------------------------------------------------" >> $fn
  echo "  " >> $fn
 done
echo "ERRORS: sending mail!"
SUBJECT="${ORACLE_SID} - ORA Error Message found in alert log file log.xml at `date`"
echo $SUBJECT
# From a HPUX server, use following line to send email:
#cat $fn | /usr/bin/mailx -s "$SUBJECT" $EMAIL
# from a Linux server, use following line to send email:
cat $fn | mail -s "$SUBJECT" $EMAIL
mv $mon_dir/alert_${TIMESTAMP}_${ORACLE_SID}.log $mon_dir/donot_delete_${ORACLE_SID}_2.log
else
echo "No Oracle error is found in alert log log.xml since last time this script was run."
fi

rm $fn
echo "The script was executed successfully."
%%%%%%%%%%%%%%%
% Clean up:
%%%%%%%%%%%%%%%
sqlplus '/as sysdba'
drop user dan cascade;
drop tablespace test_temp including contents and datafiles;
--- If you want to completely remove the objects related to this setup:
drop public synonym temp_monitor;
drop trigger system.failed_to_extend_temp;
drop table system.temp_monitor cascade constraints;
drop sequence system.temp_mon_sequence;

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
References:
1. http://download.oracle.com/docs/cd/B19306_01/appdev.102/b14251/adfns_triggers.htm#CHDCFDJG
2. http://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:374218170986

Tuesday, July 9, 2013

Setting up Oracle Gateway for ODBC for Connections between an Oracle Database and a MySQL Database


In this demo, I will setup 11gR2 Oracle gateway for ODBC to connect between a version 11.2.0.3 Oracle database and a version 5.0.29 MySQL database on different machines.. The Oracle database is on a 64-bit Red Hat Enterprise Linux 5 server, while MySQL database is on a Windows server. I will provide some tips and tricks along the way.

Unless specified, all the steps are performed on the Oracle gateway server, which is also on a Red Hat Enterprise Linux 5 server.

1. Install 11gR2 Oracle gateway
 
file used: linux.x64_11gR2_gateways.zip.

   Follow the steps listed in Oracle documentation, as seen reference below. The gateway home (or $ORACLE_HOME) is:
    
   /opt/app/oracle/product/11.2.0/gateway/

2. Create a new directory for both ODBC driver manager and ODBC driver:

              cd $ORACLE_HOME
              mkdir app

3. Download and install UnixODBC, or ODBC driver manager:

 
  a. Download unixODBC-2.2.14-linux-x86-64.tar.gz from http://sourceforge.net/projects/unixodbc/files/unixODBC/

  b. Install ODBC driver manager:
              cd $ORACLE_HOME/app
              $mkdir unixODBC-2.2.14
              cd unixODBC-2.2.14
              unzip unixODBC-2.2.14-linux-x86-64.tar.gz
              mv usr/local/* .
              rm -r usr

4. Download and install ODBC driver, also called MySQL Connector:
 
  a. Download from: http://dev.mysql.com/doc/refman/5.5/en/connector-odbc-installation-binary-unix.html 

File to download: mysql-connector-odbc-5.1.12-linux-glibc2.5-x86-64bit.tar.gz
 
  b. Install the ODBC driver:
              cd $ORACLE_HOME/app
              unzip mysql-connector-odbc-5.1.12-linux-glibc2.5-x86-64bit.tar.gz
              ls -s mysql-connector-odbc-5.1.12-linux-glibc2.5-x86-64bit odbc-5.1.12

We now have the ODBC driver manager in:

/opt/app/oracle/product/11.2.0/gateway/app/unixodbc-2.2.14

and the ODBC driver in:

/opt/app/oracle/product/11.2.0/gateway/app/myodbc-5.1.12

5. Configure ODBC data source file for ODBC driver:
 
  a. Create the etc directory under the $ORACLE_HOME:
              cd $ORACLE_HOME
              mkdir etc

  b. Create the odbc.ini file in etc directory:
$more odbc.ini
[myodbc5]
Driver = /opt/app/oracle/product/11.2.0/gateway/app/myodbc-5.1.12/lib/libmyodbc5.so
Description = Connector/ODBC 5.1 Driver DSN
SERVER =ip_address_of_mysql_server
PORT = 3306
USER = user1  (note: this is username used to log in MySQL database)
PASSWORD = password1  (note: this is the password of user1)
DATABASE = mydb  (note: this is the name of MySQL database, case sensitive)
OPTION = 0
TRACE = OFF

 6. Verifying the ODBC connection:
 
  a. Grant permissions to allow remote access to MySQL database:
      On MySQL database server:
              mysql -u root -p
              Enter password:
mysql> use mysql
mysql> GRANT ALL PRIVILEGES ON mydb1 TO 'user1'@'ip_address_of_gateway_server' IDENTIFIED BY 'password1';
mysql> FLUSH PRIVILEGES;

  b. On the gateway server:
export ODBCINI=/opt/app/oracle/product/11.2.0/gateway/etc/odbc.ini
 export LD_LIBRARY_PATH=/opt/app/oracle/product/11.2.0/gateway/app/unixodbc-2.2.14/lib:$LD_LIBRARY_PATH
 cd /opt/app/oracle/product/11.2.0/gateway/app/unixodbc-2.2.14/bin/
 ./isql myodbc5 -v
+---------------------------------------+
| Connected!                            |
|                                       |
| sql-statement                         |
| help [tablename]                      |
| quit                                  |
|                                       |
+---------------------------------------+
SQL> select version()
+----------+
| version()|
+----------+
| 5.0.95   |
+----------+
SQLRowCount returns 1
1 rows fetched
SQL>quit

7. Configure Oracle Net Listener for the gateway:
 
Modify the listener.ora created by gateway installer. For this setup, the listener name is set to MYSQLGWAY and port# is 1523.

cd /opt/app/oracle/product/11.2.0/gateway/network/admin
$more listener.ora
# listener.ora Network Configuration File: /opt/app/oracle/product/11.2.0/gateway/network/admin/listener.ora
# Generated by Oracle configuration tools.
ADR_BASE_MYSQLGWAY = /opt/app/oracle/product/11.2.0/gateway

MYSQLGWAY =
  (DESCRIPTION_LIST =
    (DESCRIPTION =
      (ADDRESS = (PROTOCOL = TCP)(HOST = ip_address_of_gateway_server)(PORT = 1523))
    )
  )

SID_LIST_MYSQLGWAY=
(SID_LIST=
 (SID_DESC=
  (SID_NAME=mydb)
  (ORACLE_HOME=/opt/app/oracle/product/11.2.0/gateway)
  (PROGRAM=dg4odbc)
  (ENVS=LD_LIBRARY_PATH=/opt/app/oracle/product/11.2.0/gateway/app/myodbc-5.1.12/lib:/opt/app/oracle/product/11.2.0/gateway/app/unixodbc-2.2.14/lib:/opt/app/oracle/product/11.2.0/gateway/lib)
 )
)

Note: make sure lines follows DESCRIPTION_LIST and SID_LIST are indented. Otherwise, Oracle may not be able to read the file correctly.

8. Add an entry to the tnsnames.ora file for the gateway on the database server:
 
The database could be on the same server as the gateway server, or on a different server.
Add following lines to $ORACLE_HOME/network/admin/tnsnames.ora on the database server:

mysqlmydb =
(DESCRIPTION=
 (ADDRESS=(PROTOCOL=TCP)(HOST=ip_address_of_gateway_server)(PORT=1523))
 (CONNECT_DATA=(SID=mydb))
 (HS=OK)
)

Note: again make sure lines follow DESCRIPTION are indented.

9. Configure the Gateway Initialization Parameter File:
 
Make a copy of sample initialization parameter file initdg4odbc.ora and rename it to include sid of MySQL database (in our case, it is mydb).

cd /opt/app/oracle/product/11.2.0/gateway/hs/admin
cp initdg4odbc.ora initmydb.ora
$more initmydb.ora
# This is a sample agent init file that contains the HS parameters that are
# needed for the Database Gateway for ODBC
#
# HS init parameters
#
HS_FDS_CONNECT_INFO=myodbc5
#HS_FDS_TRACE_LEVEL =
HS_FDS_SHAREABLE_NAME =/opt/app/oracle/product/11.2.0/gateway/app/unixodbc-2.2.14/lib/libodbc.so
#
# Based on metalink note 1320645.1,
# UnixODBC (ODBC Driver Manager) might not support any character set
# used in oracle, so the connection string will be corrupted.
# To avoid the connection string corruption it is recommended to set
# HS_LANGUAGE parameter.
#
HS_LANGUAGE=AMERICAN_AMERICA.WE8ISO8859P15
#
# ODBC specific environment variables
#
set ODBCINI=/opt/app/oracle/product/11.2.0/gateway/etc/odbc.ini
#
# Environment variables required for the non-Oracle system
#
#set =
set LD_LIBRARY_PATH=/opt/app/oracle/product/11.2.0/gateway/app/unixodbc-2.2.14/lib


10. Restart the listener:
 
export ORACLE_HOME=/opt/app/oracle/product/11.2.0/gateway
lsnrctl start MYSQLGWAY

11. Create a database link in Oracle database to connect to MySQL database:
 
Create public database link mysqlmydb connect to "user1" identified by "password1" using 'mysqlmydb';

Note: because MySQL is case sensitive, make sure to use double quotes on username and password. Otherwise, you may get following error:

ERROR at line 1:
ORA-28500: connection from ORACLE to a non-Oracle system returned this message:
[unixODBC][MySQL][ODBC 5.1 Driver]Access denied for user
'USER1'@'XXXX' (using password: YES) {HY000,NativeErr = 1045}
ORA-02063: preceding 2 lines from MYSQLMYDB

12. Test the db link from Oracle to MySQL:
 
select count(*) from "tbl1"@mysqlmydb;
  COUNT(*)
----------
              8


References:
1. Installing Oracle Database Gateway for ODBC: http://docs.oracle.com/cd/E11882_01/gateways.112/e12013/installodbc.htm#BHCJEEBA
2. Oracle support note #1320645.1 Detailed Overview of Connecting Oracle to MySQL Using DG4ODBC Database Link
3. Troubleshooting ORA-28500 error: https://forums.oracle.com/thread/2227263

Tuesday, June 18, 2013

Replicating Oracle ANYDATA datatype

At time of this writing, neither SharePlex for Oracle nor Oracle GoldenGate support replicating an Oracle table that has ANYDATA datatype.

I have developed a workaround for this. I first created a shadow table of the table with ANYDATA datatype in both source and target database. The shadow table does not have a column with ANYDATA datatype. I then created a trigger on the table with ANYDATA datatype in source database, and a trigger on shadow table in target database. After populating the shadow table with all the data from the source table, I setup to replicate the shadow table. The triggers will guarantee that any changes at source will be replicated to the target. Here is an example.
  
1. In the source database, create a table with ANYDATA datatype and populate the table:

create table t_my_anydata
(id1 number not null,
 id2 number not null,
 colA varchar2(100 CHAR),
 data anydata,
 constraint "PK_ID" primary key (id1, id2) using index
);

insert into t_my_anydata values (10,11,'A',anydata.convertnumber(1));
insert into t_my_anydata values (20,21,'B',anydata.ConvertDate(sysdate));
insert into t_my_anydata values (30,31,'C',anydata.convertvarchar2('test1'));
insert into t_my_anydata(id1,id2,colA) values (40,41,'D');
commit;

2. Use following query to check what is in the table:

column DATA_NUM format 99
column DATA_VARCHAR format a14
column colA format a20
set linesize 300

select  id1,
        id2,
                 colA,
        CASE WHEN anydata.gettypename(data) = 'SYS.NUMBER'
                  THEN  anydata.AccessNumber(data)  
        END  data_num,
        CASE WHEN anydata.gettypename(data) = 'SYS.DATE'
                  THEN  anydata.AccessDate(data)  
        END  data_date,
        CASE WHEN anydata.gettypename(data) = 'SYS.VARCHAR2'
                  THEN  anydata.AccessVarchar2(data)  
        END  data_varchar
from t_my_anydata; 

3. In the source database, create a shadow table:

create table t_my_anydata_s
(id1 number not null,
 id2 number not null,
 colA varchar2(100 CHAR),
 data_num number,
 data_date date,
 data_varchar varchar2(400 CHAR),
constraint "PK_ID_S" primary key (id1, id2) using index
);

4. Populate shadow table with data from t_my_anydata table:

insert into t_my_anydata_s
select  id1,
                id2,
                colA,
        CASE WHEN anydata.gettypename(data) = 'SYS.NUMBER'
                  THEN  anydata.AccessNumber(data)  
        END  data_num,
        CASE WHEN anydata.gettypename(data) = 'SYS.DATE'
                  THEN  anydata.AccessDate(data)  
        END  data_date,
        CASE WHEN anydata.gettypename(data) = 'SYS.VARCHAR2'
                  THEN  anydata.AccessVarchar2(data)  
        END  data_varchar
from t_my_anydata;       

COMMIT;

5. Repeat step 1 to 4 above in the target database, create and populate both table t_my_anydata and t_my_anydata_s.

6. Create a trigger on table t_my_anydata in source database to keep t_my_anydata and t_my_anydata_s tables in sync:

CREATE OR REPLACE TRIGGER t_my_anydata_trig
AFTER DELETE OR INSERT OR UPDATE ON t_my_anydata
REFERENCING NEW AS NEW OLD AS OLD
FOR EACH ROW

BEGIN

IF deleting THEN
 DELETE FROM t_my_anydata_s
 WHERE id1= :OLD.id1
 AND id2= :OLD.id2;
ELSIF inserting THEN
  INSERT INTO t_my_anydata_s values
    (:NEW.id1,
     :NEW.id2,
     :NEW.colA,
     (CASE WHEN anydata.gettypename(:NEW.data) = 'SYS.NUMBER'
     THEN  anydata.AccessNumber(:NEW.data)
      END),
     (CASE WHEN anydata.gettypename(:NEW.data) = 'SYS.DATE'
     THEN  anydata.AccessDate(:NEW.data)
      END),
     (CASE WHEN anydata.gettypename(:NEW.data) = 'SYS.VARCHAR2'
     THEN  anydata.AccessVarchar2(:NEW.data)
      END)
    );
ELSIF updating THEN
   UPDATE t_my_anydata_s
   SET id1 = :NEW.id1,
       id2 = :NEW.id2,
              colA = :NEW.colA,
       data_num = (CASE WHEN anydata.gettypename(:NEW.data) = 'SYS.NUMBER'
     THEN  anydata.AccessNumber(:NEW.data)
      END),
       data_date = (CASE WHEN anydata.gettypename(:NEW.data) = 'SYS.DATE'
     THEN  anydata.AccessDate(:NEW.data)
      END),
       data_varchar = (CASE WHEN anydata.gettypename(:NEW.data) = 'SYS.VARCHAR2'
     THEN  anydata.AccessVarchar2(:NEW.data)
      END)
   WHERE id1 = :OLD.id1
   AND id2 = :OLD.id2;
END IF;
END;
/
  
7. Create a trigger on t_my_anydata_s on target database to keep t_my_anydata_s and t_my_anydata tables in scync:

CREATE OR REPLACE TRIGGER t_my_anydata_s_trig
AFTER DELETE OR INSERT OR UPDATE ON t_my_anydata_s
REFERENCING NEW AS NEW OLD AS OLD
FOR EACH ROW
DECLARE
mydata1             ANYDATA;
BEGIN

IF :NEW.data_num is not null THEN
              mydata1 := anydata.convertnumber(:NEW.data_num);
ELSIF :NEW.data_date is not null THEN
              mydata1 := anydata.ConvertDate(:NEW.data_date);
ELSE
              mydata1 := anydata.convertvarchar2(:NEW.data_varchar);
END IF;

IF deleting THEN
 DELETE FROM t_my_anydata
 WHERE id1 = :OLD.id1
 AND id2 = :OLD.id2;
ELSIF inserting THEN
  INSERT INTO t_my_anydata values
    (:NEW.id1,
     :NEW.id2,
     :NEW.colA,
      mydata1
    );
ELSIF updating THEN
   UPDATE t_my_anydata
   SET id1 = :NEW.id1,
       id2 = :NEW.id2,
       colA = :NEW.colA,
       data = mydata1
   WHERE id1 = :OLD.id1
   AND id2 = :OLD.id2;
END IF;
END;
/

You can modify above scripts to support additional datatypes that is wrapped in ANYDATA datatype. Please note, as of Oracle version 11gR2, the only supported LOB datatype embedded in ANYDATA is BFILE.

8. Setup replication for table t_my_anydata_s. I will use SharePlex for Oracle in this example:

a. Enable supplemental logging:

sqlplus / as sysdba

ALTER DATABASE ADD SUPPLEMENTAL LOG DATA (PRIMARY KEY) COLUMNS;

ALTER SYSTEM SWITCH LOGFILE;

b. Make sure SharePlex is running. If not, starts it:

su - splex

ps -ef|grep sp_

/opt/SharePlex/productdir/bin
-sh-3.2$ ./sp_cop &

c. Create a new config file for replicating table t_my_anydata_s

cd /opt/SharePlex/vardir/config

>more anydata_config
datasource:o.SOURCEDB
zhengd.t_my_anydata_s           zhengd.t_my_anydata_s         targetsys@o.TARGETDB

d. Activate the cofiguration for table t_my_anydata_s

./sp_ctrl
sp_ctrl > list config

This will list all the configuration files and activation status. Make sure no configuration is activated, since SharePlex can only have one active configuration.

sp_ctrl > activate config anydata_config

9. Run tests to check if changes to the table t_my_anydata are replicated:

a. Insert:

SQL> insert into t_my_anydata values (50,51,'E',anydata.convertvarchar2('test2'));
1 row created.
SQL>commit;
Commit complete.

Check at source to see if insert is done on the table:

SQL> column DATA_NUM format 99
column DATA_VARCHAR format a14
column colA format a20
set linesize 300
SQL> select  id1,
  2          id2,
  3          colA,
  4          CASE WHEN sys.anydata.gettypename(data) = 'SYS.NUMBER'
  5         THEN  sys.anydata.AccessNumber(data)
  6          END  data_num,
  7          CASE WHEN sys.anydata.gettypename(data) = 'SYS.DATE'
  8         THEN  sys.anydata.AccessDate(data)
  9          END  data_date,
 10          CASE WHEN sys.anydata.gettypename(data) = 'SYS.VARCHAR2'
 11         THEN  sys.anydata.AccessVarchar2(data)
 12          END  data_varchar
 13  from t_my_anydata;

       ID1        ID2 COLA                 DATA_NUM DATA_DATE DATA_VARCHAR
---------- ---------- -------------------- -------- --------- --------------
        50         51    E                                       test2
        10         11    A                           1
        20         21    B                             11-JUL-12
        30         31    C                                       test1
        40         41    D

Check the same table on the target:

SQL> select  id1,
  2          id2,
  3          colA,
  4          CASE WHEN sys.anydata.gettypename(data) = 'SYS.NUMBER'
  5         THEN  sys.anydata.AccessNumber(data)
  6          END  data_num,
  7          CASE WHEN sys.anydata.gettypename(data) = 'SYS.DATE'
  8         THEN  sys.anydata.AccessDate(data)
  9          END  data_date,
 10          CASE WHEN sys.anydata.gettypename(data) = 'SYS.VARCHAR2'
 11         THEN  sys.anydata.AccessVarchar2(data)
 12          END  data_varchar
 13  from t_my_anydata;

       ID1        ID2 COLA                 DATA_NUM DATA_DATE          DATA_VARCHAR
---------- ---------- -------------------- -------- ------------------ --------------
        50         51      E                                                test2
        10         11      A                           1
        20         21      B                             11-JUL-12
        30         31      C                                                test1
        40         41      D

b. Update:

Now do a update at source:

SQL> update t_my_anydata set data=anydata.convertnumber(3)
where id1=10;
 
1 row updated.

SQL> commit;

Commit complete.

Check both source and target again:

At source:
SQL> select  id1,
  2      id2,
  3       colA,
  4          CASE WHEN sys.anydata.gettypename(data) = 'SYS.NUMBER'
  5         THEN  sys.anydata.AccessNumber(data)
  6          END  data_num,
  7          CASE WHEN sys.anydata.gettypename(data) = 'SYS.DATE'
  8         THEN  sys.anydata.AccessDate(data)
  9          END  data_date,
 10          CASE WHEN sys.anydata.gettypename(data) = 'SYS.VARCHAR2'
 11         THEN  sys.anydata.AccessVarchar2(data)
 12          END  data_varchar
 13  from t_my_anydata;

       ID1        ID2 COLA                 DATA_NUM DATA_DATE DATA_VARCHAR
---------- ---------- -------------------- -------- --------- --------------
        50         51      E                                       test2
        10         11      A                           3
        20         21      B                             11-JUL-12
        30         31      C                                       test1
        40         41      D

At the target:

SQL> select  id1,
  2     id2,
  3          colA,
  4          CASE WHEN sys.anydata.gettypename(data) = 'SYS.NUMBER'
  5         THEN  sys.anydata.AccessNumber(data)
  6          END  data_num,
  7          CASE WHEN sys.anydata.gettypename(data) = 'SYS.DATE'
  8         THEN  sys.anydata.AccessDate(data)
  9          END  data_date,
 10          CASE WHEN sys.anydata.gettypename(data) = 'SYS.VARCHAR2'
 11         THEN  sys.anydata.AccessVarchar2(data)
 12          END  data_varchar
 13  from t_my_anydata;

       ID1        ID2 COLA                 DATA_NUM DATA_DATE          DATA_VARCHAR
---------- ---------- -------------------- -------- ------------------ --------------
        50         51      E                                                test2
        10         11      A                           3
        20         21      B                             11-JUL-12
        30         31      C                                                test1
        40         41      D

c. Delete:

New do a delete at the source:

SQL> delete from t_my_anydata
where id2=51; 

1 row deleted.

SQL> commit;

Commit complete.

Now Check both source and target:

At the source:

SQL> select  id1,
  2         id2,
  2          colA,
  4          CASE WHEN sys.anydata.gettypename(data) = 'SYS.NUMBER'
  5         THEN  sys.anydata.AccessNumber(data)
  6          END  data_num,
  7          CASE WHEN sys.anydata.gettypename(data) = 'SYS.DATE'
  8         THEN  sys.anydata.AccessDate(data)
  9          END  data_date,
 10          CASE WHEN sys.anydata.gettypename(data) = 'SYS.VARCHAR2'
 11         THEN  sys.anydata.AccessVarchar2(data)
 12          END  data_varchar
 13  from t_my_anydata;

       ID1        ID2 COLA                 DATA_NUM DATA_DATE DATA_VARCHAR
---------- ---------- -------------------- -------- --------- --------------
        10         11      A                           3
        20         21      B                             11-JUL-12
        30         31      C                                       test1
        40         41      D

At the target:

SQL> select  id1,
  2          id2,
  3          colA,
  4          CASE WHEN sys.anydata.gettypename(data) = 'SYS.NUMBER'
  5         THEN  sys.anydata.AccessNumber(data)
  6          END  data_num,
  7          CASE WHEN sys.anydata.gettypename(data) = 'SYS.DATE'
  8         THEN  sys.anydata.AccessDate(data)
  9          END  data_date,
 10          CASE WHEN sys.anydata.gettypename(data) = 'SYS.VARCHAR2'
 11         THEN  sys.anydata.AccessVarchar2(data)
 12          END  data_varchar
 13  from t_my_anydata;

       ID1        ID2 COLA                 DATA_NUM DATA_DATE          DATA_VARCHAR
---------- ---------- -------------------- -------- ------------------ --------------
        10         11      A                           3
        20         21      B                             11-JUL-12
        30         31      C                                                test1
        40         41      D

Reference: metalink note:   Oracle GoldenGate database Complete Database Profile check script for Oracle DB (All Schemas) [ID 1298562.1]
 
 
Update:
As of version 7.6.3., SharePlex for Oracle supports replicating ANYDATA datatype if you set this in your environment before starting the daemon for replication:
 
SP_OCF_ANYDATA_TYPE_INVALID=0