Sunday, March 18, 2012

Installfest: Oracle 11g XE on Amazon Linux Micro Instance

This is just a quick (as in "I really, really mean it quick") note on installing Oracle 11g XE on Amazon EC2 on a vanilla Amazon Linux AMI as a micro instance. Cloudy...

Why Amazon Linux? It's easy and it's pre configured with most of what you need. Both cloud wise and for other things. It also gets good reviews as a production OS.

No, Oracle 11g XE on Amazon Linux this is not supported by Oracle. But then again, you will receive no official support on Oracle 11g XE by Oracle on any OS...

I have not validated if every step described below is necessary, by I reasoned that the standard install steps will stand, even if they upgrade the default Amazon Linux AMI.

If you have no experience with Linux or Amazon EC2, I think you will be better off with a more verbose installation guide than this is!

Create the Instance
If I am unclear (or just a tad abbreviated), just follow this documentation at any time.
  • Launch new instance, and choose "Basic Amazon Linux AMI YYYY.MM"
  • Choose instance type micro, set or create key pairs and set or create security group (everything else, just keep default)
Your are done creating the instance.

Connect to the Instance
I use PuTTY, for alternatives or a more elaborate description of how to connect with PuTTY, see this documentation. I assume you have PuTTY suite (with PuTTYGen) installed.

You also have to check that port 22 is open for the security group associated with the image.
  • Start PuTTYGen
  • Load the .pem file created from then key pair in use with the instance (switch to "all files" to see it)
  • Click Save Private Key, and Yes
  • Exit PuTTYGen
  • Start PuTTY
  • Copy Public DNS from EC2 Console
  • Paste Public DNS into Host Name in PuTTY Configuration
  • On left side, go to Connection -> SSH -> Auth and browse to the location of the previously generated ppk-file
  • Back to Session, give it a name  in "Saved Sessions" and Save
  • Click Open and Yes in the dialog
  • Connect as ec2-user
You are done connecting to your new instance.

Configure EC2 Tools
This you can do on your host machine, or the newly created instance. I will describe how for the newly created instance (less work for me, but also less secure for you!).
  • Log in as ec2-user
Configure root user:
  • sudo su - root
  • passwd <new password>
  • exit
  • su - root
  • <new password>
  • id
EC2 Tools are installed by default, but you have to let it know which credentials to use. Copy the pk and cert files into /root directory, and modify .bash_profile:
  • su - root
  • vi ./.bash_profile
  • export EC2_PRIVATE_KEY=/root/pk-<your pk>.pem
  • export EC2_CERT=/root/cert-<your pem>.pem
  • export EC2_URL=https://ec2.<region>.amazonaws.com
I use eu-west-1 as region. You should now be able to issue
  • ec2-describe-availability-zones
And receive a list of zones.

Configure Instance Storage
First, create and attach a new volume (30Gb) to the instance:
  • su - root 
  • ec2-describe-instances 
  • Take a not of the instance id and availability zone 
  • ec2-create-volume --size 30 --availability-zone <zoneSameAsTheInstance>
  • Take a note of the volume created
  • ec2-attach-volume <yourVolumeId> -i <yourInstanceId> -d <yourDeviceName>
  • Like this: ec2-attach-volume vol-xxxxxx -i i-xxxxxx -d /dev/sdf
  • ec2-describe-volumes
Format volume and make it stick on reboot:
  • mkfs -t ext3 /dev/sdf
  • tune2fs -m0 /dev/sdf
  • mkdir /u01
  • mount /dev/sdf /u01
  • vi /etc/fstab
  • Add the following line:
  • /dev/sdf    /u01        auto    defaults,noatime 0 0
Increase swap file (using the root device):
  • dd if=/dev/zero of=/home/swapfile bs=1024 count=2097152
  • mkswap /home/swapfile
  • vi /etc/fstab
  • Add the following line:
  • /home/swapfile swap swap defaults 0 0
This is a bit of a cheat, because the default swap disk will still attach, you cat alter that later.

Set host name for the server:
  • vi /etc/sysconfig/network
  • HOSTNAME=<yourHostName>
  • :wq
  • vi /etc/hosts
  • Add  <yourHostName> to the line containing 127.0.0.1
  • :wq
Restart to check if everything is OK:
  • shutdown -r now
At this stage I created a new AMI based on the current configuration (with the new 30Gb volume still attached). You never know :-)

Configure Instance for Oracle 11g XE
Since we can't use yum oracle-validated on the Amazon Linux, we will have to do this the hard way.

Create oracle user++:
  • su - root
  • groupadd oinstall
  • groupadd dba
  • useradd -g oinstall -G dba,oinstall oracle
  • passwd oracle
  • chown oracle:oinstall /u01
Get the latest packages:
  • yum install binutils-2.*
  • yum install compat-libstdc++-33*
  • yum install elfutils-libelf-0.*
  • yum install elfutils-libelf-devel-*
  • yum install gcc-4.*
  • yum install gcc-c++-4.*
  • yum install glibc-2.*
  • yum install glibc-common-2.*
  • yum install glibc-devel-2.*
  • yum install glibc-headers-2.*
  • yum install ksh-2*
  • yum install libaio-0.*
  • yum install libaio-devel-0.*
  • yum install libgcc-4.*
  • yum install libstdc++-4.*
  • yum install libstdc++-devel-4.*
  • yum install make-3.*
  • yum install sysstat-7.*
  • yum install unixODBC-2.*
  • yum install unixODBC-devel-2.*
Check the parameter settings:
  • /sbin/sysctl -a | grep fs.suid_dumpable
  • /sbin/sysctl -a | grep fs.aio-max-nr
  • /sbin/sysctl -a | grep fs.file-max
  • /sbin/sysctl -a | grep kernel.shmall
  • /sbin/sysctl -a | grep kernel.shmmax
  • /sbin/sysctl -a | grep kernel.shmmni
  • /sbin/sysctl -a | grep kernel.sem
  • /sbin/sysctl -a | grep net.ipv4.ip_local_port_range
  • /sbin/sysctl -a | grep net.core.rmem_default
  • /sbin/sysctl -a | grep net.core.rmem_max
  • /sbin/sysctl -a | grep net.core.wmem_default
  • /sbin/sysctl -a | grep net.core.wmem_max
Compare it to:
  • fs.suid_dumpable = 1
  • fs.aio-max-nr = 1048576
  • fs.file-max = 6815744
  • kernel.shmall = 2097152
  • kernel.shmmax = 536870912
  • kernel.shmmni = 4096
  • kernel.sem = 250 32000 100 128
  • net.ipv4.ip_local_port_range = 9000 65500
  • net.core.rmem_default = 262144
  • net.core.rmem_max = 4194304
  • net.core.wmem_default = 262144
  • net.core.wmem_max = 1048586
In my image, I had to add the following lines to sysctl.conf:
  • vi /etc/sysctl.conf
  • # Added by a Monkey
  • fs.suid_dumpable = 1
  • fs.aio-max-nr = 1048576
  • fs.file-max = 6815744
  • kernel.shmmni = 4096
  • kernel.sem = 250 32000 100 128
  • net.ipv4.ip_local_port_range = 9000 65500
  • net.core.rmem_default = 262144
  • net.core.rmem_max = 4194304
  • net.core.wmem_default = 262144
  • net.core.wmem_max = 1048586
Well, obviously not the first bullet point ;)

Configure the rest for oracle in security limits:
  • vi /etc/security/limits.conf
Added:
  • oracle              soft    nproc   2047
  • oracle              hard    nproc   16384
  • oracle              soft    nofile  1024
  • oracle              hard    nofile  65536
  • oracle              soft    stack   10240
Apply the changes:
  • /sbin/sysctl -p
Done so far!

Downloading and Installing Oracle 11g XE
Create download directory:
  • su - oracle
  • mkdir /u01/download
  • cd /u01/download
I couldn't get wget to download XE, so I had to resort to a cheat:
  • Log into oracle.com on your HOST machine with a REAL browser
  • Find download on oracle.com
  • Accept licence agreement
  • Click link to download
  • Capture and copy request (network tab in Chrome will give you that):
    http://download.oracle.com/otn/linux/oracle11g/xe/oracle-xe-11.2.0-1.0.x86_64.rpm.zip?AuthParam=<somestring>
  • Paste it into ssh session:
  • wget http://download.oracle.com/otn/linux/oracle11g/xe/oracle-xe-11.2.0-1.0.x86_64.rpm.zip?AuthParam=<someString>  -O oracle-xe-11.2.0-1.0.x86_64.rpm.zip
  • And the file will be downloaded
I had some initial problems with this, and had to go back to the browser, retrace my steps for yet another download and get a new request from the browser. Worked the second time, don't know why. Maybe because I downloaded with wget while downloading at the same time from my aws instance..?

Installing is much easier:
  • su - root
  • unzip oracle-xe-11.2.0-1.0.x86_64.rpm.zip
  • cd /u01/download/Disk1
  • rpm -ivh oracle-xe-11.2.0-1.0.x86_64.rpm
  • /etc/init.d/oracle-xe configure
  • Accept the defaults (unless you know what you are doing!)
And you are done installing Oracle 11g XE! If you want to test APEX, just open port 8080 in the security group in the AWS EC2 Console, and use the public dns for the instance to reach it.

Configure Oracle User Environment
Just a touch if you are going to use oracle user in ssh later:
  • su - oracle
  • cat /u01/app/oracle/product/11.2.0/xe/bin/oracle_env.sh
  • <copy the lines below into the .bash_profile>
  • export ORACLE_HOME=/u01/app/oracle/product/11.2.0/xe
  • export ORACLE_SID=XE
  • export NLS_LANG=`$ORACLE_HOME/bin/nls_lang.sh`
  • export PATH=$ORACLE_HOME/bin:$PATH
  • cd
  • vi .bash_profile
  • <paste the lines above>
  • :wq
Tying it Up
At this point, you should restart to verify that everything works, create a new AMI based on the current state of the instance, enable ssh connect with oracle user, reconfigure swap space, etc, etc.

Remember not to use EBS storage for anything you really care for, data should be backed up in a safe manner (as you normally would do anyway).

Monday, March 5, 2012

The Tale of the Read Only Input Text Item

It always starts out easy:
"If variable x has value y, it is forbidden to update values in column z."
Easy! ADF is well suited for the task. Some Groovy based business rules on the entity, and it just works. Simple, declarative, transparent and instantly enforced everywhere the entity is in use. Neat, isn't it?

In addition, the input item in the faces view reflects the change, and renders the input item as plain text.
"We want to keep the input box. Just disable the input text field."
Easy! On the af:inputText, just move the EL-expression from the ReadOnly property to the Disabled property, and set the ReadOnly property to false.
"We can't read the text. Set the text color to black."
Easy! Create and set a StyleClass with the ADF Skin Editor, or even some InlineStyle magic. Looks good in both Firefox and Chrome!
"We can't read the text in Internet Explorer, gray on gray is not a good combination."
Ah, I see... And I can't do much about it the "easy" way. IE won't let you set color of text on disabled elements.

Can I perhaps do something with the ReadOnly attribute to solve the issue? Nope, ReadOnly renders as plain text as soon as you touch it.

Google to the rescue.

Oh. Lots of other people with the same issue. OTN yields nothing but similar problem descriptions, all pointing to the fact that not being able to set the text color on disabled items is an IE issue.

Aino Andriessen has written a blog post describing a workaround to the same issue. Using a plain jsf input text lets you set the ReadOnly attribute, and still keeping the box intact. Yay! But blog post is from 2006, is this really all there is?

Add plain jsf input text and set Rendered property to a mutual exclusive expression with the updateable af:intputText.

Starting to get real clunky now, but it works. I even got the style class to match, and the number formatting right...
"Where is the context menu on the disabled fields?"
Doh! Take a deep breath. Context menu on plain jsf items? Just add an af:showPopupBehavior to the h:inputText elements and you are good to go! No? Ah, phases and what not, big runtime exception telling me I do not know what I am doing. Again.

Running out of time now. Stepping back to the Disabled and ReadOnly properties of the original af:inputText element. Hmm. I wonder.

Add some jQuery magic! The page already has jQuery included to do some fancy color highlight/fade thingys. Set DisabledProperty and let jQuery handle the rest after page load:
$('input[id^=<yourTableId>][id*=<yourColumnId>][disabled]').attr('readonly', 'true').removeAttr('disabled');

Simply put, it finds all input items in a given column of my table, and removes disabled attribute, and sets it to read only. I'm a genius!
"Works great the first time, but not after we push this button!"
Argh! Not so genius after all. PPR will of course ruin my newly crafted lifeline. jQuery live won't trigger correctly for some reason.

Getting really stressed out now. Explaining to the users that I do not know how to display a read only input box is not an appealing option!

Are there any javascript hooks like "I am now done refreshing your model driven table"? Diving into the ADF Javascript API. Looks like I can register a listener on the event triggering the refresh. Luckily there are just two ways for the user to invoke ppr refresh of this particular table, which means I can use af:clientEvent along with addBusyStateListener as described by Frank Nimphius in ADF Code Corner 027.
"It works now! Finally..."
Yes!

It started out simple, declarative and very maintenance friendly, but ended up as a slippery beast of a thing. All because I could not make ADF keep the box when setting ReadOnly on the af:inputText...
"By the way, we want to update the z column always. Just revert the last few changes."
*_*