content: 
  /*
   * Copyright Contributors to the OpenCue Project
   *
   * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
   * in compliance with the License. You may obtain a copy of the License at
   *
   * http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software distributed under the License
   * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
   * or implied. See the License for the specific language governing permissions and limitations under
   * the License.
   */
  
  package com.imageworks.spcue.dao.postgres;
  
  import java.sql.CallableStatement;
  import java.sql.Connection;
  import java.sql.ResultSet;
  import java.sql.SQLException;
  import java.sql.Savepoint;
  import java.util.ArrayList;
  import java.util.Map;
  import java.util.regex.Pattern;
  
  import org.springframework.dao.DataIntegrityViolationException;
  import org.springframework.jdbc.core.CallableStatementCreator;
  import org.springframework.jdbc.core.RowMapper;
  import org.springframework.jdbc.core.SqlParameter;
  import org.springframework.jdbc.core.support.JdbcDaoSupport;
  
  import com.imageworks.spcue.AllocationEntity;
  import com.imageworks.spcue.AllocationInterface;
  import com.imageworks.spcue.EntityRemovalError;
  import com.imageworks.spcue.FacilityInterface;
  import com.imageworks.spcue.dao.AllocationDao;
  import com.imageworks.spcue.util.SqlUtil;
  
  public class AllocationDaoJdbc extends JdbcDaoSupport implements AllocationDao {
  
      public static RowMapper<AllocationEntity> ALLOC_MAPPER = new RowMapper<AllocationEntity>() {
          public AllocationEntity mapRow(ResultSet rs, int rowNum) throws SQLException {
              AllocationEntity alloc = new AllocationEntity();
              alloc.id = rs.getString("pk_alloc");
              alloc.facilityId = rs.getString("pk_facility");
              alloc.name = rs.getString("str_name");
              alloc.tag = rs.getString("str_tag");
              return alloc;
          }
      };
  
      private static final String GET_ALLOCATION = "SELECT " + "alloc.pk_facility,"
              + "alloc.pk_alloc, " + "alloc.str_name, " + "alloc.str_tag, "
              + "facility.str_name AS facility_name " + "FROM " + "alloc, " + "facility " + "WHERE "
              + "alloc.pk_facility = facility.pk_facility ";
  
      public AllocationEntity getAllocationEntity(String id) {
          return getJdbcTemplate().queryForObject(GET_ALLOCATION + " AND pk_alloc=?", ALLOC_MAPPER,
                  id);
      }
  
      public AllocationEntity findAllocationEntity(String facility, String name) {
          return getJdbcTemplate().queryForObject(GET_ALLOCATION + " AND alloc.str_name=?",
                  ALLOC_MAPPER, String.format("%s.%s", facility, name));
      }
  
      @Override
      public AllocationEntity findAllocationEntity(String name) {
          return getJdbcTemplate().queryForObject(GET_ALLOCATION + " AND alloc.str_name=?",
                  ALLOC_MAPPER, name);
      }
  
      private static final String INSERT_ALLOCATION = "INSERT INTO " + "alloc " + "(" + "pk_alloc,"
              + "pk_facility," + "str_name, " + "str_tag " + ") VALUES (?,?,?,?)";
  
      public void insertAllocation(FacilityInterface facility, AllocationEntity detail) {
  
          String new_alloc_name = String.format("%s.%s", facility.getName(), detail.getName());
          /*
           * Checks if the allocation already exits.
           */
          if (getJdbcTemplate().queryForObject("SELECT COUNT(1) FROM alloc WHERE str_name=?",
                  Integer.class, new_alloc_name) > 0) {
  
              getJdbcTemplate().update("UPDATE alloc SET b_enabled = true WHERE str_name=?",
                      new_alloc_name);
          } else {
              detail.id = SqlUtil.genKeyRandom();
              detail.name = new_alloc_name;
              getJdbcTemplate().update(INSERT_ALLOCATION, detail.id, facility.getFacilityId(),
                      detail.name, detail.tag);
          }
      }
  
      public void deleteAllocation(AllocationInterface a) {
          if (getJdbcTemplate().queryForObject("SELECT COUNT(1) FROM host WHERE pk_alloc=?",
                  Integer.class, a.getAllocationId()) > 0) {
              throw new EntityRemovalError("allocation still contains hosts", a);
          }
  
          if (getJdbcTemplate().queryForObject("SELECT b_default FROM alloc WHERE pk_alloc=?",
                  Boolean.class, a.getAllocationId())) {
              throw new EntityRemovalError("you cannot delete the default allocation", a);
          }
  
          Savepoint sp1;
          try {
              sp1 = getConnection().setSavepoint();
          } catch (SQLException e) {
              throw new RuntimeException("failed to create savepoint", e);
          }
  
          /*
           * Allocations are logged in historical data so once they are used you can't specifically
           * delete them. They are disabled instead.
           */
          try {
              getJdbcTemplate().update("DELETE FROM alloc WHERE pk_alloc=?", a.getAllocationId());
          } catch (DataIntegrityViolationException e) {
              try {
                  getConnection().rollback(sp1);
              } catch (SQLException e1) {
                  throw new RuntimeException("failed to roll back failed delete", e);
              }
              getJdbcTemplate().update("UPDATE alloc SET b_enabled = false WHERE pk_alloc = ?",
                      a.getAllocationId());
          }
      }
  
      public void updateAllocationName(AllocationInterface a, String name) {
          if (!Pattern.matches("^\\w+$", name)) {
              throw new IllegalArgumentException("The new allocation name"
                      + "must be alpha numeric and not contain the facility prefix.");
          }
  
          String[] parts = a.getName().split("\\.", 2);
          String new_name = String.format("%s.%s", parts[0], name);
  
          getJdbcTemplate().update("UPDATE alloc SET str_name=? WHERE pk_alloc=?", new_name,
                  a.getAllocationId());
      }
  
      public void updateAllocationTag(AllocationInterface a, String tag) {
          getJdbcTemplate().update("UPDATE alloc SET str_tag=? WHERE pk_alloc=?", tag,
                  a.getAllocationId());
  
          getJdbcTemplate().update(
                  "UPDATE host_tag SET str_tag=? WHERE "
                          + "host_tag.str_tag_type='Alloc' AND pk_host IN "
                          + "(SELECT pk_host FROM host WHERE host.pk_alloc=?)",
                  tag, a.getAllocationId());
  
          for (Map<String, Object> e : getJdbcTemplate()
                  .queryForList("SELECT pk_host FROM host WHERE pk_alloc=?", a.getAllocationId())) {
              final String pk_host = (String) e.get("pk_host");
              getJdbcTemplate().call(new CallableStatementCreator() {
                  public CallableStatement createCallableStatement(Connection con)
                          throws SQLException {
                      CallableStatement c = con.prepareCall("{ call recalculate_tags(?) }");
                      c.setString(1, pk_host);
                      return c;
                  }
              }, new ArrayList<SqlParameter>());
          }
      }
  
      public void setDefaultAllocation(AllocationInterface a) {
          getJdbcTemplate().update("UPDATE alloc SET b_default = false WHERE b_default = true");
          getJdbcTemplate().update("UPDATE alloc SET b_default = true WHERE pk_alloc=?",
                  a.getAllocationId());
      }
  
      public AllocationEntity getDefaultAllocationEntity() {
          return getJdbcTemplate().queryForObject(
                  GET_ALLOCATION + " AND alloc.b_default = true LIMIT 1", ALLOC_MAPPER);
      }
  
      @Override
      public void updateAllocationBillable(AllocationInterface alloc, boolean value) {
          getJdbcTemplate().update("UPDATE alloc SET b_billable = ? WHERE pk_alloc = ?", value,
                  alloc.getAllocationId());
  
      }
  }
  