content: //
  // Licensed to the Apache Software Foundation (ASF) under one
  // or more contributor license agreements.  See the NOTICE file
  // distributed with this work for additional information
  // regarding copyright ownership.  The ASF licenses this file
  // to you 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.cloud.network.nicira;
  
  import java.lang.reflect.Type;
  import java.util.Collections;
  import java.util.HashMap;
  import java.util.List;
  import java.util.Map;
  import java.util.UUID;
  
  import org.apache.http.client.protocol.HttpClientContext;
  import org.apache.http.impl.client.CloseableHttpClient;
  
  import com.cloud.utils.rest.CloudstackRESTException;
  import com.cloud.utils.rest.RESTServiceConnector;
  import com.google.common.base.Optional;
  import com.google.gson.JsonDeserializer;
  import com.google.gson.reflect.TypeToken;
  
  @SuppressWarnings("rawtypes")
  public class NiciraNvpApi {
  
      private static final Optional<String> ABSENT = Optional.absent();
  
      private static final String SWITCH_URI_PREFIX = NiciraConstants.SWITCH_URI_PREFIX;
      private static final String ROUTER_URI_PREFIX = NiciraConstants.ROUTER_URI_PREFIX;
      private static final String GATEWAY_SERVICE_PREFIX = NiciraConstants.GATEWAY_SERVICE_PREFIX;
  
      private static final String ATTACHMENT_PATH_SEGMENT = NiciraConstants.ATTACHMENT_PATH_SEGMENT;
      private static final String NAT_PATH_SEGMENT = NiciraConstants.NAT_PATH_SEGMENT;
      private static final String LPORT_PATH_SEGMENT = NiciraConstants.LPORT_PATH_SEGMENT;
  
      private static final String ATTACHMENT_GWSVC_UUID_QUERY_PARAMETER = NiciraConstants.ATTACHMENT_GWSVC_UUID_QUERY_PARAMETER;
      private static final String WILDCARD_QUERY_PARAMETER = NiciraConstants.WILDCARD_QUERY_PARAMETER;
      private static final String UUID_QUERY_PARAMETER = NiciraConstants.UUID_QUERY_PARAMETER;
      private static final String FIELDS_QUERY_PARAMETER = NiciraConstants.FIELDS_QUERY_PARAMETER;
      private static final String TYPES_QUERY_PARAMETER = NiciraConstants.TYPES_QUERY_PARAMETER;
  
      private static final int DEFAULT_MAX_RETRIES = 5;
  
      private final RESTServiceConnector restConnector;
  
      protected final static Map<Class, String> prefixMap;
  
      protected final static Map<Class, Type> listTypeMap;
  
      protected final static Map<String, String> defaultListParams;
  
      static {
          prefixMap = new HashMap<Class, String>();
          prefixMap.put(SecurityProfile.class, NiciraConstants.SEC_PROFILE_URI_PREFIX);
          prefixMap.put(Acl.class, NiciraConstants.ACL_URI_PREFIX);
          prefixMap.put(LogicalSwitch.class, SWITCH_URI_PREFIX);
          prefixMap.put(LogicalRouter.class, ROUTER_URI_PREFIX);
  
          listTypeMap = new HashMap<Class, Type>();
          listTypeMap.put(SecurityProfile.class, new TypeToken<NiciraNvpList<SecurityProfile>>() {
          }.getType());
          listTypeMap.put(Acl.class, new TypeToken<NiciraNvpList<Acl>>() {
          }.getType());
          listTypeMap.put(LogicalSwitch.class, new TypeToken<NiciraNvpList<LogicalSwitch>>() {
          }.getType());
          listTypeMap.put(LogicalRouter.class, new TypeToken<NiciraNvpList<LogicalRouter>>() {
          }.getType());
  
          defaultListParams = new HashMap<String, String>();
          defaultListParams.put(FIELDS_QUERY_PARAMETER, WILDCARD_QUERY_PARAMETER);
      }
  
      private NiciraNvpApi(final Builder builder) {
          final Map<Class<?>, JsonDeserializer<?>> classToDeserializerMap = new HashMap<>();
          classToDeserializerMap.put(NatRule.class, new NatRuleAdapter());
          classToDeserializerMap.put(RoutingConfig.class, new RoutingConfigAdapter());
  
          final NiciraRestClient niciraRestClient = NiciraRestClient.create()
              .client(builder.httpClient)
              .clientContext(builder.httpClientContext)
              .hostname(builder.host)
              .username(builder.username)
              .password(builder.password)
              .loginUrl(NiciraConstants.LOGIN_URL)
              .executionLimit(DEFAULT_MAX_RETRIES)
              .build();
          restConnector = RESTServiceConnector.create()
              .classToDeserializerMap(classToDeserializerMap)
              .client(niciraRestClient)
              .build();
      }
  
      public static Builder create() {
          return new Builder();
      }
  
      /**
       * POST
       *
       * @param entity
       * @return
       * @throws NiciraNvpApiException
       */
      private <T> T create(final T entity) throws NiciraNvpApiException {
          final String uri = prefixMap.get(entity.getClass());
          return createWithUri(entity, uri);
      }
  
      /**
       * POST
       *
       * @param entity
       * @return
       * @throws NiciraNvpApiException
       */
      private <T> T createWithUri(final T entity, final String uri) throws NiciraNvpApiException {
          T createdEntity;
          try {
              createdEntity = restConnector.executeCreateObject(entity, uri, Collections.<String, String> emptyMap());
          } catch (final CloudstackRESTException e) {
              throw new NiciraNvpApiException(e, e.getErrorCode());
          }
  
          return createdEntity;
      }
  
      /**
       * GET list of items
       *
       * @param uuid
       *
       * @return
       * @throws NiciraNvpApiException
       */
      private <T> List<T> find(final Optional<String> uuid, final Class<T> clazz) throws NiciraNvpApiException {
          final String uri = prefixMap.get(clazz);
          Map<String, String> params = defaultListParams;
          if (uuid.isPresent()) {
              params = new HashMap<String, String>(defaultListParams);
              params.put(UUID_QUERY_PARAMETER, uuid.get());
          }
  
          NiciraNvpList<T> entities;
          try {
              entities = restConnector.executeRetrieveObject(listTypeMap.get(clazz), uri, params);
          } catch (final CloudstackRESTException e) {
              throw new NiciraNvpApiException(e, e.getErrorCode());
          }
  
          if (entities == null) {
              throw new NiciraNvpApiException("Unexpected response from API");
          }
  
          return entities.getResults();
      }
  
      /**
       * PUT item given a UUID as key and an item object with the new data
       *
       * @param item
       * @param uuid
       * @throws NiciraNvpApiException
       */
      private <T> void update(final T item, final String uuid) throws NiciraNvpApiException {
          final String uri = prefixMap.get(item.getClass()) + "/" + uuid;
          updateWithUri(item, uri);
      }
  
      /**
       * PUT item given a UUID as key and an item object with the new data
       *
       * @param item
       * @param uuid
       * @throws NiciraNvpApiException
       */
      private <T> void updateWithUri(final T item, final String uri) throws NiciraNvpApiException {
          try {
              restConnector.executeUpdateObject(item, uri, Collections.<String, String> emptyMap());
          } catch (final CloudstackRESTException e) {
              throw new NiciraNvpApiException(e, e.getErrorCode());
          }
      }
  
      /**
       * DELETE Security Profile given a UUID as key
       *
       * @param securityProfileUuid
       * @throws NiciraNvpApiException
       */
      private <T> void delete(final String uuid, final Class<T> clazz) throws NiciraNvpApiException {
          final String uri = prefixMap.get(clazz) + "/" + uuid;
          deleteWithUri(uri);
      }
  
      /**
       * DELETE Security Profile given a UUID as key
       *
       * @param securityProfileUuid
       * @throws NiciraNvpApiException
       */
      private void deleteWithUri(final String uri) throws NiciraNvpApiException {
          try {
              restConnector.executeDeleteObject(uri);
          } catch (final CloudstackRESTException e) {
              throw new NiciraNvpApiException(e, e.getErrorCode());
          }
      }
  
      /**
       * POST {@link SecurityProfile}
       *
       * @param securityProfile
       * @return
       * @throws NiciraNvpApiException
       */
      public SecurityProfile createSecurityProfile(final SecurityProfile securityProfile) throws NiciraNvpApiException {
          return create(securityProfile);
      }
  
      /**
       * GET list of {@link SecurityProfile}
       *
       * @return
       * @throws NiciraNvpApiException
       */
      public List<SecurityProfile> findSecurityProfile() throws NiciraNvpApiException {
          return find(ABSENT, SecurityProfile.class);
      }
  
      /**
       * GET list of {@link SecurityProfile} filtered by UUID
       *
       * We could have invoked the service: SEC_PROFILE_URI_PREFIX + "/" + securityProfileUuid but it is not working currently
       *
       * @param uuid
       * @return
       * @throws NiciraNvpApiException
       */
      public List<SecurityProfile> findSecurityProfile(final String uuid) throws NiciraNvpApiException {
          return find(Optional.fromNullable(uuid), SecurityProfile.class);
      }
  
      /**
       * PUT {@link SecurityProfile} given a UUID as key and a {@link SecurityProfile} with the new data
       *
       * @param securityProfile
       * @param securityProfileUuid
       * @throws NiciraNvpApiException
       */
      public void updateSecurityProfile(final SecurityProfile securityProfile, final String securityProfileUuid) throws NiciraNvpApiException {
          update(securityProfile, securityProfileUuid);
      }
  
      /**
       * DELETE Security Profile given a UUID as key
       *
       * @param securityProfileUuid
       * @throws NiciraNvpApiException
       */
      public void deleteSecurityProfile(final String securityProfileUuid) throws NiciraNvpApiException {
          delete(securityProfileUuid, SecurityProfile.class);
      }
  
      /**
       * POST {@link Acl}
       *
       * @param acl
       * @return
       * @throws NiciraNvpApiException
       */
      public Acl createAcl(final Acl acl) throws NiciraNvpApiException {
          return create(acl);
      }
  
      /**
       * GET list of {@link Acl}
       *
       * @return
       * @throws NiciraNvpApiException
       */
      public List<Acl> findAcl() throws NiciraNvpApiException {
          return findAcl(null);
      }
  
      /**
       * GET list of {@link Acl} filtered by UUID
       *
       * @param uuid
       * @return
       * @throws NiciraNvpApiException
       */
      public List<Acl> findAcl(final String uuid) throws NiciraNvpApiException {
          return find(Optional.fromNullable(uuid), Acl.class);
      }
  
      /**
       * PUT {@link Acl} given a UUID as key and a {@link Acl} with the new data
       *
       * @param acl
       * @param aclUuid
       * @throws NiciraNvpApiException
       */
      public void updateAcl(final Acl acl, final String aclUuid) throws NiciraNvpApiException {
          update(acl, aclUuid);
      }
  
      /**
       * DELETE Acl given a UUID as key
       *
       * @param acl
       * @throws NiciraNvpApiException
       */
      public void deleteAcl(final String aclUuid) throws NiciraNvpApiException {
          delete(aclUuid, Acl.class);
      }
  
      public LogicalSwitch createLogicalSwitch(final LogicalSwitch logicalSwitch) throws NiciraNvpApiException {
          return create(logicalSwitch);
      }
  
      /**
       * GET list of {@link LogicalSwitch}
       *
       * @return
       * @throws NiciraNvpApiException
       */
      public List<LogicalSwitch> findLogicalSwitch() throws NiciraNvpApiException {
          return findLogicalSwitch(null);
      }
  
      /**
       * GET list of {@link LogicalSwitch} filtered by UUID
       *
       * @param uuid
       * @return
       * @throws NiciraNvpApiException
       */
      public List<LogicalSwitch> findLogicalSwitch(final String uuid) throws NiciraNvpApiException {
          return find(Optional.fromNullable(uuid), LogicalSwitch.class);
      }
  
      /**
       * PUT {@link LogicalSwitch} given a UUID as key and a {@link LogicalSwitch} with the new data
       *
       * @param logicalSwitch
       * @param logicalSwitchUuid
       * @throws NiciraNvpApiException
       */
      public void updateLogicalSwitch(final LogicalSwitch logicalSwitch, final String logicalSwitchUuid) throws NiciraNvpApiException {
          update(logicalSwitch, logicalSwitchUuid);
      }
  
      public void deleteLogicalSwitch(final String uuid) throws NiciraNvpApiException {
          delete(uuid, LogicalSwitch.class);
      }
  
      public LogicalSwitchPort createLogicalSwitchPort(final String logicalSwitchUuid, final LogicalSwitchPort logicalSwitchPort) throws NiciraNvpApiException {
          return createWithUri(logicalSwitchPort, buildLogicalSwitchElementUri(logicalSwitchUuid, LPORT_PATH_SEGMENT));
      }
  
      public void updateLogicalSwitchPort(final String logicalSwitchUuid, final LogicalSwitchPort logicalSwitchPort) throws NiciraNvpApiException {
          updateWithUri(logicalSwitchPort, buildLogicalSwitchElementUri(logicalSwitchUuid, LPORT_PATH_SEGMENT, logicalSwitchPort.getUuid().toString()));
      }
  
      public void updateLogicalSwitchPortAttachment(final String logicalSwitchUuid, final String logicalSwitchPortUuid, final Attachment attachment) throws NiciraNvpApiException {
          updateWithUri(attachment, buildLogicalSwitchElementUri(logicalSwitchUuid, LPORT_PATH_SEGMENT, logicalSwitchPortUuid) + ATTACHMENT_PATH_SEGMENT);
      }
  
      public void deleteLogicalSwitchPort(final String logicalSwitchUuid, final String logicalSwitchPortUuid) throws NiciraNvpApiException {
          deleteWithUri(buildLogicalSwitchElementUri(logicalSwitchUuid, LPORT_PATH_SEGMENT, logicalSwitchPortUuid));
      }
  
      public String findLogicalSwitchPortUuidByVifAttachmentUuid(final String logicalSwitchUuid, final String vifAttachmentUuid) throws NiciraNvpApiException {
          final String uri = buildLogicalSwitchElementUri(logicalSwitchUuid, LPORT_PATH_SEGMENT);
          final Map<String, String> params = buildBasicParametersMap(UUID_QUERY_PARAMETER);
          params.put(NiciraConstants.ATTACHMENT_VIF_UUID_QUERY_PARAMETER_NAME, vifAttachmentUuid);
  
          NiciraNvpList<LogicalSwitchPort> niciraList;
          try {
              final Type niciraListType = new TypeToken<NiciraNvpList<LogicalSwitchPort>>() {
              }.getType();
              niciraList = restConnector.executeRetrieveObject(niciraListType, uri, params);
          } catch (final CloudstackRESTException e) {
              throw new NiciraNvpApiException(e, e.getErrorCode());
          }
  
          final List<LogicalSwitchPort> lspl = niciraList.getResults();
  
          final int listSize = lspl.size();
          if (listSize != 1) {
              throw new NiciraNvpApiException("Expected 1 LogicalSwitchPort, but got " + listSize);
          }
  
          final LogicalSwitchPort lsp = lspl.get(0);
          return lsp.getUuid();
      }
  
      public ControlClusterStatus getControlClusterStatus() throws NiciraNvpApiException {
          final String uri = NiciraConstants.CONTROL_CLUSTER_STATUS_URL;
          try {
              return restConnector.executeRetrieveObject(ControlClusterStatus.class, uri, new HashMap<String, String>());
          } catch (final CloudstackRESTException e) {
              throw new NiciraNvpApiException(e, e.getErrorCode());
          }
      }
  
      public List<LogicalSwitchPort> findLogicalSwitchPortsByUuid(final String logicalSwitchUuid, final String logicalSwitchPortUuid) throws NiciraNvpApiException {
          final String uri = buildLogicalSwitchElementUri(logicalSwitchUuid, LPORT_PATH_SEGMENT);
          final Map<String, String> params = buildBasicParametersMap(UUID_QUERY_PARAMETER);
          params.put(UUID_QUERY_PARAMETER, logicalSwitchPortUuid);
  
          try {
              final Type niciraListType = new TypeToken<NiciraNvpList<LogicalSwitchPort>>() {
              }.getType();
              return restConnector.<NiciraNvpList<LogicalSwitchPort>> executeRetrieveObject(niciraListType, uri, params).getResults();
          } catch (final CloudstackRESTException e) {
              throw new NiciraNvpApiException(e, e.getErrorCode());
          }
      }
  
      public List<LogicalRouterPort> findLogicalRouterPortsByUuid(final String logicalRouterUuid, final String logicalRouterPortUuid) throws NiciraNvpApiException {
          final String uri = buildLogicalRouterElementUri(logicalRouterUuid, LPORT_PATH_SEGMENT);
          final Map<String, String> params = buildBasicParametersMap(UUID_QUERY_PARAMETER);
          params.put(UUID_QUERY_PARAMETER, logicalRouterPortUuid);
  
          try {
              final Type niciraListType = new TypeToken<NiciraNvpList<LogicalRouterPort>>() {
              }.getType();
              return restConnector.<NiciraNvpList<LogicalRouterPort>> executeRetrieveObject(niciraListType, uri, params).getResults();
          } catch (final CloudstackRESTException e) {
              throw new NiciraNvpApiException(e, e.getErrorCode());
          }
      }
  
      public LogicalRouter createLogicalRouter(final LogicalRouter logicalRouter) throws NiciraNvpApiException {
          return create(logicalRouter);
      }
  
      /**
       * GET list of {@link LogicalRouter}
       *
       * @return
       * @throws NiciraNvpApiException
       */
      public List<LogicalRouter> findLogicalRouter() throws NiciraNvpApiException {
          return findLogicalRouter(null);
      }
  
      /**
       * GET list of {@link LogicalRouter} filtered by UUID
       *
       * @param uuid
       * @return
       * @throws NiciraNvpApiException
       */
      public List<LogicalRouter> findLogicalRouter(final String uuid) throws NiciraNvpApiException {
          return find(Optional.fromNullable(uuid), LogicalRouter.class);
      }
  
      public LogicalRouter findOneLogicalRouterByUuid(final String logicalRouterUuid) throws NiciraNvpApiException {
          return findLogicalRouter(logicalRouterUuid).get(0);
      }
  
      public void updateLogicalRouter(final LogicalRouter logicalRouter, final String logicalRouterUuid) throws NiciraNvpApiException {
          update(logicalRouter, logicalRouterUuid);
      }
  
      public void deleteLogicalRouter(final String logicalRouterUuid) throws NiciraNvpApiException {
          deleteWithUri(buildLogicalRouterUri(logicalRouterUuid));
      }
  
      public LogicalRouterPort createLogicalRouterPort(final String logicalRouterUuid, final LogicalRouterPort logicalRouterPort) throws NiciraNvpApiException {
          return createWithUri(logicalRouterPort, buildLogicalRouterElementUri(logicalRouterUuid, LPORT_PATH_SEGMENT));
      }
  
      public void deleteLogicalRouterPort(final String logicalRouterUuid, final String logicalRouterPortUuid) throws NiciraNvpApiException {
          deleteWithUri(buildLogicalRouterElementUri(logicalRouterUuid, LPORT_PATH_SEGMENT, logicalRouterPortUuid));
      }
  
      public void updateLogicalRouterPort(final String logicalRouterUuid, final LogicalRouterPort logicalRouterPort) throws NiciraNvpApiException {
          updateWithUri(logicalRouterPort, buildLogicalRouterElementUri(logicalRouterUuid, LPORT_PATH_SEGMENT, logicalRouterPort.getUuid().toString()));
      }
  
      public void updateLogicalRouterPortAttachment(final String logicalRouterUuid, final String logicalRouterPortUuid, final Attachment attachment) throws NiciraNvpApiException {
          updateWithUri(attachment, buildLogicalRouterElementUri(logicalRouterUuid, LPORT_PATH_SEGMENT, logicalRouterPortUuid) + ATTACHMENT_PATH_SEGMENT);
      }
  
      public NatRule createLogicalRouterNatRule(final String logicalRouterUuid, final NatRule natRule) throws NiciraNvpApiException {
          return createWithUri(natRule, buildLogicalRouterElementUri(logicalRouterUuid, NAT_PATH_SEGMENT));
      }
  
      public void updateLogicalRouterNatRule(final String logicalRouterUuid, final NatRule natRule) throws NiciraNvpApiException {
          updateWithUri(natRule, buildLogicalRouterElementUri(logicalRouterUuid, NAT_PATH_SEGMENT, natRule.getUuid().toString()));
      }
  
      public void deleteLogicalRouterNatRule(final String logicalRouterUuid, final UUID natRuleUuid) throws NiciraNvpApiException {
          deleteWithUri(buildLogicalRouterElementUri(logicalRouterUuid, NAT_PATH_SEGMENT, natRuleUuid.toString()));
      }
  
      public List<LogicalRouterPort> findLogicalRouterPortByGatewayServiceAndVlanId(final String logicalRouterUuid, final String gatewayServiceUuid, final long vlanId)
                      throws NiciraNvpApiException {
          final String uri = buildLogicalRouterElementUri(logicalRouterUuid, LPORT_PATH_SEGMENT);
          final Map<String, String> params = buildBasicParametersMap(WILDCARD_QUERY_PARAMETER);
          params.put(ATTACHMENT_GWSVC_UUID_QUERY_PARAMETER, gatewayServiceUuid);
          params.put(NiciraConstants.ATTACHMENT_VLAN_PARAMETER, Long.toString(vlanId));
  
          try {
              final Type niciraListType = new TypeToken<NiciraNvpList<LogicalRouterPort>>() {
              }.getType();
              return restConnector.<NiciraNvpList<LogicalRouterPort>> executeRetrieveObject(niciraListType, uri, params).getResults();
          } catch (final CloudstackRESTException e) {
              throw new NiciraNvpApiException(e, e.getErrorCode());
          }
      }
  
      public List<LogicalRouterPort> findLogicalRouterPortByAttachmentLSwitchUuid(final String logicalRouterUuid, final String attachmentLSwitchUuid) throws NiciraNvpApiException{
          final String uri = buildLogicalRouterElementUri(logicalRouterUuid, LPORT_PATH_SEGMENT);
          final Map<String, String> params = buildBasicParametersMap(WILDCARD_QUERY_PARAMETER);
          params.put(NiciraConstants.ATTACHMENT_LSWITCH_UUID, attachmentLSwitchUuid);
  
          try{
              final Type niciraListType = new TypeToken<NiciraNvpList<LogicalRouterPort>>() {
              }.getType();
              return restConnector.<NiciraNvpList<LogicalRouterPort>> executeRetrieveObject(niciraListType, uri, params).getResults();
          } catch (final CloudstackRESTException e) {
              throw new NiciraNvpApiException(e, e.getErrorCode());
          }
      }
  
      public List<NatRule> findNatRulesByLogicalRouterUuid(final String logicalRouterUuid) throws NiciraNvpApiException {
          final String uri = buildLogicalRouterElementUri(logicalRouterUuid, NAT_PATH_SEGMENT);
          final Map<String, String> params = buildBasicParametersMap(WILDCARD_QUERY_PARAMETER);
  
          try {
              final Type niciraListType = new TypeToken<NiciraNvpList<NatRule>>() {
              }.getType();
              return restConnector.<NiciraNvpList<NatRule>> executeRetrieveObject(niciraListType, uri, params).getResults();
          } catch (final CloudstackRESTException e) {
              throw new NiciraNvpApiException(e, e.getErrorCode());
          }
      }
  
      public List<LogicalRouterPort> findLogicalRouterPortByGatewayServiceUuid(final String logicalRouterUuid, final String l3GatewayServiceUuid)
                      throws NiciraNvpApiException {
          final String uri = buildLogicalRouterElementUri(logicalRouterUuid, LPORT_PATH_SEGMENT);
          final Map<String, String> params = buildBasicParametersMap(WILDCARD_QUERY_PARAMETER);
          params.put(ATTACHMENT_GWSVC_UUID_QUERY_PARAMETER, l3GatewayServiceUuid);
  
          try {
              final Type niciraListType = new TypeToken<NiciraNvpList<LogicalRouterPort>>() {
              }.getType();
              return restConnector.<NiciraNvpList<LogicalRouterPort>> executeRetrieveObject(niciraListType, uri, params).getResults();
          } catch (final CloudstackRESTException e) {
              throw new NiciraNvpApiException(e, e.getErrorCode());
          }
      }
  
      public List<L2GatewayServiceConfig> findL2GatewayServiceByUuidAndType(final String gatewayServiceUuid, final String serviceConfigType) throws NiciraNvpApiException{
          final String uri = buildGatewayServiceUri();
          final Map<String, String> params = buildBasicParametersMap(WILDCARD_QUERY_PARAMETER);
          params.put(UUID_QUERY_PARAMETER, gatewayServiceUuid);
          params.put(TYPES_QUERY_PARAMETER, serviceConfigType);
  
          try {
              final Type niciraListType = new TypeToken<NiciraNvpList<L2GatewayServiceConfig>>() {
              }.getType();
              return restConnector.<NiciraNvpList<L2GatewayServiceConfig>> executeRetrieveObject(niciraListType, uri, params).getResults();
          }
          catch (final CloudstackRESTException e){
              throw new NiciraNvpApiException(e, e.getErrorCode());
          }
      }
  
      private static String buildGatewayServiceUri(){
          return GATEWAY_SERVICE_PREFIX;
      }
  
      private static Map<String, String> buildBasicParametersMap(final String fieldsQueryValue) {
          final Map<String, String> params = new HashMap<String, String>();
          params.put(FIELDS_QUERY_PARAMETER, fieldsQueryValue);
          return params;
      }
  
      private static String buildUri(final String uriPrefix, final String uuid) {
          return uriPrefix + "/" + uuid;
      }
  
      private static String buildLogicalSwitchUri(final String logicalSwitchUuid) {
          return buildUri(SWITCH_URI_PREFIX, logicalSwitchUuid);
      }
  
      private static String buildLogicalSwitchElementUri(final String logicalSwitchUuid, final String logicalElementType) {
          return buildLogicalSwitchUri(logicalSwitchUuid) + logicalElementType;
      }
  
      private static String buildLogicalSwitchElementUri(final String logicalSwitchUuid, final String logicalElementType, final String elementUuid) {
          return buildLogicalSwitchElementUri(logicalSwitchUuid, logicalElementType) + "/" + elementUuid.toString();
      }
  
      private static String buildLogicalRouterUri(final String logicalRouterUuid) {
          return buildUri(ROUTER_URI_PREFIX, logicalRouterUuid);
      }
  
      private static String buildLogicalRouterElementUri(final String logicalRouterUuid, final String logicalElementType) {
          return buildLogicalRouterUri(logicalRouterUuid) + logicalElementType;
      }
  
      private static String buildLogicalRouterElementUri(final String logicalRouterUuid, final String logicalRouterElementType, final String elementUuid) {
          return buildLogicalRouterElementUri(logicalRouterUuid, logicalRouterElementType) + "/" + elementUuid.toString();
      }
  
      public static class Builder {
          private String host;
          private String username;
          private String password;
          private CloseableHttpClient httpClient;
          private HttpClientContext httpClientContext = HttpClientContext.create();
  
          public Builder host(final String host) {
              this.host = host;
              return this;
          }
  
          public Builder username(final String username) {
              this.username = username;
              return this;
          }
  
          public Builder password(final String password) {
              this.password = password;
              return this;
          }
  
          public Builder httpClient(final CloseableHttpClient httpClient) {
              this.httpClient = httpClient;
              return this;
          }
  
          public Builder httpClientContext(final HttpClientContext httpClientContext) {
              this.httpClientContext = httpClientContext;
              return this;
          }
  
          public NiciraNvpApi build() {
              return new NiciraNvpApi(this);
          }
      }
  }
  