gt:                 if (ExtDataStoreUtils.nonemptyQueryResult(R)) {
context: /*
   
      Copyright IBM Corp. 2012, 2016
      This file is part of Anomaly Detection Engine for Linux Logs (ADE).
  
      ADE is free software: you can redistribute it and/or modify
      it under the terms of the GNU General Public License as published by
      the Free Software Foundation, either version 3 of the License, or
      (at your option) any later version.
  
      ADE is distributed in the hope that it will be useful,
      but WITHOUT ANY WARRANTY; without even the implied warranty of
      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      GNU General Public License for more details.
  
      You should have received a copy of the GNU General Public License
      along with ADE.  If not, see <http://www.gnu.org/licenses/>.
   
  */
  package org.openmainframe.ade.ext.data;
  
  import java.sql.ResultSet;
  import java.sql.SQLException;
  import java.util.ArrayList;
  import java.util.List;
  
  import org.apache.commons.lang3.StringUtils;
  import org.openmainframe.ade.data.ISource;
  import org.openmainframe.ade.Ade;
  import org.openmainframe.ade.dbUtils.DriverType;
  import org.openmainframe.ade.exceptions.AdeException;
  import org.openmainframe.ade.exceptions.AdeInternalException;
  import org.openmainframe.ade.ext.utils.AtomicTransaction;
  import org.openmainframe.ade.ext.utils.ExtDataStoreUtils;
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  
  /**
   * This class holds the additional system-informational parameters which were provided
   * (as required) on a given action (e.g., Upload, Analyze).
   *
   * It is also charged with keeping their representations up-to-date in the analytics
   * database.  The logic for "when and what to update"  is specialized.
   *
   * The 2 Constructors are called for data from 2 very different origins:
   * - The "parameters" one where the values originate from the given action (e.g., Upload)
   * - The "database" one where the values originate from pre-existing db tables' rows,
   *   produced by "this" instance as its "alter ego"
   *
   * Notice that "the missing piece" is the Source, which provides the <plex-sys> name,
   * as well as a potential db lookup key.  For example, UploadSysInfo processing could be
   * working against a log file which contains messages from multiple systems.  In that scenario,
   * UploadSysInfo would instantiate one ManagedSystemInfo object, then use it repeatedly with
   * each of the systems (Sources) whose messages it encounters.  In other words, though not
   * instantiated with a Source, an instance of this class is useless unless used in conjunction
   * with an instance of Source (since only then is a full Managed System context realized).
   *
   *
   */
  public class ManagedSystemInfo {
  
      private static final Logger logger = LoggerFactory.getLogger(ManagedSystemInfo.class);
  
      private int m_gmtOffset;
      private String m_osName;
      private ManagedSystemInfo m_dbqueryManagedSystem = null;
  
      public ManagedSystemInfo(long gmtOffset, String osName) {
          m_osName = osName;
          m_gmtOffset = (int) gmtOffset;
  
          logger.trace(String.format("ManagedSystemInfo CTOR( gmt=%d, osName=%s )...",
                  m_gmtOffset, m_osName));
      }
  
      /**
       * Internal CTOR.
       *
       * Values come from database row in MANAGED_SYSTEMS table.
       * Vacant values are moot, as they will be set unconditionally
       * from external values.
       *
       * @param gmtOffset
       * @param osName
       */
      private ManagedSystemInfo(int gmtOffset,String osName) {
  
          if ((osName == null) || (osName.length() == 0)) {
              m_osName = "";
          } else {
              m_osName = osName.trim();
          }
          
          m_gmtOffset = gmtOffset;
  
  
          logger.trace(String.format("ManagedSystemInfo CTOR( gmt=%d, osName=%s )...",
                  m_gmtOffset, m_osName));
      } 
  
      public final long getGmtOffset() {
          return (long) m_gmtOffset;
      } 
  
      public final String getOsName() {
          return m_osName;
      } 
  
      /**
       * The gmtOffset (in hours) can come two places: 
       * 1) the -g option
       * 2) the date and time from the message logs
       * @param gmtOffset
       */
      public final void setGmtOffset(long gmtOffset) {
          this.m_gmtOffset = (int) gmtOffset;
      } 
  
      /**
       * This method is the "front door", the primary interface that a caller has
       * to making updates to the analytic database to ensure that it is up to date
       * in its information about managed systems.
       *
       * @param ISource - datastore object which is associated 1:1 with a SOURCES Table row
       * @throws AdeException
       */
      public final void updateDataStore(ISource S) throws AdeException {
  
          logger.info(String.format("updateDataStore(%s) -->entry",
                  S.getSourceId())); 
          try {
              final UpdateManagedSystemAtomicTransaction atomicTrans = new UpdateManagedSystemAtomicTransaction(S);
              atomicTrans.execute();
          } catch (AdeException e) {
              throw e;
          } catch (Throwable t) {
              final String msg = String.format("updateDataStore(%s) caught unexpected throwable: %s",
                      S.getSourceId(), t.getMessage()); 
              logger.info(msg); 
              throw new AdeInternalException(msg, t); 
          }
          logger.info("updateDataStore() <--exit");
  
      } 
  
      private class UpdateManagedSystemAtomicTransaction extends AtomicTransaction {
          private ISource source;
          private boolean mySQL;
  
          public UpdateManagedSystemAtomicTransaction(ISource source) throws AdeException {
              super();
              this.source = source;
              final String driver = Ade.getAde().getConfigProperties().database().getDatabaseDriver();
  
              if ((DriverType.parseDriverType(driver) == DriverType.MY_SQL) ||
                  (DriverType.parseDriverType(driver) == DriverType.MARIADB))
                  mySQL = true;
              else
                  mySQL = false;
          }
  
          public boolean execute() throws AdeException {
              return ExtDataStoreUtils.executeAtomicTransaction(this);
          }
  
          @Override
          public boolean performAtomicTransaction() throws AdeException {
              try {
  		if (mySQL) 
                      execute("LOCK TABLES " + GroupsQueryImpl.MANAGED_SYSTEMS_TABLE + " WRITE");
  		else
                      execute("LOCK TABLE " + GroupsQueryImpl.MANAGED_SYSTEMS_TABLE + " IN EXCLUSIVE MODE");
  
                  m_dbqueryManagedSystem = lookupManagedSystemInfo(source);
                  if (m_dbqueryManagedSystem == null) {
                      addManagedSystem(source);
                  } else {
                      updateManagedSystem(source);
                  }
              } catch (SQLException e) {
                  logger.error("Error encountered executing the transaction.", e);
              }
              if (mySQL) {
                  try {
                      execute("UNLOCK TABLES");
                  } catch (SQLException e) {
                      logger.error("Error encountered unlocking the table.", e);
                  }
              }
  
              return true;
          }
  
          /**
           * Query the analytics database to obtain the current managed system-related information
           * for the managed system designated by the input Source.  If there is such information
           * in the database, return a second instance of the current class informed by those values,
           * so that the caller knows that it should pursue a "read and perhaps modify database"
           * logic path, based on comparing "this" instance with "that" (i.e., to determine what, if
           * any, changes are to be made in the database).
           *
           * Of course, there may well be no such information, as this may be driven on a "first
           * encounter", in which case we return a null "that", and the caller knows that it should
           * pursue a "create various database rows" logic path.
           *
           * SPECIAL NOTE:  Practice shows that none of these column values can really be
           *                trusted to have content.  These are set/reset by various
           *                untrusted parties (e.g., bulk load).  It is imperative that
           *                a null value be handled and that a non-null object M be
           *                returned so that the caller is not mislead into thinking that
           *                they need to do a (erroneous, redundant) database ADD operation
           *                when in fact the designated ManagedSystem exists in the database
           *                and it is really an UPDATE operation that should be performed.
           *                Hence the super-paranoid try catch logic.
           *
           * @param ISource - datastore Source object which is associated with a SOURCES Table row,
           *                 and *may* already be associated with a MANAGED_SYSTEMS Table row
           * @return instance of ManagedSystemInfo, or <null>
           * @throws AdeException                                                         
           */
          private ManagedSystemInfo lookupManagedSystemInfo(ISource S) throws AdeException {
  
              ManagedSystemInfo M = null;
              final String MANAGED_QUERY = String.format("SELECT * FROM MANAGED_SYSTEMS WHERE SOURCE_INTERNAL_ID=%s",
                      S.getSourceInternalId());
              ResultSet R = null;
              String osName;
              int gmtOffset;
  
              logger.trace(String.format("lookupManagedSystemInfo(%s) -->entry",
                      S.getSourceId())); 
              try {
                  R = executeScrollInsensitiveQuery(MANAGED_QUERY);
  
                  /********************************************************************************
                   * Here we make a vital distinction between a null result (query malfunction),
                   * and an empty result (query successful, managed system not found).  In the
                   * former case, we throw to prevent the caller from wrongly concluding that it
                   * should add a new (spurious) MANAGED_SYSTEMS row to the database.
                   *******************************************************************************/
                  if (R == null) {
                      final String msg = String.format("lookupManagedSystemInfo(%s) - unexpected null (ResultSet)",
                              S.getSourceId()); 
                      logger.error(msg); 
                      throw new AdeInternalException(msg); 
                  }
  
  