content: package com.amazon.ask;
  
  import com.amazon.ask.impl.AbstractSkill;
  import com.amazon.ask.model.Request;
  import com.amazon.ask.model.services.Serializer;
  import com.amazon.ask.model.utils.SubTypesManifest;
  import com.amazon.ask.request.UnmarshalledRequest;
  import com.amazon.ask.request.dispatcher.GenericRequestDispatcher;
  import com.amazon.ask.request.dispatcher.impl.BaseRequestDispatcher;
  import com.amazon.ask.request.impl.BaseUnmarshalledRequest;
  import com.amazon.ask.util.JacksonSerializer;
  import com.amazon.ask.util.impl.JacksonJsonMarshaller;
  import com.amazon.ask.util.impl.JacksonJsonUnmarshaller;
  import com.amazon.ask.attributes.persistence.PersistenceAdapter;
  import com.amazon.ask.builder.CustomSkillConfiguration;
  import com.amazon.ask.dispatcher.request.handler.HandlerInput;
  import com.amazon.ask.model.RequestEnvelope;
  import com.amazon.ask.model.Response;
  import com.amazon.ask.model.ResponseEnvelope;
  import com.amazon.ask.model.services.ApiClient;
  import com.amazon.ask.model.services.ApiConfiguration;
  import com.amazon.ask.model.services.DefaultApiConfiguration;
  import com.amazon.ask.model.services.ServiceClientFactory;
  import com.amazon.ask.response.template.TemplateFactory;
  import com.amazon.ask.util.SdkConstants;
  import com.amazon.ask.util.UserAgentUtils;
  import com.fasterxml.jackson.databind.JsonNode;
  import org.slf4j.Logger;
  
  import java.util.Arrays;
  import java.util.Optional;
  
  import static org.slf4j.LoggerFactory.getLogger;
  
  /**
   * Custom type Alexa Skill.
   */
  public class CustomSkill extends AbstractSkill<RequestEnvelope, ResponseEnvelope> implements AlexaSkill<RequestEnvelope, ResponseEnvelope> {
  
      /**
       * Logger instance to log information for debugging purposes.
       */
      private static final Logger LOGGER = getLogger(CustomSkill.class);
  
      /**
       * Request Dispatcher.
       */
      protected final GenericRequestDispatcher<HandlerInput, Optional<Response>> requestDispatcher;
  
      /**
       * store skill attributes to a persistence layer.
       */
      protected final PersistenceAdapter persistenceAdapter;
  
      /**
       * Client to make external API calls.
       */
      protected final ApiClient apiClient;
  
      /**
       * Serializer for marshalling and un-marshalling purposes.
       */
      protected final Serializer serializer;
  
      /**
       * Custom user agent.
       */
      protected final String customUserAgent;
  
      /**
       * Unique ID associated with a Skill.
       */
      protected final String skillId;
  
      /**
       * Interface to process template and data to generate skill response.
       */
      protected final TemplateFactory<HandlerInput, Response> templateFactory;
  
      /**
       * Constructor for CustomSkill.
       * @param configuration custom skill configuration.
       */
      public CustomSkill(final CustomSkillConfiguration configuration) {
          super(JacksonJsonUnmarshaller.withTypeBinding(RequestEnvelope.class, Arrays.asList("request", "type"),
                  SubTypesManifest.getSubType(Request.class)),
                  JacksonJsonMarshaller.forType(ResponseEnvelope.class));
          this.persistenceAdapter = configuration.getPersistenceAdapter();
          this.requestDispatcher = BaseRequestDispatcher.<HandlerInput, Optional<Response>>builder()
                  .withRequestMappers(configuration.getRequestMappers())
                  .withHandlerAdapters(configuration.getHandlerAdapters())
                  .withExceptionMapper(configuration.getExceptionMapper())
                  .withRequestInterceptors(configuration.getRequestInterceptors())
                  .withResponseInterceptors(configuration.getResponseInterceptors())
                  .build();
          this.apiClient = configuration.getApiClient();
          this.serializer = new JacksonSerializer();
          this.customUserAgent = configuration.getCustomUserAgent();
          this.skillId = configuration.getSkillId();
          this.templateFactory = configuration.getTemplateFactory();
      }
  
      /**
       * {@inheritDoc}.
       */
      public ResponseEnvelope invoke(final RequestEnvelope requestEnvelope) {
          return invoke(requestEnvelope, null);
      }
  
      /**
       * {@inheritDoc}.
       */
      public ResponseEnvelope invoke(final RequestEnvelope requestEnvelope, final Object context) {
          return invoke(new BaseUnmarshalledRequest<>(requestEnvelope, null), context);
      }
  
      /**
       * Invokes the dispatcher to handler the request envelope and construct the handler input.
       * @param unmarshalledRequest unmarshalled output from {@link JacksonJsonUnmarshaller}, containing a
       *                            {@link RequestEnvelope} and a JSON representation of the request.
       * @param context context
       * @return optional request envelope
       */
      protected ResponseEnvelope invoke(final UnmarshalledRequest<RequestEnvelope> unmarshalledRequest, final Object context) {
          RequestEnvelope requestEnvelope = unmarshalledRequest.getUnmarshalledRequest();
          JsonNode requestEnvelopeJson = unmarshalledRequest.getRequestJson();
  
          if (skillId != null && !requestEnvelope.getContext().getSystem().getApplication().getApplicationId().equals(skillId)) {
              LOGGER.debug("AlexaSkill ID verification failed. Expected skillId: {} and skillId in the request: {}",
                      skillId, requestEnvelope.getContext().getSystem().getApplication().getApplicationId());
              return null;
          }
  
          ServiceClientFactory serviceClientFactory = apiClient != null ? ServiceClientFactory.builder()
                  .withDefaultApiConfiguration(getApiConfiguration(requestEnvelope))
                  .build() : null;
  
          HandlerInput handlerInput = HandlerInput.builder()
                  .withRequestEnvelope(requestEnvelope)
                  .withPersistenceAdapter(persistenceAdapter)
                  .withContext(context)
                  .withRequestEnvelopeJson(requestEnvelopeJson)
                  .withServiceClientFactory(serviceClientFactory)
                  .withTemplateFactory(templateFactory)
                  .build();
  
          Optional<Response> response = requestDispatcher.dispatch(handlerInput);
          return ResponseEnvelope.builder()
                  .withResponse(response != null ? response.orElse(null) : null)
                  .withSessionAttributes(requestEnvelope.getSession() != null ? handlerInput.getAttributesManager().getSessionAttributes() : null)
                  .withVersion(SdkConstants.FORMAT_VERSION)
                  .withUserAgent(UserAgentUtils.getUserAgent(customUserAgent))
                  .build();
      }
  
      /**
       * Getter for Api configuration.
       * @param requestEnvelope request envelope.
       * @return {@link ApiConfiguration}.
       */
      protected ApiConfiguration getApiConfiguration(final RequestEnvelope requestEnvelope) {
          String apiEndpoint = requestEnvelope.getContext().getSystem().getApiEndpoint();
          String apiToken = requestEnvelope.getContext().getSystem().getApiAccessToken();
          return DefaultApiConfiguration.builder()
                  .withApiClient(apiClient)
                  .withApiEndpoint(apiEndpoint)
                  .withAuthorizationValue(apiToken)
                  .withSerializer(serializer)
                  .build();
      }
  
  }
  