V1_0.java [src/java/d/crlmod/rusle2] Revision:   Date:
/*
 * $Id: 1.0+65 V1_0.java 402d39c37049 2021-12-29 od $
 *
 * This file is part of the Cloud Services Integration Platform (CSIP),
 * a Model-as-a-Service framework, API, and application suite.
 *
 * 2012-2024, OMSLab, Colorado State University.
 *
 * OMSLab licenses this file to you under the MIT license.
 * See the LICENSE file in the project root for more information.
 */
package d.crlmod.rusle2;

import crlmod.ServiceResources;
import static crlmod.ServiceResources.CR_LMOD_R2;
import csip.ModelDataService;
import csip.annotations.Description;
import csip.annotations.Name;
import csip.annotations.Resource;
import csip.annotations.State;
import static csip.annotations.State.RELEASED;
import csip.annotations.VersionInfo;
import csip.api.server.ServiceException;
import csip.utils.JSONUtils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import javax.ws.rs.Path;
import org.codehaus.jettison.json.JSONArray;

/**
 *
 * @author <a href="mailto:shaun.case@colostate.edu">Shaun Case</a>
 */
@Name("RUSLE2")
@Description("Retrieves XML data files for any RUSLE2 object")
@VersionInfo("1.0")
@State(RELEASED)
@Path("d/rusle2/1.0")
@Resource(from = ServiceResources.class)
public class V1_0 extends ModelDataService {

  static final String ID_KEY = "id";
  static final String NAME_KEY = "name";
  static final String PARTIAL_NAME_KEY = "partial_name";
  static final String PATH_KEY = "path";
  static final String PARTIAL_PATH_KEY = "partial_path";
  static final String LIMIT_KEY = "limit";

  private String[] ids;
  private String[] names;
  private String[] partial_names;
  private String[] paths;
  private String[] partial_paths;
  private int limit;

  private List<TableResult> searchResults = new ArrayList<>();

  @Override
  protected void preProcess() throws Exception {
    boolean haveValidRequest = false;

    //   If we have the ids input key, don't check the other keys, that would not
    // make logical sense to structure such a request.
    if (parameter().has(ID_KEY)) {
      ids = parameter().getStringArray(ID_KEY);
      haveValidRequest = true;
    } else {

      //  If we have a names list, no need to check for a partial names list
      if (parameter().has(NAME_KEY)) {
        names = parameter().getStringArray(NAME_KEY);
        haveValidRequest = true;
      } else {
        if (parameter().has(PARTIAL_NAME_KEY)) {
          partial_names = parameter().getStringArray(PARTIAL_NAME_KEY);
          haveValidRequest = true;
        }
      }

      //  If we have a paths list, no need to check for a partial paths list
      if (parameter().has(PATH_KEY)) {
        paths = parameter().getStringArray(PATH_KEY);
        haveValidRequest = true;
      } else {
        if (parameter().has(PARTIAL_PATH_KEY)) {
          partial_paths = parameter().getStringArray(PARTIAL_PATH_KEY);
          haveValidRequest = true;
        }
      }
    }

    // Shall we limit the return amount.  Really only useful if the client 
    // requests a list of objects by specifying an empty ids list input.
    limit = parameter().getInt(LIMIT_KEY, 0);

    if (!haveValidRequest)
      throw new ServiceException("Invalid input provided. Please check the required parameters documentation.");
  }

  @Override
  protected void doProcess() throws Exception {
    if ((null != ids) && (ids.length <= 0)) {
      // ************************************************************************
      // Client wants a list of available objects.  Ideally, this should not be
      // called by any model code.  This will return a huge amount of data and a
      // model should already know what it is looking for.
      //
      // This will return only a list of ID, Path, Name values, and will not
      // return any XML data (result would be too large otherwise)
      // ************************************************************************
      String query = "SELECT " + ((limit > 0) ? " TOP " + limit : "") + " id, path, name, scienceDate FROM R2_Data ORDER BY path, name;";
      try (Connection conn = resources().getJDBC(getJDBCId()); Statement stmnt = conn.createStatement();) {
        try (ResultSet results = stmnt.executeQuery(query)) {
          while (results.next()) {
            searchResults.add(new TableResult(results, false));
          }
        }
      }

    } else {
      if (null != ids) {
        //  Client wants data by Id
        String query = "SELECT " + ((limit > 0) ? " TOP " + limit : "") + " id, path, name, scienceDate, xmlData FROM R2_Data WHERE ";
        query += buildPreparedWhereIsList("id", ids) + " ORDER BY path, name;";
        try (Connection conn = resources().getJDBC(getJDBCId()); PreparedStatement stmnt = conn.prepareStatement(query)) {
          int counter = 1;
          for (String id : ids) {
            stmnt.setString(counter++, id);
          }
          try (ResultSet results = stmnt.executeQuery()) {
            while (results.next()) {
              searchResults.add(new TableResult(results, true));
            }
          }
        }
      } else {
        //  Client wants data by other fields.  Query other fields.
        String query = "SELECT " + ((limit > 0) ? " TOP " + limit : "") + " id, path, name, scienceDate, xmlData FROM R2_Data WHERE ";
        boolean add = false;

        if ((null != paths) && (paths.length >= 0)) {
          add = true;
          query += buildPreparedWhereIsList("path", paths);
        }
        if ((null != names) && (names.length >= 0)) {
          query += ((add) ? " AND " : " ") + buildPreparedWhereIsList("name", names);
          add = true;
        }
        if ((null != partial_paths) && (partial_paths.length >= 0)) {
          query += ((add) ? " AND " : " ") + buildPreparedWhereLikeList("path", partial_paths);
          add = true;
        }
        if ((null != partial_names) && (partial_names.length >= 0)) {
          query += ((add) ? " AND " : " ") + buildPreparedWhereLikeList("name", partial_names);
          add = true;
        }
        query += " ORDER BY path,name;";

        try (Connection conn = resources().getJDBC(getJDBCId()); PreparedStatement stmnt = conn.prepareStatement(query)) {
          int counter = 1;
          if ((null != paths) && (paths.length >= 0)) {
            for (String path : paths) {
              stmnt.setString(counter++, path);
            }
          }
          if ((null != names) && (names.length >= 0)) {
            for (String name : names) {
              stmnt.setString(counter++, name);
            }
          }
          if ((null != partial_paths) && (partial_paths.length >= 0)) {
            for (String path : partial_paths) {
              stmnt.setString(counter++, "%" + path + "%");
            }
          }
          if ((null != partial_names) && (partial_names.length >= 0)) {
            for (String name : partial_names) {
              stmnt.setString(counter++, "%" + name + "%");
            }
          }
          try (ResultSet results = stmnt.executeQuery()) {
            while (results.next()) {
              searchResults.add(new TableResult(results, true));
            }
          }
        }
      }
    }
  }

  @Override
  protected void postProcess() throws Exception {
    JSONArray rows = new JSONArray();
    for (TableResult row : searchResults) {
      rows.put(row.toJSON());
    }
    results().put("r2_data_list", rows);
  }

  protected String buildPreparedWhereIsList(String fieldName, String[] values) {
    String where = "";
    if ((null != values) && (values.length > 0)) {
      where = fieldName + " IN (";
      for (int i = 0; i < values.length; i++) {
        where += "?";
        if ((i + 1) < values.length)
          where += ",";
      }
      where += ")";
    }
    return where;
  }

  protected String buildPreparedWhereLikeList(String fieldName, String[] values) {
    String where = "";
    if ((null != values) && (values.length > 0)) {
      where = "(";
      for (int i = 0; i < values.length; i++) {
        where += fieldName + " LIKE ? ";
        if ((i + 1) < values.length)
          where += " OR ";
      }
      where += ")";
    }
    return where;
  }

  protected String getJDBCId() {
    return CR_LMOD_R2;
  }

  @Override
  protected Map<String, Object> getConfigInfo() {
    return new LinkedHashMap<String, Object>() {
      {
        put(getJDBCId(), resources().getResolved(getJDBCId()));
      }
    };
  }

  private class TableResult {

    String id;
    String path;
    String name;
    String scienceDate;
    String XML = null;

    TableResult(ResultSet results, boolean getXML) throws SQLException {
      id = results.getString("id");
      path = results.getString("path");
      name = results.getString("name");
      scienceDate = results.getString("scienceDate");
      if (getXML)
        XML = results.getString("xmlData");
    }

    public JSONArray toJSON() {
      JSONArray result = new JSONArray();
      result.put(JSONUtils.data(ID_KEY, id, "The unique id for this path/name combination.", null));
      result.put(JSONUtils.data(PATH_KEY, path, "The path for this object", null));
      result.put(JSONUtils.data(NAME_KEY, name, "The name for this object.", null));
      result.put(JSONUtils.data("science_date", scienceDate, "The date associated with this version of the XML file in this record.", null));
      result.put(JSONUtils.data("xml_file", XML, "The formatted XML file, if any, for this path/name combination..", null));
      return result;
    }
  }
}