#!/usr/local/bin/perl
#
# $Header: EMDeployTool.pm 30-apr-2007.15:29:17 minfan Exp $
#
# Copyright (c) 2004, 2007, Oracle. All rights reserved.  
#
#    NAME
#      EMDeployTool.pm
#
#    DESCRIPTION
#      common subroutines for EMDeploy tool
#
#    NOTES
#
#    MODIFIED   (MM/DD/YY)
#     rkpandey  08/21/07 - 6344765: fix em_ear.properties for platforms
#     minfan    04/25/07 - ojdbc14 sunset
#     pchebrol  11/29/06 - 
#     vivsharm  09/05/06 - for emdbsa 11g build warnings
#     aime      08/30/06 - 
#     smodh     02/20/06 - XbranchMerge smodh_jdbc_emcore from main 
#     tthakur   12/18/05 - XbranchMerge tthakur_bug-4889591 from main 
#     tthakur   12/07/05 - XbranchMerge tthakur_bug-4864079 from main 
#     tthakur   11/23/05 - XbranchMerge tthakur_bug-4747613 from main 
#     smodh     02/03/06 - use ojdbc14.jar 
#     tthakur   12/16/05 - bug-4889591
#     tthakur   12/06/05 - bug-4864079
#     tthakur   11/21/05 - bug-4747613
#     ktlaw     08/18/05 - fix bug 4555497 
#     ktlaw     08/16/05 - fix mkdir issue 
#     ktlaw     08/09/05 - add recoverEar sub 
#     skhekale  07/20/05 - use file:/ classpath for only NT 
#     skhekale  07/13/05 - 
#     ktlaw     05/27/05 - remove path existence check 
#     ktlaw     05/24/05 - generate orion-web.xml 
#     ktlaw     05/24/05 - add deploy.master support 
#     tthakur   05/04/05 - PSEP for deploy 
#     tthakur   05/04/05 - rm em.ear after deployment 
#     ktlaw     04/06/05 - 
#     ktlaw     03/30/05 - 
#     ktlaw     03/18/05 - ktlaw_emdeploy_setup_view
#     ktlaw     01/19/05 - created
################################################################################

package EMDeployTool;

use strict;
use English;
use Cwd;
use Directory;
use Path;
use File::Basename;
use File::Copy;
use File::Path;

1;

#
# constructor
# create a new EMDeployTool object
#
sub new
{
  my $ref = {};
  $ref->{'prog'} = $0;
  $ref->{'redeploy'} = "-redeploy";
  $ref->{'OH'} = undef ;
  $ref->{'stage_dir'} = undef;
  $ref->{'stage_home'} = undef;
  $ref->{'exit_status'} = 0 ;
  $ref->{'path_sep'} = ":" ;
  $ref->{'tmp_dir'} = undef;
  $ref->{'nodeploymaster'}=undef;
  bless $ref;
  return $ref;
}

sub initialize
{
  my $self = shift ;
  my ($oh, $stage_home, $stage_dir, $tmp_dir, $path_sep ) = @_ ;
  $self->{'OH'} = $oh if defined $oh;
  $self->{'stage_home'} = $stage_home if defined $stage_home;
  $self->{'path_sep'} = $path_sep if defined $path_sep;
  $self->{'tmp_dir'} = $tmp_dir if defined $tmp_dir;
  $self->setStageDir($stage_dir);
}

sub setNoDeployMaster
{
  my $self = shift;
  $self->{'nodeploymaster'}="true";
}

sub setStageDir
{
  my $self = shift ;
  my ($stage_dir) = @_ ;
  $self->{'stage_dir'} = $stage_dir if defined $stage_dir;
  if(!defined $self->{'stage_dir'} || $self->{'stage_dir'} eq "")
  {
     $self->{'stage_dir'} = "$self->{'OH'}/j2ee/OC4J_EM/applications/em";
  }
}

### start of non-method subroutines ###

my %COMP_ORDER = 
(
  'common' => 0 ,
  'core' => 1 ,
  'db' => 2 ,
  'as' => 3 ,
  'cs' => 4
);

sub deployCmp($$)
{
  my ($a,$b) = @_;
    
  $a =~ /deploy\.([a-z]+)\.*(.*)/ ;
  my $sa = defined $COMP_ORDER{$1} ? $COMP_ORDER{$1} : 99; 
  my $a3 = $2 ;
    
  $b =~ /deploy\.([a-z]+)\.*(.*)/ ;
  my $sb = defined $COMP_ORDER{$1} ? $COMP_ORDER{$1} : 99; 
  my $b3 = $2 ;
  
  $sa <=> $sb or   
  $a3 cmp $b3 ;
}  

sub createTimeStamp()
{
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
  if ($year<=1000) {$year += 1900;}
  $mon += 1;
  my $timestamp = sprintf("%02d%02d%4d_%02d%02d%02d",
        $mday,$mon,$year,$hour,$min,$sec);
  return $timestamp;
}

### end of non-method subroutine ###

sub logAndExec
{ 
  my $self = shift ;
  my $cmd = shift ;
  #NT Workaround
  $cmd =~ s/\\/\//g;
  print "exeuting cmd line : $cmd\n" ;
  system($cmd);
  
  $self->{'exit_status'} = $?;
  return $self->{'exit_status'} ;
}
#forward slash replacement is done in first command-portion only
sub logAndExec2
{
  my $self = shift ;
  my $cmd1 = shift ;
  my $cmd2 = shift;
  #NT Workaround
  $cmd1 =~ s/\\/\//g;
  my $cmd = "$cmd1 $cmd2";
  print "exeuting cmd line : $cmd\n" ;
  system($cmd);
  
  $self->{'exit_status'} = $?;
  return $self->{'exit_status'} ;
}

sub getExitStatus
{
  my $self = shift ;
  return $self->{'exit_status'} ;
}

sub getDeployClasspaths
{
  my $self = shift ;
  
  my $oh = $self->{'OH'} ;
  my @classpaths ;
  my @deployfiles ;
  my $h ;
  
  # Adds an entry to classpath.
  sub addClasspath
  {
    push(@classpaths, substr(shift, length("$oh/"))); # path is relative to $self->{'OH'}
  }
  
  sub addDeployFile
  {
    my $f = shift ;
    if($f !~ /deploy.ini/ && $f !~ /deploy.master/)
    {
      push(@deployfiles, $f);
    }
  }
  
  sub addDeployFileContent
  {
    open(DEPFILE, shift);
    while (<DEPFILE>)
    {
      chomp;
      s/#.*//; # remove comment portion (starting with #)
      s/^\s+//g; # remove front spaces
      s/\s+$//g; # remove trailing spaces
      if ($_ ne "")
      {
        push(@classpaths, $_);
      }
    }
    close(DEPFILE);
  }
  
  
  # collect em classpath entries from deploy.* files under $self->{'OH'}/j2ee.
  my $deployFilesDir = new Directory();
  $deployFilesDir->setPath("$oh/j2ee");  
  $deployFilesDir->find(\&addDeployFile, 'false', "deploy\..+");
  
  @deployfiles = sort deployCmp @deployfiles ;  
  
  foreach $h (@deployfiles)
  {
   addDeployFileContent($h); 
  }
  
  # add jar/zip files under $self->{'OH'}/sysman/jlib/j2ee as em classpath entries
  my $jarZipDir = new Directory();
  $jarZipDir->setPath("$oh/sysman/jlib/j2ee");
  $jarZipDir->find(\&addClasspath, 'true', '\.jar$');
  $jarZipDir->find(\&addClasspath, 'true', '\.zip$');
  
  # remove duplicate entries from classpath
  my %saw;
  grep(!$saw{$_}++, @classpaths);
}
 
# print the environment being used.
sub printEnviron()
{
  my $self = shift ;
  print "Environment : \n";
  print " ORACLE HOME = $self->{'OH'} \n";
  print " STAGE DIR = $self->{'stage_dir'} \n";
  print " TEMP DIR = $self->{'tmp_dir'} \n";
  print " PSEP = $self->{'path_sep'} \n";
  print "\n";
}  

sub displayHelp()
{
  my $self = shift;
  
  print STDERR <<EOM;
  Usage: $self->{'prog'} [ options ]
  where 
    options:
      -stage_dir Source directory containing em application; 
           Defaults to $self->{'OH'}/j2ee/OC4J_EM/applications/em
      -nodeploymaster Flag indicating that deploy.master entries are not to be used for this deployment
  
EOM
}

sub doPrereqChecks()
{
  my $self = shift;

  # check if oracle home exists
  if (!(-e $self->{'OH'}))
  {
      print "Oracle home $self->{'OH'} does not exist. \n";
      exit 3;
  }  
  #check if its OMS home - todo: call OUI api
}

sub createBaseDeployIniEntry()
{
  my $self = shift;
  if (($^O eq "AIX") or ($^O eq "OSF1"))
  {
    open(EM_EAR , $self->{'stage_home'}."/sysman/config/em_ear.properties");
    open(EM_EAR_TMP, $self->{'stage_home'}."/sysman/config/em_ear.properties.tmp");
    while(<EM_EAR>)
    {
      $_ =~ s/-XX:MaxPermSize=256m/ /g;
      print EM_EAR_TMP $_;
    }
    copy($self->{'stage_home'}."/sysman/config/em_ear.properties.tmp", $self->{'stage_home'}."/sysman/config/em_ear.properties");
  }
  return "em=em,$self->{'stage_home'}/lib/em.ear,$self->{'stage_home'}/sysman/config/em_ear.properties,OC4J_EM";
}

sub appendToDeployIni()
{
  my $self = shift ;
  my $line = $self->createBaseDeployIniEntry();
  open(DINI,">>$self->{'OH'}/j2ee/deploy.ini");
  print DINI "$line\n";
  close(DINI);
}

sub createBaseDeployIni()
{
  my $self = shift ;
  my $line = $self->createBaseDeployIniEntry();
  open(DINI,">>$self->{'OH'}/j2ee/deploy.ini");
  print DINI "$line\n";
  close(DINI);
}

sub appendFromDeployMaster()
{
  my $self = shift ;

  if (!defined $self->{'nodeploymaster'})
  {
    my $oh = $self->{'OH'} ;
    if(-e "$oh/j2ee/deploy.master")
    {
      open(A,"$oh/j2ee/deploy.master");
      open(DINI,">>$self->{'OH'}/j2ee/deploy.ini");
      while(<A>)
      {
        chomp;
        s/#.*//; # remove comment portion (starting with #)
        s/^\s+//g; # remove front spaces
        s/\s+$//g; # remove trailing spaces
        if ($_ ne "")
        {
          print DINI "$_\n";
        }
      }
      close(A);
      close(DINI);
    }else
    {
      print "WARNING: $oh/j2ee/deploy.master does not exist, skipping\n" ;
    }
  }
}

sub createDeployIniEntry()
{
  my $self = shift;

  my @classpaths = $self->getAllClasspaths();

  my $iniEntry = $self->createBaseDeployIniEntry();
  # create deploy.ini entry for EM
  my $cp = join(',', @classpaths);
  if ($cp ne "")
  {
    $iniEntry = $iniEntry.",".$cp;
  }
  
  return $iniEntry
}

sub generateOrionWebXml
{
  my $self = shift ;
  my $template = shift ;
  my $output = shift ;
  my $oh = $self->{'OH'} ;

  if(! defined $template)
  {
    $template = "$oh/j2ee/OC4J_EM/applications/em/em/WEB-INF/orion-web.xml.template";
  }
  if(! defined $output)
  {
    $output = "$oh/j2ee/OC4J_EM/applications/em/em/WEB-INF/orion-web.xml";
  }

  my @classpaths = $self->getAllClasspaths();
  my $h ;

  open(TEMPL,"$template");
  open(ORION,">$output");
  
  while(<TEMPL>)
  {
    if(/\%EM_CLASSPATH_ELEMENTS\%/)
    {
      foreach $h (@classpaths)
      {
	if (($^O eq "MSWin32") or ($^O eq "Windows_NT")) {
         print "adding  <classpath path=\"$oh/$h\"/> to orion-web.xml\n";
         print ORION "  <classpath path=\"file:/$oh/$h\"/>\n";
	} else {
         print "adding  <classpath path=\"$oh/$h\"/> to orion-web.xml\n";
         print ORION "  <classpath path=\"$oh/$h\"/>\n";
	}
      }
    }else
    {
      print ORION ;
    }
  }
  close(TEMPL);
  close(ORION);
}

sub getAllClasspaths()
{
  my $self = shift;

  my @classpaths = $self->getDeployClasspaths();

  sub addJarZipToClasspath()
  {
    push(@classpaths, substr(shift, length("$self->{'OH'}/"))); # path is relative to $self->{'OH'}
  }
  
  # add jar/zip files under $self->{'OH'}/sysman/jlib/j2ee as em classpath entries
  my $jarZipDir = new Directory();
  $jarZipDir->setPath("$self->{'OH'}/sysman/jlib/j2ee");

  $jarZipDir->find(\&addJarZipToClasspath, 'true', '\.jar$');
  $jarZipDir->find(\&addJarZipToClasspath, 'true', '\.zip$');
  
  # remove duplicate entries from classpath
  my %saw;
  my $h ;
  foreach $h ( @classpaths )
  {
    if(! -e "$self->{'OH'}/$h")
    {
      print STDOUT "Info : $self->{'OH'}/$h doesn't exist\n";
    }
  }
  @classpaths = grep(!$saw{$_}++, @classpaths);
  #grep(-e "$self->{'OH'}/$_", @classpaths);
}

sub createMasterHelpsetConfig()
{
  my $self = shift;
  my $PSEP = $self->{'path_sep'} ;
  my $helpsetDir = "$self->{'stage_dir'}/em";
  chdir($helpsetDir) || die "Failed to cd to $helpsetDir: $!\n";
  $self->logAndExec("$self->{'OH'}/jdk/bin/java -classpath \"$self->{'OH'}/sysman/jlib/emCORE.jar$PSEP$self->{'OH'}/sysman/jlib/emagentSDK.jar$PSEP$self->{'OH'}/sysman/jlib/log4j-core.jar\" oracle.sysman.emSDK.util.help.BuildMasterHelpSet $helpsetDir") and die ("Failed to create master helpset configuration: $!\n");
}

sub createEmEar()
{
  my $self = shift;
  # create a subdirectory for use in temp directory
  my $timeStamp = $self->createTimeStamp();
  my $EM_TMP_DIR = "$self->{'tmp_dir'}/em_$timeStamp";
  mkdir("$EM_TMP_DIR", 0750) or die "Failed to mkdir $EM_TMP_DIR: $!\n";

  # create em.war
  my $warDir = "$self->{'stage_dir'}/em";
  chdir("$warDir") or die "Failed to cd to $warDir: $!\n";
  $self->logAndExec("$self->{'OH'}/jdk/bin/jar cfM \"$EM_TMP_DIR/em.war\" *");
  
  # create em.ear
  my $earDir = "$self->{'stage_dir'}";

  mkdir("$EM_TMP_DIR/META-INF", 0750) or die "Failed to mkdir $EM_TMP_DIR/META-INF: $!\n";
  copy("$earDir/META-INF/application.xml", "$EM_TMP_DIR/META-INF");
  copy("$earDir/META-INF/orion-application.xml", "$EM_TMP_DIR/META-INF");

  chdir("$EM_TMP_DIR") or die "Failed to cd to $EM_TMP_DIR: $!\n";
  $self->logAndExec("$self->{'OH'}/jdk/bin/jar cfM \"$self->{'OH'}/lib/em.ear\" em.war META-INF");
  chdir("$earDir") or die "Failed to cd to $earDir: $!\n";
  rmtree $EM_TMP_DIR;
}
 
sub deployEmEar()
{
  my $self = shift;
  my $PSEP = $self->{'path_sep'} ;
  # redeploy em.ear
  #  on nt, in -oraclehome arg backward slashes must be passed for this command to succeed
  $self->logAndExec2("$self->{'OH'}/jdk/bin/java -Xmx512M -DemLocOverride=$self->{'OH'} -classpath \"$self->{'OH'}/dcm/lib/dcm.jar$PSEP$self->{'OH'}/jlib/emConfigInstall.jar$PSEP$self->{'OH'}/oc4j/jdbc/lib/ojdbc5dms.jar$PSEP$self->{'OH'}/oc4j/jdbc/lib/ojdbc14dms.jar$PSEP$self->{'OH'}/oc4j/jdbc/lib/orai18n-net-ee.jar$PSEP$self->{'OH'}/oc4j/jdbc/lib/orai18n-utility.jar$PSEP$self->{'OH'}/oc4j/jdbc/lib/orai18n-collation.jar$PSEP$self->{'OH'}/oc4j/jdbc/lib/orai18n-net.jar$PSEP$self->{'OH'}/oc4j/jdbc/lib/orai18n-internal.jar$PSEP$self->{'OH'}/oc4j/jdbc/lib/orai18n-servlet.jar$PSEP$self->{'OH'}/oc4j/jdbc/lib/orai18n-lcsd.jar$PSEP$self->{'OH'}/oc4j/jdbc/lib/orai18n-tools.jar$PSEP$self->{'OH'}/oc4j/jdbc/lib/orai18n-mapping.jar$PSEP$self->{'OH'}/oc4j/jdbc/lib/orai18n-translation.jar$PSEP$self->{'OH'}/oc4j/jdbc/lib/orai18n.jar$PSEP$self->{'OH'}/oc4j/lib/dms.jar$PSEP$self->{'OH'}/jdbc/lib/ojdbc14.jar$PSEP$self->{'OH'}/lib/dms.jar$PSEP$self->{'OH'}/j2ee/home/oc4j.jar$PSEP$self->{'OH'}/lib/xschema.jar$PSEP$self->{'OH'}/lib/xmlparserv2.jar$PSEP$self->{'OH'}/opmn/lib/ons.jar$PSEP$self->{'OH'}/dcm/lib/oc4j_deploy_tools.jar\" oracle.j2ee.tools.deploy.Oc4jDeploy -verbose -inifile $self->{'OH'}/j2ee/deploy.ini $self->{'redeploy'}", "-oraclehome $self->{'OH'}");
  
  # 050809 don't remove $OH/lib/em.ear as recoverStage will need it
  # remove the deployed em.ear as a copy exists in stagedir
  #unlink("$self->{'OH'}/lib/em.ear") or die "Failed to unlink $self->{'OH'}/lib/em.ear: $!\n";                  
}

sub recoverStage
{
  my $self = shift ;
  my $OH = $self->{'OH'} ;
  my $STAGE = $self->{'stage_dir'} ;
  if(defined $STAGE && defined $OH)
  { 
    mkdir ("$STAGE", 0750) if ( ! -e "$STAGE" );
    mkdir ("$STAGE/em", 0750) if ( ! -e "$STAGE/em" );
    chdir "$STAGE";
    $self->logAndExec("$OH/jdk/bin/jar xf \"$OH/lib/em.ear\"");
    chdir "$STAGE/em";
    $self->logAndExec("$OH/jdk/bin/jar xf ../em.war");
  }
}

sub renameIndexHtml
{
  my $self = shift;
  copy("$self->{'OH'}/j2ee/OC4J_EM/applications/em/em/index.html.enterprise", "$self->{'OH'}/j2ee/OC4J_EM/applications/em/em/index.html");
}

