public class CreateDBParameterGroupActionRequestUnmarshaller
    implements Unmarshaller<CreateDBParameterGroupActionRequestMessage, Map<String, String[]>> {
  private static Logger logger =
      Appctx.getLogger(CreateDBParameterGroupActionRequestUnmarshaller.class.getName());

  private static CreateDBParameterGroupActionRequestUnmarshaller instance;

  public static CreateDBParameterGroupActionRequestUnmarshaller getInstance() {
    if (instance == null) {
      instance = new CreateDBParameterGroupActionRequestUnmarshaller();
    }
    return instance;
  }

  /*
   * (non-Javadoc)
   *
   * @see com.amazonaws.transform.Unmarshaller#unmarshall(java.lang.Object)
   */
  @Override
  public CreateDBParameterGroupActionRequestMessage unmarshall(Map<String, String[]> in) {
    final CreateDBParameterGroupActionRequestMessage.Builder req =
        CreateDBParameterGroupActionRequestMessage.newBuilder();
    logger.debug("Unmarshalling (inbound) CreateDBParameterGroup");
    req.setDbParameterGroupFamily(QueryUtilV2.getString(in, "DBParameterGroupFamily"));
    req.setDbParameterGroupName(QueryUtilV2.getString(in, "DBParameterGroupName"));
    req.setDescription(QueryUtilV2.getString(in, "Description"));
    return req.buildPartial();
  }
}
  public static AccessMySQL getInstance() throws InternalErrorException {
    if (access == null) {
      String userName = (String) Appctx.getBean("DB_USERNAME");
      String password = (String) Appctx.getBean("DB_PASSWORD");
      String jdbctarget = (String) Appctx.getBean("BIND_DB_URL");
      logger.debug(
          "Initializing a new connection with JDBC = "
              + "{ Username : "******", Password : "******", URL : "
              + jdbctarget
              + " }");

      access = new AccessMySQL(userName, password, jdbctarget);
    }
    return access;
  }
public class CreateServiceAction extends UnsecuredAction {

  private final Logger logger = Appctx.getLogger(CreateServiceAction.class.getName());

  @Override
  public String process0(
      final Session s,
      final HttpServletRequest req,
      final HttpServletResponse resp,
      final Map<String, String[]> map)
      throws Exception {

    final String hostname = QueryUtil.getString(map, "hostname");
    final String inport = QueryUtil.getString(map, "inport");
    final String outport = QueryUtil.getString(map, "outport");
    final String backend = QueryUtil.getString(map, "backend");
    if (hostname == null || inport == null || outport == null || backend == null) {
      return "All paremeters not sent";
    }
    SQLQuery q =
        s.createSQLQuery(
            "select * from ts_inst_lb  where hostnm='"
                + hostname
                + "' and inport="
                + inport
                + " and outport="
                + outport
                + " and  backend='"
                + backend
                + "'");
    if (q.list().size() > 0) {
      return "Already Exists";
    }
    SQLQuery i =
        s.createSQLQuery(
            "insert into ts_inst_lb (hostnm,inport,outport,backend) values ('"
                + hostname
                + "',"
                + inport
                + ","
                + outport
                + ",'"
                + backend
                + "')");
    i.executeUpdate();
    ServiceLBUtil.reconfigure(s);
    return "CREATED";
  }
}
public class SignalCreateDBSnapshotResultMarshaller
    implements Marshaller<String, SignalCreateDBSnapshotResultMessage> {
  private static final Logger logger =
      Appctx.getLogger(SignalCreateDBSnapshotResultMarshaller.class.getName());

  /*
   * (non-Javadoc)
   *
   * @see com.amazonaws.transform.Marshaller#marshall(java.lang.Object)
   */
  @Override
  public String marshall(final SignalCreateDBSnapshotResultMessage input) {
    logger.debug("Finished SignalCreateDBSnapshot implementation");
    final XMLNode nodeResponse = new XMLNode(RDS_Constants.NODE_DESCRIBEEVENTSRESPONSE);

    return nodeResponse.toString();
  }
}
public class DeleteDBSecurityGroupActionTest extends AbstractBaseRdsTest {

  private static Logger logger = Appctx.getLogger(DeleteDBSecurityGroupActionTest.class.getName());

  private final String baseName = UUID.randomUUID().toString().substring(0, 8);

  String name1 = "rds-delSecGp-3-" + baseName;

  // DBSecurityGroupName is required
  @Test(expected = AmazonServiceException.class)
  public void testDeleteDBSecurityGroupNoArgs() throws Exception {
    final DeleteDBSecurityGroupRequest request = new DeleteDBSecurityGroupRequest();
    getRdsClient().deleteDBSecurityGroup(request);
  }

  @Test(expected = AmazonServiceException.class)
  public void testDeleteDBSecurityGroupInvalidParameters() throws Exception {
    final DeleteDBSecurityGroupRequest request = new DeleteDBSecurityGroupRequest();
    request.withDBSecurityGroupName("No Group");
    getRdsClient().deleteDBSecurityGroup(request);
  }

  @Test
  public void testGoodDeleteDBSecurityGroup() throws Exception {
    logger.info("Creating good DBSecGroup");
    final CreateDBSecurityGroupRequest request = new CreateDBSecurityGroupRequest();
    request.withDBSecurityGroupDescription("TestSecGroup");
    request.withDBSecurityGroupName(name1);
    getRdsClient().createDBSecurityGroup(request);

    logger.info("Deleting DBSecGroup with name = " + name1);
    final DeleteDBSecurityGroupRequest delRequest = new DeleteDBSecurityGroupRequest();
    delRequest.withDBSecurityGroupName(name1);
    getRdsClient().deleteDBSecurityGroup(delRequest);
  }
}
/** Address helper for non-web tests (using actions in-VM). */
@Component
public class CreateSecurityGroupLocalHelper {
  private static Logger logger = Appctx.getLogger(CreateSecurityGroupLocalHelper.class.getName());

  private List<String> mSecurityGroups = new ArrayList<String>();

  private static CreateSecurityGroupWorker mCreateSecurityGroupWorker = null;
  private static DeleteSecurityGroupWorker deleteSecurityGroupWorker = null;

  private static ActionTestHelper mActionHelper = null;

  /**
   * Construct a minimal valid create request.
   *
   * @param sgName security group name
   * @param sgDescription security group description
   * @return
   */
  public CreateSecurityGroupRequest createSecurityGroupRequest(
      String sgName, String sgDescription) {
    final CreateSecurityGroupRequest.Builder builder = CreateSecurityGroupRequest.newBuilder();
    builder.setTypeId(true);
    builder.setCallerAccessKey(mActionHelper.getAccessKey());
    builder.setRequestId("test");
    builder.setGroupName(sgName);
    builder.setGroupDescription(sgDescription);
    return builder.build();
  }

  /**
   * Create a security group (don't care about the details, just need one).
   *
   * @return: String groupId
   */
  public String createSecurityGroup() throws Exception {

    final String suffix = UUID.randomUUID().toString().substring(0, 8);
    final String groupName = this.getTestGroupName(suffix);
    final String groupDescription = this.getTestGoupDescription(suffix);

    CreateSecurityGroupRequest req = createSecurityGroupRequest(groupName, groupDescription);
    CreateSecurityGroupResponse response = null;

    response = mCreateSecurityGroupWorker.doWork(req);
    String groupId = response.getGroupId();
    logger.debug("Created GroupID: " + groupId);
    mSecurityGroups.add(groupId);
    return groupId;
  }

  /**
   * Perform operations necessary to complete a full integration test.
   *
   * @return: String groupId
   */
  public String integrationTest(AmazonEC2Client computeClient)
      throws AmazonServiceException, AmazonClientException {
    final String suffix = UUID.randomUUID().toString().substring(0, 8);
    final String groupName = this.getTestGroupName(suffix);
    final String groupDescription = this.getTestGoupDescription(suffix);
    CreateSecurityGroupResult createResult = null;
    final com.amazonaws.services.ec2.model.CreateSecurityGroupRequest ec2request =
        new com.amazonaws.services.ec2.model.CreateSecurityGroupRequest();
    ec2request.setGroupName(groupName);
    ec2request.setDescription(groupDescription);

    logger.info("Creating Security Group");
    createResult = computeClient.createSecurityGroup(ec2request);
    mSecurityGroups.add(groupName);
    final String groupId = createResult.getGroupId();
    assertNotNull("Expect a group id.", groupId);

    return groupId;
  }

  public String getTestGroupName(String suffix) {
    return "sgLocal-test-" + suffix;
  }

  public String getTestGoupDescription(String suffix) {
    return "sgLocal-test-" + suffix + " create group test.";
  }

  /**
   * Delete a group with the given name.
   *
   * @param groupId
   */
  public void deleteSecurityGroup(String groupId) throws Exception {
    DeleteSecurityGroupRequestMessage.Builder req = DeleteSecurityGroupRequestMessage.newBuilder();
    req.setTypeId(true);
    req.setCallerAccessKey(mActionHelper.getAccessKey());
    req.setRequestId("test");
    req.setGroupId(groupId);
    deleteSecurityGroupWorker.doWork(req.build());
  }

  /** Delete all created groups. */
  public void deleteAllSecurityGroups() throws Exception {
    for (String groupName : mSecurityGroups) {
      deleteSecurityGroup(groupName);
    }
    mSecurityGroups.clear();
  }

  @Autowired(required = true)
  public void setActionTestHelper(ActionTestHelper actionTestHelper) {
    CreateSecurityGroupLocalHelper.mActionHelper = actionTestHelper;
  }

  @Autowired(required = true)
  public void setCreateSecurityGroupWorker(CreateSecurityGroupWorker createSecurityGroupWorker) {
    CreateSecurityGroupLocalHelper.mCreateSecurityGroupWorker = createSecurityGroupWorker;
  }

  @Autowired(required = true)
  public void setDeleteSecurityGroupWorker(DeleteSecurityGroupWorker deleteSecurityGroupWorker) {
    CreateSecurityGroupLocalHelper.deleteSecurityGroupWorker = deleteSecurityGroupWorker;
  }
}
public class PutServiceHealthEvent extends AbstractAction<Object> {
  private static final Logger logger = Appctx.getLogger(PutServiceHealthEvent.class.getName());
  private static final String RootNodeName = "PutServiceHealthEvent";
  private static final String RootNodeNameResult = "PutServiceHealthEventResult";

  private String serviceAbbreviation;
  private String region;
  private String availabilityZone;
  private String healthEventDescription;
  private ServiceHealthStatus serviceHealthStatus;

  //    private ServiceBean serviceBean ;
  private ServiceHealthEventBean serviceHealthEventBean;

  // This action is not AWS Compatible, so the Marshall/UnMarshall code
  // is defined here, not related to the AWS SDK

  public String marshall(MarshallStruct<Object> input, HttpServletResponse resp) throws Exception {
    logger.debug("Marshalling PutServiceHealthEvent...");

    XMLNode messageNode = new XMLNode(RootNodeName);

    XMLNode resultWrapNode = QueryUtil.addNode(messageNode, RootNodeNameResult);

    MarshallingUtils.marshallServiceHealthEvent(resultWrapNode, serviceHealthEventBean);

    input.addResponseMetadata(messageNode, null);

    return messageNode.toString();
  }

  public void unmarshall(HttpServletRequest req, Map<String, String[]> map) {
    logger.debug("Unmarshalling PutServiceHealthEvent");

    serviceAbbreviation = QueryUtil.requiredString(map, "Service");

    region = QueryUtil.getString(map, "Region");

    availabilityZone = QueryUtil.getString(map, "AvailabilityZone");

    final String status = QueryUtil.requiredString(map, "Status");
    serviceHealthStatus = ServiceHealthStatus.valueOf(status);

    healthEventDescription = req.getParameter("HealthEventDescription");
    if (healthEventDescription == null || healthEventDescription.length() == 0) {
      throw ErrorResponse.invlidData("HealthEventDescription is required");
    }
  }

  @Override
  public Object process0(
      Session session, HttpServletRequest req, HttpServletResponse resp, Map<String, String[]> map)
      throws Exception {
    logger.debug("PutServiceHealthEvent Process0");

    unmarshall(req, map);

    ServiceBean serviceBean =
        (ServiceBean)
            session
                .createCriteria(ServiceBean.class)
                .add(Restrictions.eq("serviceNameAbbreviation", serviceAbbreviation))
                .uniqueResult();

    if (null == serviceBean) {
      throw ErrorResponse.invlidData("Service is invalid");
    }

    /*
            ServiceHealthEventBean preExistingString = (ServiceHealthEventBean)session
            		.createCriteria(ServiceHealthEventBean.class)
            		.add(Restrictions.eq("serviceHealthEventDescription", healthEventDescription))
            		.uniqueResult() ;

        	if( preExistingString != null )
        	{
            	logger.debug("Found existing record:" + preExistingString.getId());

        		serviceHealthEventBean = preExistingString ;

        		return Constants.EMPTYSTRING;
        	}
    */

    serviceHealthEventBean =
        new ServiceHealthEventBean(
            serviceBean, region, availabilityZone, healthEventDescription, serviceHealthStatus);

    try {
      // The goal was to keep duplicate strings from our list
      // Hibernate does enforce unique constraints on Save(), but
      // it seemed the session wasn't active and could not lookup
      // the ID of the item that caused the constraint violation to
      // prevent duplicate strings
      session.save(serviceHealthEventBean);
    } catch (Exception ex) {
      logger.debug("PutServiceHealthEvent exception on save");
      logger.debug(ex.getMessage());
    }
    logger.info("-------------------------------------------------");

    return MonitorConstants.EMPTYSTRING;
  }
}
public class DescribeDBParameterGroupsActionWorker
    extends AbstractWorker<
        DescribeDBParameterGroupsActionRequestMessage,
        DescribeDBParameterGroupsActionResultMessage> {
  private static final Logger logger =
      Appctx.getLogger(DescribeDBParameterGroupsActionWorker.class.getName());

  /**
   * We need a local copy of this doWork to provide the transactional annotation. Transaction
   * management is handled by the annotation, which can only be on a concrete class.
   *
   * @param req
   * @return
   * @throws Exception
   */
  @Transactional
  public DescribeDBParameterGroupsActionResultMessage doWork(
      DescribeDBParameterGroupsActionRequestMessage req) throws Exception {
    logger.debug("Performing work for DescribeDBParameterGroupsAction.");
    return super.doWork(req, getSession());
  }

  /*
  	 * (non-Javadoc)
  	 *
  	 * @see com.msi.tough.query.AbstractProxyAction#
  	protected DescribeDBParameterGroupsActionResultMessage doWork0(DescribeDBParameterGroupsActionRequestMessage req,
  ServiceRequestContext context) throws Exception {

  	 * javax.servlet.http.HttpServletRequest,
  	 * javax.servlet.http.HttpServletResponse, java.util.Map)
  	 */
  @Override
  protected DescribeDBParameterGroupsActionResultMessage doWork0(
      DescribeDBParameterGroupsActionRequestMessage req, ServiceRequestContext context)
      throws Exception {

    DescribeDBParameterGroupsActionResultMessage.Builder resp = null;
    String msg = "";
    final Session sess = HibernateUtil.newSession();
    try {
      sess.beginTransaction();
      final AccountBean ac = context.getAccountBean();
      final long userID = ac.getId();
      final String grpName = req.getDbParameterGroupName();
      final String marker = req.getMarker();
      final int maxRecords = ValidationManager.validateMaxRecords(req.getMaxRecords(), false);

      logger.info(
          "DescribeDBParameterGroups: "
              + " account = "
              + ac.getId()
              + " DBParameterGroupName = "
              + grpName
              + " Marker = "
              + marker
              + " MaxRecords = "
              + maxRecords);

      // select the list of DBParameterGroups.
      final List<RdsDbparameterGroup> result =
          ParameterGroupEntity.selectDBParameterGroups(
              sess, grpName, ac.getId(), marker, maxRecords);

      if (grpName != null && (result == null || result.size() == 0)) {
        throw RDSQueryFaults.DBParameterGroupNotFound();
      }

      // build response document
      final List<DBParameterGroup> grl = new ArrayList<DBParameterGroup>();
      if (result != null) {
        logger.debug(
            result.size() + " DBParameterGroups are selected from custom DBParameterGroups.");
        for (final RdsDbparameterGroup gb : result) {
          grl.add(toDBParameterGroup(gb));
        }
      }
      resp = DescribeDBParameterGroupsActionResultMessage.newBuilder();
      resp.addAllDbParameterGroups(grl);
      sess.getTransaction().commit();
    } catch (final ErrorResponse rde) {
      sess.getTransaction().rollback();
      throw rde;
    } catch (final Exception e) {
      e.printStackTrace();
      sess.getTransaction().rollback();
      msg = "DescribeDBParameterGroups: Class: " + e.getClass() + "Msg:" + e.getMessage();
      logger.error(msg);
      throw RDSQueryFaults.InternalFailure();
    } finally {
      sess.close();
    }
    return resp.buildPartial();
  }

  public static DBParameterGroup toDBParameterGroup(final RdsDbparameterGroup p) {
    final DBParameterGroup.Builder g = DBParameterGroup.newBuilder();
    g.setDbParameterGroupFamily(p.getDbparameterGroupFamily());
    g.setDbParameterGroupName(p.getDbparameterGroupName());
    g.setDescription(p.getDescription());
    return g.buildPartial();
  }
}
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"/test-toughcore-context.xml"})
@TransactionConfiguration(transactionManager = "transactionManager", defaultRollback = false)
@TestExecutionListeners({DependencyInjectionTestExecutionListener.class})
public class ChefUtilTest {

  private static final Logger logger = Appctx.getLogger(ChefUtilTest.class.getName());

  static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss-SSS");
  private static final String baseName =
      dateFormat.format(new Date()) + UUID.randomUUID().toString().substring(0, 4);

  static String name1 = "chefutil-" + baseName;
  static String name2 = "chefutil2-" + baseName;

  static String bag1 = "chefutil-bag1-" + baseName;
  static String bag2 = "chefutil-bag2-" + baseName;

  @Resource ChefUtil chefUtil = null;

  @Test
  public void testClient() throws Exception {
    String json = ChefUtil.createClient(name1);
    assertThat("Create returns private key.", json, containsString("BEGIN"));
    assertThat("Create returns private key.", json, containsString("END"));
    json = ChefUtil.getClient(name1);
    assertThat("Client is created.", json, containsString("\"name\":\"" + name1));
    json = ChefUtil.putClientAsAdmin(name1);
    assertThat("Client can be made admin.", json, containsString("\"admin\":true"));
    json = ChefUtil.getClient(name1);
    assertThat("Client is admin.", json, containsString("\"admin\":true"));
  }

  @Test(expected = ChefUtil.InvalidChefRequest.class)
  public void testClientBadArgs() throws Exception {
    String oldClientId = chefUtil.getChefClientId();
    try {
      chefUtil.setChefClientId("some-bad-garbage-id");

      String json = ChefUtil.createClient(name2);
      logger.debug("Got response:" + json);
      json = ChefUtil.getClient(name2);
      logger.debug("Got response:" + json);
    } finally {
      chefUtil.setChefClientId(oldClientId);
    }
  }

  @Test
  public void testDatabag() throws Exception {
    String json = ChefUtil.createDatabag(bag1);
    assertThat("Databag is created.", json, containsString(bag1));

    json = ChefUtil.createDatabagItem(bag1, "item1");
    assertThat("Databag item is created.", json, containsString("\"id\":\"item1\""));
    final String databag = ChefUtil.getDatabag(bag1);
    logger.debug("Got databag:" + databag);
    // final JsonNode json = JsonUtil.load(databag);
    ChefUtil.putDatabagItem(bag1, "item1", "{\"id\":\"item1\",\"f\":\"fff\"}");
    json = ChefUtil.getDatabagItem(bag1, "item1");
    assertThat("Databag item put ok.", json, containsString("\"f\":\"fff\""));

    ChefUtil.deleteDatabagItem(bag1, "item1");
    ChefUtil.deleteDatabag(bag1);
  }

  @Test
  public void testDatabagAllInOne() throws Exception {
    final String bagContent = "{\"One\":\"one\", " + "\"Two\":\"two\"}";
    ChefUtil.createDatabagItem(bag1, "item1", bagContent);
    ChefUtil.createDatabagItem(bag1, "item2", bagContent);

    String json = ChefUtil.getDatabagItem(bag1, "item1");
    assertThat("Databag is created, item set.", json, containsString("Two"));

    ChefUtil.deleteDatabagItem(bag1, "item1");
    ChefUtil.deleteDatabag(bag1);
  }

  @Test
  public void testNode() throws Exception {
    String jsonText = ChefUtil.createNode(name1);
    ChefUtil.putNodeAttribute(name1, "__TRANSCEND_TEST__", name1);
    logger.debug(jsonText);
    assertThat("Node is created.", jsonText, containsString(name1));
    jsonText = ChefUtil.getNode(name1);
    logger.debug(jsonText);
    JsonNode json = JsonUtil.load(jsonText);
    // logger.debug(JsonUtil.toJsonPrettyPrintString(json));
    assertThat("Node name is correct.", json.get("name").getTextValue(), is(name1));
  }

  @Test
  public void testNodeUpdateRunlist() throws Exception {
    try {
      ChefUtil.createNode(name1);
      ChefUtil.putNodeAttribute(name1, "__TRANSCEND_TEST__", name1);
    } catch (Exception e) {
      // ignore failure; may have already been created.
    }

    String jsonText = ChefUtil.putNodeRunlist(name1, "role[transcend_defaultrole]");
    logger.debug(jsonText);
    jsonText = ChefUtil.getNode(name1);
    JsonNode json = JsonUtil.load(jsonText);
    logger.debug(JsonUtil.toJsonPrettyPrintString(json));
    assertThat("Node name is correct.", json.get("name").getTextValue(), is(name1));
    assertThat(
        "Node attributes are intact.",
        json.get("normal").toString(),
        containsString("__TRANSCEND_TEST__"));
  }

  @Test
  public void testBasicGet() throws Exception {
    final String json = ChefUtil.executeJson("GET", "/clients", "");
    assertNotNull("client response is valid", json);
  }

  @Test
  public void testSearchNode() throws Exception {
    try {
      ChefUtil.createNode(name1);
      ChefUtil.putNodeAttribute(name1, "__TRANSCEND_TEST__", name1);
    } catch (Exception e) {
      // ignore failure; may have already been created.
    }

    // Search for :"name:chefutil-*";
    String search = "name%3A" + name1.substring(0, 9) + "*";
    final List<String> nodes = ChefUtil.searchNodes(search);

    // final JsonNode jsonNodes = JsonUtil.load(nodes);
    for (String node : nodes) {
      JsonNode json = JsonUtil.load(node);
      logger.debug(JsonUtil.toJsonPrettyPrintString(json));
    }
  }

  @Test
  public void testDeletePattern() throws Exception {

    // Search for :"name:chefutil-*";
    String search = "name%3A" + name1.substring(0, 9) + "*";
    search = "name%3A" + "rds-default*";
    final List<String> nodes = ChefUtil.searchNodes(search);

    // final JsonNode jsonNodes = JsonUtil.load(nodes);
    for (String node : nodes) {
      JsonNode json = JsonUtil.load(node);
      ChefUtil.deleteNode(json.get("name").getTextValue());
      logger.debug("Deleted: " + json.get("name").getTextValue());
    }
    search = "name%3A" + name1.substring(0, 9) + "*";
    search = "name%3A" + "rds-*";
    final List<String> clients = ChefUtil.searchClients(search);

    // final JsonNode jsonNodes = JsonUtil.load(nodes);
    for (String client : clients) {
      JsonNode json = JsonUtil.load(client);
      ChefUtil.deleteClient(json.get("name").getTextValue());
      logger.debug("Deleted: " + json.get("name").getTextValue());
    }
  }

  @AfterClass
  public static void cleanupCreated() throws Exception {
    try {
      ChefUtil.deleteClient(name1);
    } catch (Exception e) {
      // ignore.
    }
    try {
      ChefUtil.deleteDatabag(bag1);
    } catch (Exception e) {
      // ignore.
    }
    try {
      ChefUtil.deleteNode(name1);
    } catch (Exception e) {
      // ignore.
    }
  }
}
public class AccessMySQL implements AccessDB {
  private static final Logger logger = Appctx.getLogger(AccessMySQL.class.getName());
  private Connection sqlConnection;
  private static AccessMySQL access;

  /**
   * Constructor for AccessMySQL
   *
   * @param userName username used to access MySQL database
   * @param password password used to access MySQL database
   * @param jdbctarget
   * @throws InternalErrorException
   */
  private AccessMySQL(String userName, String password, String jdbctarget) throws ErrorResponse {
    this.loadDriver();
    this.sqlConnection = initConnection(userName, password, jdbctarget);
  }

  public static AccessMySQL getInstance() throws InternalErrorException {
    if (access == null) {
      String userName = (String) Appctx.getBean("DB_USERNAME");
      String password = (String) Appctx.getBean("DB_PASSWORD");
      String jdbctarget = (String) Appctx.getBean("BIND_DB_URL");
      logger.debug(
          "Initializing a new connection with JDBC = "
              + "{ Username : "******", Password : "******", URL : "
              + jdbctarget
              + " }");

      access = new AccessMySQL(userName, password, jdbctarget);
    }
    return access;
  }

  public static AccessMySQL initialize(String userName, String password, String jdbctarget)
      throws InternalErrorException {
    logger.debug(
        "Initializing a new connection with JDBC = "
            + "{ Username : "******", Password : "******", URL : "
            + jdbctarget
            + " }");
    access = new AccessMySQL(userName, password, jdbctarget);
    return access;
  }

  /** @return Connection object used to establish the connection with mysql server */
  public Connection getConnection() {
    return this.sqlConnection;
  }

  /**
   * Load the jdbc driver for MySQL server
   *
   * @throws InternalErrorException
   */
  @Override
  public void loadDriver() throws ErrorResponse {
    try {
      Class.forName("com.mysql.jdbc.Driver").newInstance();
    } catch (Exception e) {
      e.printStackTrace();
      throw DNS53Faults.InternalError(); // just put some bogus UUID
      // unless RequestID becomes
      // important this layer
    }
  }

  /**
   * @param userName username used to access MySQL database
   * @param password password used to access MySQL database
   * @param jdbctarget
   * @return Connection object with established connection to MySQL server
   * @throws InternalErrorException
   */
  private Connection initConnection(String userName, String password, String jdbctarget)
      throws ErrorResponse {
    Connection conn = null;
    try {
      Properties connectionProps = new Properties();
      connectionProps.put("user", userName);
      if (password != null || password != "") {
        connectionProps.put("password", password);
      }
      conn = DriverManager.getConnection(jdbctarget, connectionProps); // TODO
      // make
      // this
      // installation
      // dependent
    } catch (SQLException e) {
      logger.debug("Failed to establish a connection.");
      e.printStackTrace();
      throw DNS53Faults.InternalError(); // just put some bogus UUID
      // unless RequestID becomes
      // important this layer
    }
    return conn;
  }

  /**
   * Close the connection used by AccessMySQL object
   *
   * @throws InternalErrorException
   */
  @Override
  public void closeConnection() throws ErrorResponse {
    try {
      this.sqlConnection.close();
      // logger.debug("Connection closed succesfully.");
    } catch (SQLException e) {
      // logger.debug("Failed to close the connection.");
      e.printStackTrace();
      throw DNS53Faults.InternalError(); // just put some bogus UUID
      // unless RequestID becomes
      // important this layer
    }
  }

  /**
   * Populate the new hosted zone data into MySQL database
   *
   * @param domainName name of the hosted zone
   * @param callerRef unique caller reference for this request
   * @param comment user's comment for this request
   * @return ID of the new hosted zone (automatically issued); "DUPLICATE_NAME" is returned if
   *     target name already exists
   * @throws InternalErrorException
   */
  @Override
  public List<String> createHostedZone(
      final Session session, String domainName, String callerRef, String comment, long acid)
      throws ErrorResponse {
    List<String> result = new LinkedList<String>();
    // String tableName = acid + callerRef;
    String tableName = "B" + UUID.randomUUID().toString().replace("-", "").toUpperCase();
    if (callerReferenceIsUsed(callerRef, acid)) {
      result.add("DUPLICATE_REFERENCE");
      return result; // response: signal that the name is already in use
    }
    String domainNameDot = domainName;
    if (domainName.charAt(domainName.length() - 1) != '.') {
      domainNameDot += ".";
    } else {
      domainName = domainName.substring(0, domainName.length() - 1);
    }

    if (domainExists(domainNameDot, acid)) {
      result.add("DUPLICATE_NAME");
      return result;
    }
    String query =
        "CREATE TABLE `"
            + tableName
            + "` ("
            + "		  name varchar(255) default NULL,"
            + "		  ttl int(11) default NULL,"
            + "		  rdtype varchar(255) default NULL,"
            + "		  rdata varchar(255) default NULL"
            + "		) ENGINE=MyISAM DEFAULT CHARSET=latin1;";
    try {
      Statement stmt = this.sqlConnection.createStatement();
      stmt.executeUpdate(query);
      stmt.close();
    } catch (SQLException e) {
      System.err.println("Failed to create a new schema in MySQL database.");
      e.printStackTrace();
      throw DNS53Faults.InternalError(); // just put some bogus UUID
      // unless RequestID becomes
      // important this layer
    }
    // insert this new ID-name mapping entry including caller reference and
    // comment
    String uniqueID = "Z" + UUID.randomUUID().toString().replace("-", "").toUpperCase();
    final DNS53HostedZone zone = new DNS53HostedZone();
    zone.setId(uniqueID);
    zone.setName(domainNameDot);
    zone.setCallerRefernce(callerRef);
    zone.setComment(comment);
    zone.setAccount(acid);
    zone.setTableName(tableName);
    session.save(zone);

    addRecord(
        tableName,
        domainName,
        259200,
        "SOA",
        domainName + ". hostmaster." + domainName + ". 200309181 28800 7200 86400 28800");
    DNS53ResourceRecord recordSOA = new DNS53ResourceRecord();
    recordSOA.setName(domainNameDot);
    recordSOA.setTtl(259200);
    recordSOA.setRdata(
        domainName + ". hostmaster." + domainName + ". 200309181 28800 7200 86400 28800");
    recordSOA.setRdtype("SOA");
    recordSOA.setZoneId(uniqueID);
    recordSOA.setZoneName(domainNameDot);
    session.save(recordSOA);

    addRecord(tableName, domainName, 259200, "NS", "ns0." + domainName + ".");
    DNS53ResourceRecord recordNS0 = new DNS53ResourceRecord();
    recordNS0.setName(domainNameDot);
    recordNS0.setTtl(259200);
    recordNS0.setRdata("ns0." + domainName + ".");
    recordNS0.setRdtype("NS");
    recordNS0.setZoneId(uniqueID);
    recordNS0.setZoneName(domainNameDot);
    session.save(recordNS0);

    addRecord(tableName, domainName, 259200, "NS", "ns1." + domainName + ".");
    DNS53ResourceRecord recordNS1 = new DNS53ResourceRecord();
    recordNS1.setName(domainNameDot);
    recordNS1.setTtl(259200);
    recordNS1.setRdata("ns1." + domainName + ".");
    recordNS1.setRdtype("NS");
    recordNS1.setZoneId(uniqueID);
    recordNS1.setZoneName(domainNameDot);
    session.save(recordNS1);

    final String nameserverIp =
        (String) ConfigurationUtil.getConfiguration(Arrays.asList(new String[] {"DNS_IP"}));
    String ns0name = "ns0." + domainName;
    String ns1name = "ns1." + domainName;
    String ns0nameDot = "ns0." + domainNameDot;
    String ns1nameDot = "ns1." + domainNameDot;
    addRecord(tableName, ns0name, 259200, "A", nameserverIp);
    DNS53ResourceRecord recordA0 = new DNS53ResourceRecord();
    recordA0.setName(ns0nameDot);
    recordA0.setTtl(259200);
    recordA0.setRdata(nameserverIp);
    recordA0.setRdtype("A");
    recordA0.setZoneId(uniqueID);
    recordA0.setZoneName(domainNameDot);
    session.save(recordA0);

    addRecord(tableName, ns1name, 259200, "A", nameserverIp);
    DNS53ResourceRecord recordA1 = new DNS53ResourceRecord();
    recordA1.setName(ns1nameDot);
    recordA1.setTtl(259200);
    recordA1.setRdata(nameserverIp);
    recordA1.setRdtype("A");
    recordA1.setZoneId(uniqueID);
    recordA1.setZoneName(domainNameDot);
    session.save(recordA1);

    result.add(uniqueID);
    result.add(tableName);
    result.add(ns0name);
    result.add(ns1name);

    session.save(zone);

    return result;
  }

  /**
   * Check if the target name is already being used for another hosted zone
   *
   * @param tableName name of the table to be checked
   * @return true if name exists; false otherwise
   * @throws InternalErrorException
   */
  private boolean callerReferenceIsUsed(String callerRef, long acId) throws ErrorResponse {
    String query =
        "SELECT name FROM msi.zones WHERE accountId = "
            + acId
            + " AND callerReference = \'"
            + callerRef
            + "\';";
    boolean result = false;
    try {
      Statement stmt = this.sqlConnection.createStatement();
      ResultSet rs = stmt.executeQuery(query);
      result = rs.next();
    } catch (SQLException e) {
      System.err.println("Failed while checking if the target name already exists.");
      e.printStackTrace();
      throw DNS53Faults.InternalError(); // just put some bogus UUID
      // unless RequestID becomes
      // important this layer
    }
    return result;
  }

  private boolean domainExists(String zoneName, long acId) throws ErrorResponse {
    String query =
        "SELECT name FROM msi.zones WHERE accountId = "
            + acId
            + " AND name = \'"
            + zoneName
            + "\';";
    boolean result = false;
    try {
      Statement stmt = this.sqlConnection.createStatement();
      ResultSet rs = stmt.executeQuery(query);
      result = rs.next();
    } catch (SQLException e) {
      System.err.println("Failed while checking if the target name already exists.");
      e.printStackTrace();
      throw DNS53Faults.InternalError(); // just put some bogus UUID
      // unless RequestID becomes
      // important this layer
    }
    return result;
  }

  /**
   * Returns matching table name for given zone ID
   *
   * @param zoneID ID of the target hosted zone
   * @return table name of the target hosted zone
   * @throws InternalErrorException
   */
  @Override
  public String getTableName(String zoneID, long accId) throws ErrorResponse {
    String tname = "FAILED";
    String query =
        "SELECT tableName FROM msi.zones WHERE ID = \'"
            + zoneID
            + "\' AND accountId = "
            + accId
            + ";";
    logger.debug("Retrieving the table name with the query: " + query);
    try {
      Statement stmt = this.sqlConnection.createStatement();
      ResultSet rs = stmt.executeQuery(query);
      int counter = 0;
      while (rs.next()) {
        tname = rs.getString("tableName");
        ++counter;
      }
      logger.debug("Counter = " + counter + "; the value should always be 1.");
      if (counter == 0) {
        throw DNS53Faults.NoSuchHostedZone(zoneID);
      }
      if (counter > 1) {
        throw DNS53Faults.InternalError();
      }
    } catch (SQLException e) {
      System.err.println("Failed to get domain name for the input ID.");
      e.printStackTrace();
      throw DNS53Faults.InternalError(); // just put some bogus UUID
      // unless RequestID becomes
      // important this layer
    }
    return tname;
  }

  public boolean tableExists(String tableName) {
    boolean result = false;
    String query = "SELECT * FROM `" + tableName + "`;";
    Statement stmt;
    try {
      stmt = this.sqlConnection.createStatement();
      ResultSet rs = stmt.executeQuery(query);
      if (rs.next()) {
        result = true;
      }
    } catch (SQLException e) {
      // e.printStackTrace();
      return false;
    }
    return result;
  }

  public String getCallerReference(String zoneID, long accId) throws ErrorResponse {
    String tname = "FAILED";
    String query =
        "SELECT callerReference FROM msi.zones WHERE ID = \'"
            + zoneID
            + "\' AND accountId = "
            + accId
            + ";";
    logger.debug("Retrieving the table name with the query: " + query);
    try {
      Statement stmt = this.sqlConnection.createStatement();
      ResultSet rs = stmt.executeQuery(query);
      while (rs.next()) {
        tname = rs.getString("callerReference");
      }
    } catch (SQLException e) {
      System.err.println("Failed to get domain name for the input ID.");
      e.printStackTrace();
      throw DNS53Faults.InternalError(); // just put some bogus UUID
      // unless RequestID becomes
      // important this layer
    }
    return tname;
  }

  @Override
  public String getZoneName(String zoneId) throws ErrorResponse {
    String zname = "FAILED";
    String query = "SELECT name FROM msi.zones WHERE ID = \'" + zoneId + "\';";
    try {
      Statement stmt = this.sqlConnection.createStatement();
      ResultSet rs = stmt.executeQuery(query);
      while (rs.next()) {
        zname = rs.getString("name");
      }
    } catch (SQLException e) {
      System.err.println("Failed to get domain name for the input ID.");
      e.printStackTrace();
      throw DNS53Faults.InternalError(); // just put some bogus UUID
      // unless RequestID becomes
      // important this layer
    }
    return zname;
  }

  @Override
  public String getZoneId(String zoneName) throws ErrorResponse {
    String zid = "FAILED";
    String query = "SELECT id FROM msi.zones WHERE name = \'" + zoneName + "\';";
    try {
      Statement stmt = this.sqlConnection.createStatement();
      ResultSet rs = stmt.executeQuery(query);
      while (rs.next()) {
        zid = rs.getString("id");
      }
    } catch (SQLException e) {
      System.err.println("Failed to get domain name for the input ID.");
      e.printStackTrace();
      throw DNS53Faults.InternalError(); // just put some bogus UUID
      // unless RequestID becomes
      // important this layer
    }
    return zid;
  }

  @Override
  public List<DNS53HostedZone> getAllHostedZones() {
    List<DNS53HostedZone> result = new LinkedList<DNS53HostedZone>();

    String query = "SELECT * FROM msi.zones;";
    try {
      Statement stmt = this.sqlConnection.createStatement();
      ResultSet rs = stmt.executeQuery(query);
      while (rs.next()) {
        DNS53HostedZone temp = new DNS53HostedZone();
        temp.setId(rs.getString("ID"));
        temp.setAccount(rs.getLong("accountId"));
        temp.setCallerRefernce(rs.getString("callerReference"));
        temp.setComment(rs.getString("comment"));
        temp.setName(rs.getString("name"));
        temp.setTableName(rs.getString("tableName"));
        result.add(temp);
      }
    } catch (SQLException e) {
      e.printStackTrace();
    }
    return result;
  }

  @Override
  public void addResourceRecord(
      String zoneID, String name, long ttl, String rdtype, String rdata, long accId)
      throws ErrorResponse {
    String tableName = getTableName(zoneID, accId);
    addRecord(tableName, name, ttl, rdtype, rdata);
  }

  @Override
  public void addRecord(String tableName, String name, long ttl, String rdtype, String rdata)
      throws ErrorResponse {
    name = "\'" + name + "\'";
    rdtype = "\'" + rdtype + "\'";
    rdata = "\'" + rdata + "\'";
    String query =
        "INSERT INTO `"
            + tableName
            + "` VALUES ("
            + name
            + ", "
            + ttl
            + ", "
            + rdtype
            + ", "
            + rdata
            + ");";
    try {
      Statement stmt = this.sqlConnection.createStatement();
      stmt.executeUpdate(query);
      stmt.close();
    } catch (SQLException e) {
      System.err.println("Failed to insert a resource record into database.");
      e.printStackTrace();
      throw DNS53Faults.InternalError(); // just put some bogus UUID
      // unless RequestID becomes
      // important this layer
    }
  }

  /** @throws InternalErrorException */
  @Override
  public void deleteResourceRecord(
      String zoneID, String name, long ttl, String rdtype, String rdata, long accId)
      throws ErrorResponse {
    String tableName = getTableName(zoneID, accId);
    deleteRecord(tableName, name, ttl, rdtype, rdata);
  }

  /** @throws InvalidChangeBatchException */
  @Override
  public void deleteRecord(String tableName, String name, long ttl, String rdtype, String rdata)
      throws ErrorResponse {
    name = "\'" + name + "\'";
    rdtype = "\'" + rdtype + "\'";
    rdata = "\'" + rdata + "\'";
    String query =
        "DELETE FROM `"
            + tableName
            + "` where name = "
            + name
            + " AND ttl = "
            + ttl
            + " AND rdtype = "
            + rdtype
            + " AND rdata = "
            + rdata
            + ";";
    try {
      Statement stmt = this.sqlConnection.createStatement();
      stmt.executeUpdate(query);
      stmt.close();
    } catch (SQLException e) {
      e.printStackTrace();
      throw DNS53Faults.InternalError(); // just put some bogus UUID
      // unless RequestID becomes
      // important this layer
    }
  }

  @Override
  public List<DNS53ResourceRecord> listResourceRecords(
      Session sess,
      String zoneId,
      String name,
      String value,
      String type,
      String sid,
      long ttl,
      long weight)
      throws ErrorResponse {
    List<DNS53ResourceRecord> records = new LinkedList<DNS53ResourceRecord>();

    String zoneName = getZoneName(zoneId);
    if (zoneName.equals("FAILED")) {
      throw DNS53Faults.NoSuchHostedZone(zoneId);
    }

    String query = "SELECT * FROM `msi`.`rrSet` WHERE zoneName = \'" + zoneName + "\'";
    if (type != null && !type.equals("")) {
      type = "\'" + type + "\'";
      query += " AND rdtype = " + type;
    }
    if (name != null && !name.equals("")) {
      String nameDot = name;
      if (name.charAt(name.length() - 1) != '.') {
        nameDot += ".";
      } else {
        name = name.substring(0, name.length() - 1);
      }
      name = "\'" + nameDot + "\'";
      query += " AND name = " + name;
    }
    if (value != null && !value.equals("")) {
      value = "\'" + value + "\'";
      query += " AND rdata = " + value;
    }
    if (sid != null && !sid.equals("")) {
      sid = "\'" + sid + "\'";
      query += " AND sid = " + sid;
    }
    if (ttl != -1) {
      query += " AND ttl = " + ttl;
    }
    if (weight != -1) {
      query += " AND weight = " + weight;
    }
    query += ";";
    // now query is created
    try {
      Statement stmt = this.sqlConnection.createStatement();
      ResultSet rs = stmt.executeQuery(query);
      while (rs.next()) {
        DNS53ResourceRecord temp = new DNS53ResourceRecord();
        temp.setName(rs.getString("name"));
        temp.setTtl(rs.getLong("ttl"));
        temp.setRdtype(rs.getString("rdtype"));
        temp.setRdata(rs.getString("rdata"));
        temp.setSid(rs.getString("sid"));
        temp.setWeight(rs.getLong("weight"));
        records.add(temp);
      }
    } catch (SQLException e) {
      e.printStackTrace();
      throw DNS53Faults.InternalError();
    }
    return records;
  }

  public List<DNS53ResourceRecord> listResourceRecords(
      String tableName, String type, String name, String rdata) throws ErrorResponse {
    List<DNS53ResourceRecord> records = new LinkedList<DNS53ResourceRecord>();
    boolean whereFlag = false;
    if (tableName == null) {
      // TODO throw InternalError
    }
    String query = "select * from `" + tableName + "`";
    if (type != null && !type.equals("")) {
      type = "\'" + type + "\'";
      if (whereFlag) {
        // probably never reached:
        query += " AND rdtype = " + type;
      } else {
        query += " WHERE rdtype = " + type;
        whereFlag = true;
      }
    }
    if (name != null && !name.equals("")) {
      name = "\'" + name + "\'";
      if (whereFlag) {
        query += " AND name = " + name;
      } else {
        query += " WHERE name = " + name;
        whereFlag = true;
      }
    }
    if (rdata != null && !rdata.equals("")) {
      rdata = "\'" + rdata + "\'";
      if (whereFlag) {
        query += " AND rdata = " + rdata;
      } else {
        query += " WHERE rdata = " + rdata;
        whereFlag = true;
      }
    }
    query += ";";
    // now query is created
    try {
      Statement stmt = this.sqlConnection.createStatement();
      ResultSet rs = stmt.executeQuery(query);
      while (rs.next()) {
        DNS53ResourceRecord temp = new DNS53ResourceRecord();
        temp.setName(rs.getString("name"));
        temp.setTtl(rs.getLong("ttl"));
        temp.setRdtype(rs.getString("rdtype"));
        temp.setRdata(rs.getString("rdata"));
        records.add(temp);
      }
    } catch (SQLException e) {
      e.printStackTrace();
      throw DNS53Faults.InternalError();
    }
    return records;
  }

  /**
   * Returns ID, name, caller reference, and comment for the target hosted zone
   *
   * @param zoneID ID of the target hosted zone
   * @return String[] with ID, name, caller reference, and comment of the target hosted zone
   * @throws InternalErrorException
   */
  @Override
  public String[] getHostedZone(String zoneID) throws ErrorResponse {
    String[] result = new String[4];
    result[0] = zoneID;
    try {
      String query = "SELECT * FROM msi.zones WHERE ID = \'" + zoneID + "\';";
      Statement stmt = this.sqlConnection.createStatement();
      ResultSet rs = stmt.executeQuery(query);
      if (rs.next()) {
        result[1] = rs.getString("name");
        result[2] = rs.getString("callerReference");
        result[3] = rs.getString("comment");
      }
    } catch (SQLException e) {
      System.err.println("Failed to get domain name for the input ID.");
      e.printStackTrace();
      throw DNS53Faults.InternalError(); // just put some bogus UUID
      // unless RequestID becomes
      // important this layer
    }
    return result;
  }

  /** @throws HostedZoneNotEmptyException */
  public void deleteHostedZone(String zoneId, String tableName, String callerRef)
      throws ErrorResponse {
    String zoneName = getZoneName(zoneId);

    // check to see if the table is empty or not
    boolean empty = true;
    String chkquery =
        "SELECT * FROM `" + tableName + "` WHERE NOT rdtype = \'SOA\' AND NOT rdtype = \'NS\';";
    try {
      Statement stmt = this.sqlConnection.createStatement();
      ResultSet rs = stmt.executeQuery(chkquery);
      if (rs.next()) {
        // TODO handling special cases are poor and hardcoded since
        // there's not enough time; fix the snippet below later
        String name = rs.getString("name");
        String rdtype = rs.getString("rdtype");
        if (!rdtype.equals("A")) {
          empty = false;
        } else if (!(name.equals("ns0." + zoneName) || name.equals("ns1." + zoneName))) {
          empty = false;
        }
      }
    } catch (SQLException e) {
      System.err.println("Failed to delete a hosted zone.");
      e.printStackTrace();
      throw DNS53Faults.InternalError(); // just put some bogus UUID
      // unless RequestID becomes
      // important this layer
    }
    if (!empty) {
      // TODO right now "empty" variable is not being calculated correctly
      // since A records are added for CreateHostedZone as well
      // TODO modify the snippet to verify whether zone is empty or not;
      // implement HostedZoneNotEmptyException in DNS53Faults and throw it
      // throw new
      // HostedZoneNotEmptyException(UUID.randomUUID().toString()); //just
      // put some bogus UUID unless RequestID becomes important this layer
    }
    // delete
    String query = "DROP TABLE `" + tableName + "`;";
    try {
      Statement stmt = this.sqlConnection.createStatement();
      stmt.executeUpdate(query);
      stmt.close();
    } catch (SQLException e) {
      System.err.println("Failed to delete a hosted zone.");
      e.printStackTrace();
      throw DNS53Faults.InternalError(); // just put some bogus UUID
      // unless RequestID becomes
      // important this layer
    }
    query = "DELETE FROM msi.zones WHERE callerReference = \'" + callerRef + "\';";
    try {
      Statement stmt = this.sqlConnection.createStatement();
      stmt.executeUpdate(query);
      stmt.close();
    } catch (SQLException e) {
      System.err.println("Failed to delete the mapping information of a deleted hosted zone.");
      e.printStackTrace();
      throw DNS53Faults.InternalError(); // just put some bogus UUID
      // unless RequestID becomes
      // important this layer
    }
    query = "DELETE FROM msi.rrSet WHERE zoneId = \'" + zoneId + "\';";
    try {
      Statement stmt = this.sqlConnection.createStatement();
      stmt.executeUpdate(query);
      stmt.close();
    } catch (SQLException e) {
      System.err.println("Failed to delete the mapping information of a deleted hosted zone.");
      e.printStackTrace();
      throw DNS53Faults.InternalError(); // just put some bogus UUID
      // unless RequestID becomes
      // important this layer
    }
  }

  /**
   * Returns hashmap of <KEY: zoneID, VALUE: String[] of ID, name, caller reference, and comment>
   *
   * @param marker_tableName table name of the marker
   * @param maxItems number of items returned when the actual number of list exceeds maxItems
   * @return Hashmap of <KEY: zoneID, VALUE: String[] of ID, name, caller reference, and comment>
   * @throws InternalErrorException
   */
  @Override
  public ListHostedZonesResult listHostedZones(String marker, int maxItems, long accId)
      throws ErrorResponse {
    ListHostedZonesResult result = new ListHostedZonesResult();
    Collection<HostedZone> hostedZones = new LinkedList<HostedZone>();
    int lim = maxItems;
    try {
      ResultSet rs = null;
      String query = null;
      Statement stmt = this.sqlConnection.createStatement();
      if (marker == null) {
        logger.debug("No marker is given.");
        query = "SELECT * FROM msi.zones WHERE accountId = " + accId + ";";

      } else {
        logger.debug("Marker is assigned.");
        query =
            "SELECT * FROM msi.zones WHERE accountId = " + accId + " AND ID >= \'" + marker + "\';";
      }

      rs = stmt.executeQuery(query);
      while (lim != 0 && rs.next()) {
        HostedZone hz =
            new HostedZone(
                rs.getString("ID"), rs.getString("name"), rs.getString("callerReference"));
        HostedZoneConfig config = new HostedZoneConfig();
        config.setComment(rs.getString("comment"));
        hz.setConfig(config);
        --lim;
        hostedZones.add(hz);
      }

      if (marker != null && hostedZones.size() == 0) {
        // TODO throw an exception for marker not existing (test against
        // AWS to see which exception is being returned)
      }

      boolean truncated = rs.next();

      logger.debug("Relative Limit = " + lim + "; MaxItems = " + maxItems);
      logger.debug("Truncated = " + truncated);

      if (lim == 0 && truncated) {
        truncated = true;
      }

      result.setHostedZones(hostedZones);
      result.setMaxItems(String.valueOf(maxItems));
      result.setIsTruncated(truncated);
      if (truncated) {
        result.setNextMarker(rs.getString("ID"));
      }

    } catch (SQLException e) {
      System.err.println("Failed to get zone informations for listHostedZone request.");
      e.printStackTrace();
      throw DNS53Faults.InternalError();
    }
    logger.debug("Returning " + hostedZones.size() + " hosted zones information.");
    return result;
  }

  @Override
  public String[] getChange(String changeID) throws ErrorResponse {
    String query = "SELECT * FROM msi.changes WHERE ID = \'" + changeID + "\';";
    String[] result = new String[4];
    try {
      Statement stmt = this.sqlConnection.createStatement();
      ResultSet rs = stmt.executeQuery(query);
      if (rs.next()) { // should be only one entry or none at all
        result[0] = rs.getString("ID");
        result[1] = rs.getString("status");
        result[2] = rs.getString("submit_time");
        result[3] = rs.getString("zone_table");
      }
    } catch (SQLException e) {
      System.err.println("Failed to get domain name for the input ID.");
      e.printStackTrace();
      throw DNS53Faults.InternalError();
    }
    // logger.debug(result[0] + "; " + result[1] + "; " + result[2] + "; " +
    // result[3]);
    return result;
  }

  @Override
  public boolean pendingChangesExist() throws SQLException {
    boolean result = false;
    String query = "SELECT * FROM msi.changes WHERE status = 'PENDING';";
    Statement stmt = this.sqlConnection.createStatement();
    ResultSet rs = stmt.executeQuery(query);
    if (rs.next()) {
      result = true;
    }
    return result;
  }

  @Override
  public void updateChanges() throws SQLException {
    String query = "UPDATE msi.changes SET status = 'INSYNC';";
    Statement stmt = this.sqlConnection.createStatement();
    stmt.executeUpdate(query);
  }

  @Override
  public void addChangeRecord(
      String changeID, String status, String submitTime, String tableName, String request)
      throws ErrorResponse {
    changeID = "\'" + changeID + "\'";
    status = "\'" + status + "\'";
    submitTime = "\'" + submitTime + "\'";
    tableName = "\'" + tableName + "\'";
    request = "\'" + request + "\'";
    String query =
        "INSERT INTO msi.changes VALUES ("
            + changeID
            + ", "
            + status
            + ", "
            + submitTime
            + ", "
            + tableName
            + ", "
            + request
            + ");";
    try {
      Statement stmt = this.sqlConnection.createStatement();
      stmt.executeUpdate(query);
      stmt.close();
    } catch (SQLException e) {
      System.err.println("Failed to insert a resource record into the database.");
      e.printStackTrace();
      throw DNS53Faults.InternalError();
    }
  }

  /*
   * @Override public boolean recordExists(String tableName, String name,
   * String rdtype) throws ErrorResponse { boolean result = false;
   * if("NS".equals(rdtype)){ //allow duplicate NS records with different
   * values return result; } String query = "SELECT * from `" + tableName +
   * "`"; boolean where = false; if(name != null){ where = true; name = "\'" +
   * name +"\'"; query += " WHERE name = " + name; } if(rdtype != null){
   * rdtype = "\'" + rdtype +"\'"; if(where){ query += " AND rdtype = " +
   * rdtype; }else{ query += " WHERE rdtype = " + rdtype; } } query += ";";
   * try { Statement stmt = this.sqlConnection.createStatement(); ResultSet rs
   * = stmt.executeQuery(query); if(rs.next()) { result = true; } }
   * catch(SQLException e){
   * System.err.println("Failed while checking if a record exists.");
   * e.printStackTrace(); throw DNS53Faults.InternalError(); //put some bogus
   * UUID for now until Request ID actually matters } return result; }
   */

  @Override
  public boolean recordExists(
      Session session, String zoneId, String name, String rdtype, String value)
      throws ErrorResponse {
    List<DNS53ResourceRecord> list =
        ResourceRecordEntity.selectResourceRecords(
            session, zoneId, name, value, rdtype, null, null, null);
    if (list != null && list.size() > 0) {
      return true;
    }
    return false;
  }
}
public class AssociateAddressWorker
    extends AbstractWorker<AssociateAddressRequest, AssociateAddressResponse> {
  private final Logger logger = Appctx.getLogger(AssociateAddressWorker.class.getName());

  private static final int RETRY_MAX = 5;
  private static final int RETRY_SECS = 1;

  /**
   * We need a local copy of this doWork to provide the transactional annotation. Transaction
   * management is handled by the annotation, which can only be on a concrete class.
   *
   * @param req
   * @return
   * @throws Exception
   */
  @Transactional
  public AssociateAddressResponse doWork(AssociateAddressRequest req) throws Exception {
    logger.debug("Performing work for AssociateAddress.");
    return super.doWork(req, getSession());
  }

  /*
   * (non-Javadoc)
   *
   * @see
   * com.msi.tough.workflow.core.AbstractWorker#doWork0(com.google.protobuf
   * .Message, com.msi.tough.query.ServiceRequestContext)
   */
  @Override
  protected AssociateAddressResponse doWork0(
      AssociateAddressRequest req, ServiceRequestContext context) throws Exception {

    final AssociateAddressResponse.Builder result = AssociateAddressResponse.newBuilder();
    final AccountBean account = context.getAccountBean();

    final CloudProvider cloudProvider =
        DaseinHelper.getProvider(
            account.getDefZone(), account.getTenant(),
            account.getAccessKey(), account.getSecretKey());
    final ComputeServices compute = cloudProvider.getComputeServices();
    final VirtualMachineSupport vmSupport = compute.getVirtualMachineSupport();

    final String publicIp = req.getPublicIp();
    final String instanceId = req.getInstanceId();

    VirtualMachine vm = vmSupport.getVirtualMachine(instanceId);
    // Check if instance id refers to existing instance
    if (vm == null) {
      throw ComputeFaults.instanceDoesNotExist(instanceId);
    }

    final NetworkServices network = cloudProvider.getNetworkServices();
    final IpAddressSupport ipsupport = network.getIpAddressSupport();

    // check if specified address exists in the pool
    IpAddress address = null;
    for (final IpAddress i : ipsupport.listIpPool(IPVersion.IPV4, false)) {
      if (i.getRawAddress().getIpAddress().equals(publicIp)) {
        address = i;
        break;
      }
    }
    if (address == null || "".equals(address.getRawAddress().getIpAddress())) {
      throw ComputeFaults.IpAddressDoesNotExist(publicIp);
    }

    logger.debug("Address info - BEGIN: \n" + address.toString() + "\n - END");
    logger.debug("Address ID: " + address.getProviderIpAddressId());

    // Currently Dasein gets for the actual string "null" rather than the
    // null object for address.getServerId() if there is no assigned
    // instance
    // According to AWS docs, if address is associated with another
    // instance, disassociate it and reassociate to the instance specified
    // in the request.
    if (address.getServerId() != null && !address.getServerId().equals("null")) {
      logger.info("The address " + publicIp + " is currently associated with an instance.");
      logger.info("Diassociating address...");
      ipsupport.releaseFromServer(address.getProviderIpAddressId());
    }

    logger.info(
        "Associating address "
            + address.getRawAddress().getIpAddress()
            + " to instance "
            + instanceId);

    if ("OpenStack".equals(cloudProvider.getProviderName())) {
      String privateIp = null;
      int retryCount = 0;
      while (privateIp == null && retryCount++ < RETRY_MAX) {
        // Must avoid associating too early; instance should have a fixed IP.
        if (vm.getPrivateAddresses() != null && vm.getPrivateAddresses().length > 0) {
          privateIp = vm.getPrivateAddresses()[0].getIpAddress();
        }
        if (privateIp == null || privateIp.length() == 0 || privateIp.equals("0.0.0.0")) {
          logger.debug("Instance does not have private IP, waiting for network ready.");
          privateIp = null;
          Thread.sleep(RETRY_SECS * 1000);
          vm = vmSupport.getVirtualMachine(instanceId);
        }
      }
      if (retryCount >= RETRY_MAX) {
        logger.error("Error assigning IP Address: instance doesn't " + "have a private IP.");
        throw QueryFaults.invalidState();
      }
    }

    /*
     * TODO: Add VPC Support.
     *   THIS IMPLEMENTATION SUPPORTS EC2-CLASSIC ONLY!
     */
    try {
      ipsupport.assign(address.getProviderIpAddressId(), instanceId);
    } catch (final CloudException e) {
      final ExceptionItems eitms = NovaException.parseException(e.getHttpCode(), e.getMessage());
      throw new Exception("Error assigning IP Address: error type = " + eitms.type.toString());
    }

    /* If execution arrives here, no exceptions occurred */
    result.setReturn(true);
    return result.buildPartial();
  }
}