###
# Licensed Materials - Property of IBM* and/or HCL**
# @product.name.full@
# (c) Copyright IBM Corporation 2014, 2016, 2017. All Rights Reserved.
# (c) Copyright HCL Technologies Ltd. 2018. All Rights Reserved.
#
# U.S. Government Users Restricted Rights - Use, duplication or disclosure restricted by
# GSA ADP Schedule Contract with IBM Corp.
#
# * Trademark of International Business Machines
# ** Trademark of HCL Technologies Limited
#
# Filename: importobject.py
#
#
###

import codecs;
import sys;
import os;
import traceback;
from java.lang import Throwable;
from org.codehaus.jettison.json import JSONObject, JSONArray;

# Necessary to make "os" functions work on Windows systems that did not exist when Jython 2.1 was created.
# This workaround overcomes the Jython Windows limitation described on the following webpage:
# http://www.ibm.com/support/knowledgecenter/SS7JFU_6.1.0/com.ibm.websphere.express.doc/info/exp/ae/cxml_jython.html
# WebSphere 9 has moved to Jython 2.7 so this workaround will fail in that environment - that is why
# I surrounded it with a try/except structure.
try:
    import javaos
    if javaos._osType == 'posix' and java.lang.System.getProperty('os.name').startswith('Windows'):  # @UndefinedVariable
      sys.registry.setProperty('python.os', 'nt');
      reload(javaos);
except:
    pass

sys.modules['AdminConfig'] = AdminConfig  # @UndefinedVariable
sys.modules['AdminControl'] = AdminControl  # @UndefinedVariable
sys.modules['AdminApp'] = AdminApp  # @UndefinedVariable
sys.modules['AdminTask'] = AdminTask  # @UndefinedVariable

importedConfigIds = {};
configurationTypes = [];


"""
{
   cluster -> {
      node -> {
          servername -> {
             endpointmappings -> [[
                endpointname,
                endpointhost,
                endpointport
             ]]
          }
      }
   }
}
"""
dynclustermembers = {};
sslConfigGroupArray = JSONArray();

def updateDynamicallyCreatedServers():
  for clustername in dynclustermembers.keys():
    clustermemberstokeep = [];
    Log.debug("Handling dynservers for cluster %s" % clustername);
    clusterconpath = "/Cell:%(cell)s/ServerCluster:%(cluster)s/" % { 'cell': Util.getCell(), 'cluster': clustername}
    clusterid = Util.getid(clusterconpath);
    clustermemberconpath = "/Cell:%(cell)s/ServerCluster:%(cluster)s/ClusterMember:/" % {'cell':Util.getCell(), 'cluster':clustername }
    clustermemberids = Util.getid(clustermemberconpath).splitlines();
    nodedict = dynclustermembers[clustername];
    for nodename in nodedict.keys():
      Log.debug("Handling dynservers for cluster %s node %s" % (clustername, nodename));
      serverdict = nodedict[nodename];
      for servername in serverdict.keys():
        Log.debug("Handling dynservers for cluster %s node %s server %s" % (clustername, nodename, servername));
        #create cluster member if necessary
        found = 0;
        for cmid in clustermemberids:
          cmnodename = Util.getRequiredAttribute(cmid, "nodeName", "ClusterMember");
          cmservername = Util.getRequiredAttribute(cmid, "memberName", "ClusterMember");
          if cmnodename == nodename and cmservername == servername:
            found = 1;
            clustermemberstokeep.append(cmid);
            Log.debug("Found clustermember that matches criteria");
            break;

        if found == 0:
          Log.debug("Failed to find clustermember. creating");
          cmatts = [];
          cmatts.append(['nodeName', nodename]);
          cmatts.append(['memberName', servername]);
          clusmem = Util.create("ClusterMember", clusterid, cmatts, None, 1);
          clustermemberstokeep.append(clusmem);

        #create/update endpoint mappings
        serventconpath = "/Cell:%(cell)s/Node:%(node)s/ServerIndex:/ServerEntry:%(server)s/" % { "cell": Util.getCell(), "node":nodename, "server":servername };
        serventobj = Util.getid(serventconpath);
        endpointlistpath = "/Cell:%(cell)s/Node:%(node)s/ServerIndex:/ServerEntry:%(server)s/NamedEndPoint:/" % { "cell": Util.getCell(), "node":nodename, "server":servername };
        endpointlist = Util.getid(endpointlistpath).splitlines();

        endptmaplist = {};
        if not serverdict[servername]:
          Log.log("No user-defined endpoint port mappings found.  Attempting to use original endpoint port mappings for cluster:  %(cluster)s, node: %(node)s, server: %(server)s" % { "cluster": clustername, "node": nodename, "server": servername});
          if Util.getSavedClusterEndpoints(clustername):
            savednodes = Util.getSavedClusterEndpoints(clustername);
            if savednodes.has_key(nodename):
              savedServers = savednodes[nodename];
              if savedServers.has_key(servername):
                endptmaplist = savedServers[servername];
                Log.log("Located original endpoint mappings for cluster:  %(cluster)s, node: %(node)s, server: %(server)s" % { "cluster": clustername, "node": nodename, "server": servername});
              else:
                Log.log("Server unavailable.  Unable to locate original endpoint mappings");
            else:
              Log.log("Node unavailable.  Unable to locate original endpoint mappings");
          else:
            Log.log("Cluster unavailable.  Unable to locate original endpoint mappings");
        else:
          endptmaplist = serverdict[servername];

        if Util.isValidType("OverlayEndpoint"):
          endPointObjectName = "OverlayEndpoint";
        else:
          endPointObjectName = "EndPoint";

        if endptmaplist:
          for endptmap in endptmaplist:
            Log.log("updateDynamicallyCreatedServers - updating endpoint:  " + endptmap[0]);
            epname = endptmap[0];
            ephost = endptmap[1];
            epport = endptmap[2];
            endptattrs = [];

            if ephost is None:
              Log.debug("Endpoint host is none for %(epname)s and $(node)s" % { 'epname':epname, 'node':nodename });
            else:
              endptattrs.append(['host', ephost]);

            if ephost is None:
              Log.debug("Endpoint port is none for %(epname)s and $(node)s" % { 'epname':epname, 'node':nodename });
            else:
              endptattrs.append(['port', epport]);

            endfound = 0;
            for namedendpt in endpointlist:
              lookupname = Util.getRequiredAttribute(namedendpt, "endPointName", "NamedEndPoint");
              if lookupname == epname:
                endfound = 1;
                endpt = Util.getOptionalAttribute(namedendpt, "endPoint", "NamedEndPoint");
                if endpt is None or endpt.strip() == "":
                  endpt = Util.create(endPointObjectName, namedendpt, endptattrs, None, 1);
                else:
                  Util.modify(endpt, endptattrs, 1);
                break;
            #namedendpt not found create it
            if endfound == 0:
              namedendptattrs = [];
              namedendptattrs.append(['endPointName', epname]);
              namedendpt = Util.create("NamedEndPoint", serventobj, namedendptattrs, None, 1);
              endpt = Util.create(endPointObjectName, namedendpt, endptattrs, None, 1);
        else:
          Log.log("updateDynamicallyCreatedServer - no endpoint mapping list is available for cluster:  %(cluster)s, node: %(node)s, server: %(server)s" % { "cluster": clustername, "node": nodename, "server": servername});

    #cleanup other members
    clustermemberlistconpath = "/Cell:%(cell)s/ServerCluster:%(cluster)s/ClusterMember:/" % { 'cell':Util.getCell(), 'cluster':clustername };
    clustermemberlist = Util.getid(clustermemberlistconpath).splitlines();
    for cmid in clustermemberlist:
      if cmid in clustermemberstokeep:
        #keep it
        pass;
      else:
        Log.log("Removing no longer applicable cluster member");
        nodename = Util.getRequiredAttribute(cmid, "nodeName", "ClusterMember");
        servername = Util.getRequiredAttribute(cmid, "memberName", "ClusterMember");
        Util.remove(cmid);
        serverobjconpath = "/Cell:%(cell)s/Node:%(node)s/Server:%(server)s/" % { 'cell':Util.getCell(), 'node': nodename, 'server': servername };
        serverobj = Util.getid(serverobjconpath);
        if serverobj is not None and serverobj.strip() != "":
          Util.remove(serverobj);

  # update named end point SSL configuration for mutliplicity cluster members
  for i in range(sslConfigGroupArray.length()):
    sslConfigGroupsForServerJsonObj = sslConfigGroupArray.getJSONObject(i)
    childrenArray = sslConfigGroupsForServerJsonObj.getJSONArray('children');
    for j in range(childrenArray.length()):
      sslConfigGroupJsonObj = childrenArray.getJSONObject(j);
      # force update/create of SSLConfigGroups
      SSLConfigGroup._doImport(sslConfigGroupJsonObj, 1);

def getTypesFromContainmentPath(path):
  #we know that containment paths have to be / separated
  types = [];
  parts = path.split("/");
  for part in parts:
    type2name = part.split(":", 1);
    curtype = type2name[0];
    types.append(curtype);
  return types;

def handleObject(conpath):
  if len(configurationTypes) == 0:
    return 1;
  types = getTypesFromContainmentPath(conpath);
  for curtype in types:
    if curtype in configurationTypes:
      return 1;
  return 0;

def addServerTemplateToImport(serverjsonobject, roledict, servertemplateconpath):
  """
  this is a server template.
  we need to
  1) copy this jsonobject for each node x range(count)
  2) update all paths -- not necessary
  3) ensure containment paths are correct
  4) add to imports
  5) add updated children to imports
  6) after creating/updating/deleting all server, add ClusterMember mappings
  7) after creating/updating/deleting all servers, we need to update the server entries for the servers
  """
  serverjsonobjectstring = serverjsonobject.toString();
  roleProperties = serverjsonobject.getJSONObject("roleProperties");
  origservername = roleProperties.getString("websphere.server");
  clustername = None;
  conpathparts = servertemplateconpath.split("/");
  for part in conpathparts:
    partparts = part.split(":", 2);
    partname = partparts[0];
    if partname == "ServerCluster":
      clustername = partparts[1];
      break;
  if clustername is None or clustername.strip() == "":
    raise Exception("Server template exists when no clustername could be found: %s" % servertemplateconpath);
  serverstoimport = [];
  if roledict.has_key("Server"):
    serverstoimport = roledict["Server"];
  else:
    roledict["Server"] = serverstoimport;

  nodes = roleProperties.optString("websphere.server.nodelist", None);
  servercount = roleProperties.optString("websphere.server.servercount", "1");
  servernameformats = roleProperties.optString('websphere.server.servernameformats', None);
  epmappingstring = roleProperties.optString("websphere.server.endpointportmappings", None);
  starttokendelimiter = roleProperties.optString("websphere.server.starttokendelimiter", "@");
  endtokendelimiter = roleProperties.optString("websphere.server.endtokendelimiter", "@");

  nodenamelist = Util.getNodeNameList(nodes)
  nameformats = Util.getServerNameFormats(servernameformats)
  epmappings = Util.getEndPointMappings(epmappingstring)
  if epmappings is None:
    Log.log("addServerTemplateToImport - endpointmappings is empty");

  nodedict = {};
  dynclustermembers[clustername] = nodedict;

  for nodename in nodenamelist:
    # strip leading and trailing whitespace from nodename
    nodename = nodename.strip();

    nodeconpath = "/Cell:" + Util.getCell() + "/Node:" + nodename + "/";
    nodeid = Util.getid(nodeconpath);
    if nodeid is None or nodeid.strip() == "":
      raise Exception("The node name '%s' was specified in the NODELIST property and cannot be found." % nodename);

    # If the dmgr node is included in list, display msg and skip it
    dmgrFound = 0;
    servers = AdminConfig.list("Server", nodeid);
    for server in servers.splitlines():
      if AdminConfig.showAttribute(server, "serverType") == "DEPLOYMENT_MANAGER":
        dmgrFound = 1;
        break;
    if dmgrFound:
      print "The Deployment Manager node (" + nodename + ") was specified in the NODELIST property. Application servers are not allowed to run in the Deployment Manager node profile. The Deployment Manager node will be ignored."
      continue;

    serverdict = {};
    nodedict[nodename] = serverdict;
    nameformat = "%c-%n-%i";

    if nameformats.has_key("*"):
      nameformat = nameformats["*"];
    if nameformats.has_key(nodename):
      nameformat = nameformats[nodename];

    hostname = Util.getRequiredAttribute(nodeid, "hostName", "Node");

    for i in range(int(servercount)):
      serverName = nameformat.replace("%c", clustername);
      serverName = serverName.replace("%n", nodename);
      serverName = serverName.replace("%i", str(i+1));
      curconpath = "/Cell:" + Util.getCell() + "/Node:" + nodename + "/Server:" + serverName + "/";
      newserobjid = Util.getid(curconpath);
      if newserobjid == None or newserobjid.strip() == "":
        Util.createClusterMember(clustername, nodename, serverName);
      # Save original endpoints to use if the user does not supply their own endpoint port mappings
      # Note that the Util.createClusterMember() call uses the -genUniquePorts option to guarantee that
      # unique port values are generated.
      # Note that it is possible that the actual cluster member creation was delayed waiting for the actual cluster
      # to be created.  In that case, the call to Util.saveClusterMemberOriginalEndpoints() will do nothing.
      Util.saveClusterMemberOriginalEndpoints(clustername, nodename, serverName);

      newjsonstring = serverjsonobjectstring.replace(origservername, serverName);
      newjsonstring = newjsonstring.replace(starttokendelimiter + "websphere.node" + endtokendelimiter, nodename);
      newjsonstring = newjsonstring.replace(starttokendelimiter + "websphere.node.hostname" + endtokendelimiter, hostname);
      newjsonobject = JSONObject(newjsonstring);
      Log.log("Adding server from server template with containmentpath of %s" % curconpath);
      serverstoimport.append({'containmentpath':curconpath, 'jsonobject':newjsonobject});
      if newjsonobject.has('children'):
        childrenArray = newjsonobject.getJSONArray('children');
        newChildrenArray = JSONArray()
        # strip out the SSLConfigGroup config data for use later
        for k in range(childrenArray.length()):
          child = childrenArray.getJSONObject(k);
          if child.getString("path").find("/SSL Config Groups") != -1:
            sslConfigGroupArray.put(child)
          else:
            newChildrenArray.put(child)
        addChildrenToImports(newChildrenArray, roledict, curconpath);

      endpointlist = [];
      serverdict[serverName] = endpointlist;
      if epmappings is not None:
        for epmap in epmappings:
          epname = epmap[0];
          ephost = epmap[1];
          if ephost == ".":
            ephost = hostname;
          epport = epmap[2];
          endpointlist.append([epname, ephost, str(int(epport) + i)]);


def addChildrenToImports(jsonArray, roledict, containmentpath):
  for i in range(jsonArray.length()):
    curcontainmentpath = containmentpath;
    obj = jsonArray.getJSONObject(i);
    if obj.has("roleName"):
      curRoleName = Util.roleNameToType(obj.getString("roleName"));
      if curRoleName == "Server" and curcontainmentpath.find("ServerCluster") != -1:
        """
        this is a server template.
        we need to
        1) copy this jsonobject for each node x range(count)
        2) update all paths
        3) ensure containment paths are correct
        4) add to imports
        5) add updated children to imports
        6) after creating/updating/deleting all servers, we need to update the server entries for the servers
        """
        addServerTemplateToImport(obj, roledict, curcontainmentpath);
        #skip the rest of the stuff in the for loop
        #specifically, we really really don't want to add any of the children of this object as imports
        continue;
      # dynamic clusters create their own servers
      # so, if the server is a member of a dynamic cluster, skip it (the dynamic cluster will create it for us)
      if curRoleName == "Server":
        roleProperties = obj.getJSONObject("roleProperties")
        if roleProperties.has("isdynamicclustermember"):
          isDynamicClusterServerMember = roleProperties.optString("isdynamicclustermember", None)
          if isDynamicClusterServerMember == "true":
            Log.logMsg("Skipping a server that is a member of a dynamic cluster. Containment path of server is " + str(curcontainmentpath), Log.LogLevelDebug);
            continue;
      mod = Util.getRoleModule(curRoleName);
      if mod is not None:
        if ('doImport' in dir(mod) and Util.__applyPartialConfig__ == "false") or ('doImportPartial' in dir(mod) and (Util.__applyPartialConfig__ == "true" or Util.__removePartialConfig__ == "true")):
          curName = Util.getObjectNameFromJson(curRoleName, obj);
          curcontainmentpath = "%(path)s%(type)s:%(name)s/" % { 'path':containmentpath, 'type':curRoleName, 'name':curName };
          rolelist = None;
          if roledict.has_key(curRoleName):
            rolelist = roledict[curRoleName];
          else:
            rolelist = [];
            roledict[curRoleName] = rolelist;
          Log.debug("Adding %(name)s with type %(type)s at containmentpath %(path)s" % { 'name':obj.getString("name"), 'type':curRoleName, 'path':curcontainmentpath});
          rolelist.append({'containmentpath':curcontainmentpath, 'jsonobject':obj});
          if obj.has('children'):
            childrenArray = obj.getJSONArray('children');
            addChildrenToImports(childrenArray, roledict, curcontainmentpath);
        else:  #end if import
          Log.debug("Skipping %s and all its children" % curRoleName);
      else:#mod is not None
        Log.debug("Skipping none websphere role %s" % curRoleName);
        if obj.has('children'):
          childrenArray = obj.getJSONArray('children');
          addChildrenToImports(childrenArray, roledict, curcontainmentpath);
        pass;
    else:#obj.has(roleName)
      Log.debug("Skipping %s" % obj.getString("name"));
      if obj.has('children'):
        childrenArray = obj.getJSONArray('children');
        addChildrenToImports(childrenArray, roledict, curcontainmentpath);
      pass;

def importObject(curtype, curobject):
  # if the removeSnippet option was selected, and if the current configuration object's type is
  # the same object type specified in the WebSphere Configuration Types field, instead of calling
  # the object's doImport method, we will remove the object.
  if Util.__removeSnippet__ == "true" and len(Util.__configurationTypes__) != 1:
    # if the removeSnippet option was selected, only one configuration type should be specified
    raise Exception(("The Snippet Actions for Individual Configuration Objects field "
                     "was set to Remove Individual Configuration Object, however the "
                     "WebSphere Configuration Types field did not contain a single configuration "
                     "object type.  Verify the WebSphere Configuration Type field contains only the "
                     "configuration object type your snippet represents.  If you are not using "
                     "snippets, set the Snippet Actions for Individual Objects field to None."))
  if Util.__removeSnippet__ == "true" and curtype == Util.__configurationTypes__[0]:
    # this is the object the snippet represents, and we want to remove it
    containmentpath = curobject['containmentpath'];
    objid = Util.getid(containmentpath);
    Log.logMsg("Found the snippet object to delete: " + str(objid), Log.LogLevelDebug);
    # temporarily set __removeSnippet__ to false so the remove method will complete
    Util.__removeSnippet__ = "false"
    Util.remove(objid, 1);
    # set __removeSnippet__ back to true
    Util.__removeSnippet__ = "true"
    # because we don't want to create/remove any more objects, let's set __handletypepaths__ to [].
    # this will cause creates/removes to fail the handletypepaths check, preventing them from occurring
    Log.logMsg("Setting Util.__handletypepaths__ to [].", Log.LogLevelDebug);
    Util.__handletypepaths__ = []
    return;

  Log.debug("Start Import of containment path %s " % curobject['containmentpath']);
  mod = Util.getRoleModule(curtype);
  containmentpath = curobject['containmentpath'];
  if mod is not None:
    if 'doImport' in dir(mod):
      #some objects are reliant on the parent to tell them to import
      #mostly things with no names
      #if not handleObject(containmentpath):
        #Log.debug("Skipping import of containmentpath %s because it is not configured to be applied/collected" % containmentpath);
      #else:
      jsonobject = curobject['jsonobject'];
      Log.log("Running import for containmentpath %(conpath)s"  % { 'conpath': containmentpath });
      configId = mod.doImport(containmentpath, curtype, jsonobject);
      importedConfigIds[configId] = containmentpath;
      Log.log("Finished Import");
    else:
      Log.debug("Not importing for containment path %(conpath)s with type %(type)s because its module has no import method" % { 'conpath':containmentpath, 'type':curtype});
  else:
    Log.debug("Skipping object with none WebSphere role %s" % type);

def importTypes(importtype, objectsToImport):
  if importtype != "" and not importtype.startswith("#"):
    Log.debug("Importing %ss" % importtype);
    if objectsToImport.has_key(importtype):
      objectlist = objectsToImport[importtype];
      if objectlist != None:
        for curobj in objectlist:
          importObject(importtype, curobj);
    Log.debug("Finished Importing %ss" % importtype);

def doCleanup(conpath, roleName, objectsToImport, objectsDir):
  #assume the obj with conpath has already been checked
  for childtype in Util.getChildrenTypes(roleName, objectsDir):
    if childtype != "" and Util.isValidType(childtype):
      childsearchpath = "%(curpath)s%(type)s:/" % { 'curpath': conpath, 'type': childtype }
      Log.debug("Checking child search path %s for cleanup" % childsearchpath);
      for objid in Util.getid(childsearchpath).splitlines():
        if importedConfigIds.has_key(objid):
          Log.debug("Not deleting imported config id %s" % objid);
          doCleanup(importedConfigIds[objid], childtype, objectsToImport, objectsDir);
        elif checkForNewDynamicCluster(objid, childsearchpath, childtype):
          Log.debug("Not deleting imported config id %s because it is associated with new Dynamic Cluster" % objid);
        else:
          if handleObject(childsearchpath):
            Log.log("Deleting config id %s" % objid);
            Util.remove(objid);
          else:
            Log.debug("Not deleting config id %s because it is not configured to be handled" % objid);

def checkForNewDynamicCluster(objid, conpath, childtype):
  if not Util.isValidType("DynamicCluster"):
    return 0;
  if (childtype == "Server"):
    clusterName = Util.getOptionalAttribute(objid, "clusterName", "Server");
    if clusterName is not None and len(clusterName) > 0:
      dynamicClusters = Util.getid("/DynamicCluster:/").splitlines();
      if len(dynamicClusters) > 0:
        for dynamicCluster in dynamicClusters:
          if len(dynamicCluster) > 0:
            dynamicClusterName = Util.getRequiredAttribute(dynamicCluster, "name", "DynamicCluster");
            if (clusterName == dynamicClusterName):
              return 1;
  elif (childtype == "ServerCluster"):
    serverClusterName = Util.getOptionalAttribute(objid, "name", "ServerCluster");
    if serverClusterName is not None and len(serverClusterName) > 0:
      dynamicClusters = Util.getid("/DynamicCluster:/").splitlines();
      if len(dynamicClusters) > 0:
        for dynamicCluster in dynamicClusters:
          if len(dynamicCluster) > 0:
            dynamicClusterName = Util.getRequiredAttribute(dynamicCluster, "name", "DynamicCluster");
            if (serverClusterName == dynamicClusterName):
              return 1;

  return 0;

# gets any discovered configuration types specified in the config file
def getConfigTypesFromFile(inputProps):
  configTypesSpecifiedInFile = "";
  configFile = inputProps.getProperty("configurationFile");
  if configFile == "":
    return configTypesSpecifiedInFile
  inputFile = codecs.open(configFile, "r", "utf-8");
  inputData = inputFile.readlines();
  inputData = ''.join(inputData);
  inputDataStripped = inputData.strip();
  firstChar = inputDataStripped[0:1];
  if not firstChar == "[":
    inputData = "[" + inputData + "]";
  jsonArray = JSONArray(inputData);
  for i in range(jsonArray.length()):
    jsonObj = jsonArray.getJSONObject(i);
    name = jsonObj.getString("name");
    if name == "Specified Configuration Types to Discover":
      configTypesSpecifiedInFile = jsonObj.getString("discoveredConfigurationTypes");
      Log.logMsg("Found discovered configuration types specified in the configuration file. They are: " + str(configTypesSpecifiedInFile), Log.LogLevelDebug);
      configTypesSpecifiedInFile = Util.formatConfigurationTypes(configTypesSpecifiedInFile)
      break;
  return configTypesSpecifiedInFile

# get all object types from the importOrderPartial file
def getAllObjectTypesForPartial():
  configurationTypesPartialInputSplit = [];
  orderPartialFile = open("%s/importOrderPartial" % objectsDir, 'r');
  for ordertype in orderPartialFile.readlines():
    ordertype = ordertype.strip();
    validOrderType = Util.isValidType(ordertype);
    if validOrderType is not None:
      configurationTypesPartialInputSplit.append(validOrderType);
  orderPartialFile.close();
  return configurationTypesPartialInputSplit;

def mainmethod():
  pihelper = PluginInputHelper.PluginInputHelper(sys.argv[0]);
  inputProps = pihelper.getInputProperties();

  logDir = pihelper.getLogDirectory();
  logFile = open("%s/debugLog.txt" % logDir.getAbsolutePath(),'w');
  redoFile = open("%s/redoLog.txt" % logDir.getAbsolutePath(),'w');
  Log.setLogLevel(inputProps.getProperty("logLevel"));
  Log.setLogFile(logFile);
  Log.setRedoFile(redoFile);

  resourceJSONObject = pihelper.getFullResourceConfiguration();

  curcontainmentpath = Util.findContainmentPath(resourceJSONObject);
  configurationTypesInput = Util.formatConfigurationTypes(inputProps.getProperty("configurationTypes"))
  if configurationTypesInput is None:
    configurationTypesInput = "";
  configurationTypesPartialInput = Util.formatConfigurationTypes(inputProps.getProperty("partialConfigTypes"))
  configurationTypesPartialRemoveInput  = Util.formatConfigurationTypes(inputProps.getProperty("partialRemoveConfigTypes"))
  # configurationTypesPartialInput is None unless we are running the Partial Apply step
  # (this is how we determine we are running the Partial Apply step)
  if configurationTypesPartialInput is not None:
    Log.logMsg("Running importobject.py for partial apply...", Log.LogLevelDebug);
    Util.setUpdateAndCreateOnlyToTrue();
    Util.setApplyPartialConfigToTrue();
    if configurationTypesPartialInput == "":
      # if users left the config types for partial apply field empty,
      # we will apply all supported config types.
      # Get all supported config types from importOrderPartial
      configurationTypesPartialInputSplit = getAllObjectTypesForPartial();
    else:
      configurationTypesPartialInputSplit = configurationTypesPartialInput.splitlines();
      configurationTypesPartialInputSplit.sort();
    Log.logMsg("Partial apply will be run on object types: " + str(configurationTypesPartialInputSplit), Log.LogLevelDebug);
  # configurationTypesPartialRemoveInput is None unless we are running the Partial Remove step
  # (this is how we determine we are running the Partial Remove step)
  if configurationTypesPartialRemoveInput is not None:
    Log.logMsg("Running importobject.py for partial remove...", Log.LogLevelDebug);
    Util.setUpdateAndCreateOnlyToTrue();
    Util.setRemovePartialConfigToTrue();
    if configurationTypesPartialRemoveInput == "":
      # if users left the config types for partial remove field empty,
      # we will remove all supported config types.
      # Get all supported config types from importOrderPartial
      configurationTypesPartialInputSplit = getAllObjectTypesForPartial();
    else:
      configurationTypesPartialInputSplit = configurationTypesPartialRemoveInput.splitlines();
      configurationTypesPartialInputSplit.sort();
    Log.logMsg("Partial remove will be run on object types: " + str(configurationTypesPartialInputSplit), Log.LogLevelDebug);
  configTypesSpecifiedInFile = getConfigTypesFromFile(inputProps)
  configurationTypesInputSplit = configurationTypesInput.splitlines()
  configTypesSpecifiedInFileSplit = configTypesSpecifiedInFile.splitlines()

  configurationTypesInputSplit.sort()
  configTypesSpecifiedInFileSplit.sort()

  # if there are no Specified Configuration Types in the config file, but there are
  # specified config types on the apply, log a warning message
  if configTypesSpecifiedInFile == "":
    if configurationTypesInputSplit is not None and len(configurationTypesInputSplit) > 0:
      Log.logMsg(("Warning: The configuration file shows that, when the WebSphere Configuration Discovery "
        "step was executed, the WebSphere Configuration Types field had no values set. "
        "However, the WebSphere Configuration Apply step's WebSphere Configuration Types field "
        "was executed with these values: " +  configurationTypesInput + "."), Log.LogLevelWarn);

  # verify the config file's Specified Configuration Types to Discover matches what
  # was entered for the Apply step's WebSphere Configuration Types field
  if configTypesSpecifiedInFile != "":
    if str(configurationTypesInputSplit) != str(configTypesSpecifiedInFileSplit):
      # clean up configurationTypesInput for the exception message
      if configurationTypesInput is not None and len(configurationTypesInput) > 0:
        configurationTypesInput = Util.formatConfigurationTypes(configurationTypesInput, "\n", ",")
      raise Exception(("The configuration file shows that, when the WebSphere Configuration Discovery "
        "step was executed, the WebSphere Configuration Types field had the following values "
        "set: " + Util.formatConfigurationTypes(configTypesSpecifiedInFile, "\n", ", ") + ". The WebSphere Configuration Apply step "
        "needs to be executed with its WebSphere Configuration Types field set to these same values. The "
        "WebSphere Configuration Apply step was executed with these values: " +  configurationTypesInput + "."))

  adminConfigTypes = Util.getAdminConfigTypes();
  if configurationTypesInput is not None and len(configurationTypesInput) > 0:
    for line in configurationTypesInput.splitlines():
      line = line.strip();
      if len(line) > 0:
        if line in adminConfigTypes:
          configurationTypes.append(line);

  Util.setConfigurationTypes(configurationTypes);
  applicableTypes = Util.getApplicableTypes("%s/plugin.xml" % PLUGIN_HOME);
  handleTypePaths = Util.getHandleTypePathsFromJSON(resourceJSONObject, applicableTypes, configurationTypes);
  Log.debug(handleTypePaths);
  Util.setAttributesToNotUpdateDuringApply();

  roleName = resourceJSONObject.getString("roleName");
  roleName = Util.roleNameToType(roleName);

  objectsToImport = {};
  if resourceJSONObject.has("parent"):
    resourceJSONObject.remove("parent");

  children = None;
  if resourceJSONObject.has("children"):
    children = resourceJSONObject.getJSONArray("children");

  objectsToImport[roleName] = [];
  objectsToImport[roleName].append({ 'containmentpath':curcontainmentpath, 'jsonobject':resourceJSONObject });
  Log.debug("Adding ROOT Object with type %(type)s at containmentpath %(path)s" % { 'type':roleName, 'path':curcontainmentpath});

  # this is where we build a list of containment paths in the format
  # 'JDBCProvider': [{'containmentpath': '/Cell:halloween-dmgrCell01/JDBCProvider:CellJDBC_3/', 'jsonobject': {"path":"\/halloween...
  if children != None:
    addChildrenToImports(children, objectsToImport, curcontainmentpath);

  # special handling for dynamic clusters:
  # dynamic clusters consist of two object types that are both children of Cell: DynamicCluster and ServerCluster.
  # both objects may be in the config file
  # since they are siblings (both children of Cell), pihelper.getFullResourceConfiguration() will
  # return the json object for either DynamicCluster OR ServerCluster.
  # if our json object is DynamicCluster or ServerCluster, check to see if the config file also
  # contains config data for the other object.  if it does, update handleTypePaths and make sure we
  # apply BOTH DynamicCluster and ServerCluster.
  if roleName == "ServerCluster":
    pihelper2 = PluginInputHelper.PluginInputHelper(sys.argv[0], pihelper.getSecret());
    resourceJSONObject2 = pihelper2.getFullResourceConfiguration("/Dynamic Clusters/");
    roleName2 = resourceJSONObject2.getString("roleName");
    roleName2 = Util.roleNameToType(roleName2);
    if roleName2 == "DynamicCluster":
      curcontainmentpath2 = Util.findContainmentPath(resourceJSONObject2);
      if resourceJSONObject2.has("parent"):
        resourceJSONObject2.remove("parent");
      children2 = None;
      if resourceJSONObject2.has("children"):
        children2 = resourceJSONObject2.getJSONArray("children");
      objectsToImport[roleName2] = [];
      objectsToImport[roleName2].append({ 'containmentpath':curcontainmentpath2, 'jsonobject':resourceJSONObject2 });
      Log.debug("Adding ROOT Object with type %(type)s at containmentpath %(path)s" % { 'type':roleName2, 'path':curcontainmentpath2});
      if children2 != None:
        addChildrenToImports(children2, objectsToImport, curcontainmentpath2);
      handleTypePaths = Util.getHandleTypePathsFromJSON(resourceJSONObject2, applicableTypes);
      Log.debug("Updated handleTypePaths");
      Log.debug(handleTypePaths);
  elif roleName == "DynamicCluster":
    pihelper2 = PluginInputHelper.PluginInputHelper(sys.argv[0], pihelper.getSecret());
    resourceJSONObject2 = pihelper2.getFullResourceConfiguration("/ServerClusters/");
    roleName2 = resourceJSONObject2.getString("roleName");
    roleName2 = Util.roleNameToType(roleName2);
    if roleName2 == "ServerCluster":
      curcontainmentpath2 = Util.findContainmentPath(resourceJSONObject2);
      if resourceJSONObject2.has("parent"):
        resourceJSONObject2.remove("parent");
      children2 = None;
      if resourceJSONObject2.has("children"):
        children2 = resourceJSONObject2.getJSONArray("children");
      objectsToImport[roleName2] = [];
      objectsToImport[roleName2].append({ 'containmentpath':curcontainmentpath2, 'jsonobject':resourceJSONObject2 });
      Log.debug("Adding ROOT Object with type %(type)s at containmentpath %(path)s" % { 'type':roleName2, 'path':curcontainmentpath2});
      if children2 != None:
        addChildrenToImports(children2, objectsToImport, curcontainmentpath2);
      handleTypePaths = Util.getHandleTypePathsFromJSON(resourceJSONObject2, applicableTypes);
      Log.debug("Updated handleTypePaths");
      Log.debug(handleTypePaths);

  # __updateAndCreateOnly__ may already be set to true if running partial apply/remove
  if Util.__updateAndCreateOnly__ == "false":
    updateAndCreateOnlyCheck = inputProps.getProperty("updateAndCreateOnly")
    if updateAndCreateOnlyCheck == "true":
      Util.setUpdateAndCreateOnlyToTrue();

  # removing the snippetAction property in version 49
  snippetAction = "0";
  # snippetAction = inputProps.getProperty("snippetAction")
  # snippetAction == "0" = None
  # snippetAction == "1" = Apply
  # snippetAction == "2" = Remove
  if snippetAction == "2":
    Log.logMsg("Snippet Actions for Individual Configuration Objects is set to Remove", Log.LogLevelDebug);
    Util.setRemoveSnippetToTrue();

  if snippetAction == "1":
    Log.logMsg("Snippet Actions for Individual Configuration Objects is set to Apply", Log.LogLevelDebug);
    Util.setApplySnippetToTrue();

  if Util.__applyPartialConfig__ == "true" or Util.__removePartialConfig__ == "true":
    orderfile = open("%s/importOrderPartial" % objectsDir, 'r');
  else:
    orderfile = open("%s/importOrder" % objectsDir, 'r');

  for ordertype in orderfile.readlines():
    ordertype = ordertype.strip();
    if configurationTypesPartialInput is not None or configurationTypesPartialRemoveInput is not None:
      if (Util.__applyPartialConfig__ == "true" or Util.__removePartialConfig__ == "true") and ordertype in configurationTypesPartialInputSplit:
        importTypes(ordertype, objectsToImport);
    else:
      if snippetAction == "1" and configurationTypes[0] == ordertype:
        # if the Snippet Actions for Individual Configuration Objects is set to Apply and
        # the current object type is the same as what was entered in the WebSphere Configuration Types
        # field, set applySnippet to false in order to let the containment path update it
        # (we will verify configurationTypes only has one entry in Util.create)
        Util.setApplySnippetToFalse();
      importTypes(ordertype, objectsToImport);

  orderfile.close();

  updateDynamicallyCreatedServers();

  if snippetAction == "2":
    Util.removeSnippet();

  if Util.__updateAndCreateOnly__ == "true":
    Log.logMsg("updateAndCreateOnly is set to true. Configuration objects that exist in WAS but are not present in your configuration file will not be deleted.", Log.LogLevelDebug);
  else:
    Log.logMsg("updateAndCreateOnly is set to false.  Checking to see if there are objects to delete.", Log.LogLevelDebug);
    doCleanup(curcontainmentpath, roleName, objectsToImport, objectsDir);


  AdminConfig.save();  # @UndefinedVariable
  Util.printCurPath();
  logFile.close();
  redoFile.close();
  print "\nConfiguration Apply Complete.";


PLUGIN_HOME=os.environ['PLUGIN_HOME'];
objectsDir = "%(plugHome)s%(fileSep)sobjects" % { 'plugHome': PLUGIN_HOME, 'fileSep': os.sep };
sys.path.insert(0,objectsDir);
Util = __import__("utilities.Util").Util;
SSLConfigGroup = __import__("SSLConfigGroup.SSLConfigGroup").SSLConfigGroup;
Log = __import__("WASConfLog.Log").Log;
PluginInputHelper = __import__("PluginInputHelper.PluginInputHelper").PluginInputHelper;

try:
  mainmethod();
except: #except on anything, then check if its a java or python exception
  #why in the did jython not handle this gracefully....
  #print the stack here because websphere may decide to just swallow it
  actualExcept = sys.exc_info()[1];
  if isinstance(actualExcept, Throwable):
    actualExcept.printStackTrace();
  traceback.print_exc();
  raise actualExcept;
