// Path: functions-framework-api/src/main/java/com/google/cloud/functions/BackgroundFunction.java
// @FunctionalInterface
// public interface BackgroundFunction<T> {
//   /**
//    * Called to service an incoming event. This interface is implemented by user code to provide the
//    * action for a given background function. If this method throws any exception (including any
//    * {@link Error}) then the HTTP response will have a 500 status code.
//    *
//    * @param payload the payload of the event, deserialized from the original JSON string.
//    * @param context the context of the event. This is a set of values that every event has,
//    *     separately from the payload, such as timestamp and event type.
//    * @throws Exception to produce a 500 status code in the HTTP response.
//    */
//   void accept(T payload, Context context) throws Exception;
// }
// 
// Path: functions-framework-api/src/main/java/com/google/cloud/functions/CloudEventsFunction.java
// @FunctionalInterface
// public interface CloudEventsFunction {
//   /**
//    * Called to service an incoming event. This interface is implemented by user code to provide the
//    * action for a given background function. If this method throws any exception (including any
//    * {@link Error}) then the HTTP response will have a 500 status code.
//    *
//    * @param event the event.
//    * @throws Exception to produce a 500 status code in the HTTP response.
//    */
//   void accept(CloudEvent event) throws Exception;
// }
// 
// Path: functions-framework-api/src/main/java/com/google/cloud/functions/Context.java
// public interface Context {
//   /**
//    * Returns event ID.
//    *
//    * @return event ID
//    */
//   String eventId();
// 
//   /**
//    * Returns event timestamp.
//    *
//    * @return event timestamp
//    */
//   String timestamp();
// 
//   /**
//    * Returns event type.
//    *
//    * @return event type
//    */
//   String eventType();
// 
//   /**
//    * Returns event resource.
//    *
//    * @return event resource
//    */
//   String resource();
// 
//   /**
//    * Returns additional attributes from this event. For CloudEvents, the entries in this map will
//    * include the <a
//    * href="https://github.com/cloudevents/spec/blob/v1.0/spec.md#required-attributes">required
//    * attributes</a> and may include <a
//    * href="https://github.com/cloudevents/spec/blob/v1.0/spec.md#required-attributes">optional
//    * attributes</a> and <a
//    * href="https://github.com/cloudevents/spec/blob/v1.0/spec.md#extension-context-attributes">
//    * extension attributes</a>.
//    *
//    * <p>The map returned by this method may be empty but is never null.
//    *
//    * @return additional attributes form this event.
//    */
//   default Map<String, String> attributes() {
//     return Collections.emptyMap();
//   }
// }
// 
// Path: functions-framework-api/src/main/java/com/google/cloud/functions/RawBackgroundFunction.java
// @FunctionalInterface
// public interface RawBackgroundFunction {
//   /**
//    * Called to service an incoming event. This interface is implemented by user code to provide the
//    * action for a given background function. If this method throws any exception (including any
//    * {@link Error}) then the HTTP response will have a 500 status code.
//    *
//    * @param json the payload of the event, as a JSON string.
//    * @param context the context of the event. This is a set of values that every event has,
//    *     separately from the payload, such as timestamp and event type.
//    * @throws Exception to produce a 500 status code in the HTTP response.
//    */
//   void accept(String json, Context context) throws Exception;
// }


import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toMap;
import com.google.cloud.functions.BackgroundFunction;
import com.google.cloud.functions.CloudEventsFunction;
import com.google.cloud.functions.Context;
import com.google.cloud.functions.RawBackgroundFunction;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.TypeAdapter;
import io.cloudevents.CloudEvent;
import io.cloudevents.core.message.MessageReader;
import io.cloudevents.http.HttpMessageFactory;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.Reader;
import java.lang.reflect.Type;
import java.time.OffsetDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.TreeMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
    // accept(Object, Context).
    return Arrays.stream(functionClass.getMethods())
        .filter(
            m ->
                m.getName().equals("accept")
                    && m.getParameterCount() == 2
                    && m.getParameterTypes()[1] == Context.class
                    && m.getParameterTypes()[0] != Object.class)
        .map(m -> m.getGenericParameterTypes()[0])
        .findFirst();
  }

  private static Event parseLegacyEvent(HttpServletRequest req) throws IOException {
    try (BufferedReader bodyReader = req.getReader()) {
      return parseLegacyEvent(bodyReader);
    }
  }

  static Event parseLegacyEvent(Reader reader) throws IOException {
    // A Type Adapter is required to set the type of the JsonObject because CloudFunctionsContext
    // is abstract and Gson default behavior instantiates the type provided.
    TypeAdapter<CloudFunctionsContext> typeAdapter = CloudFunctionsContext.typeAdapter(new Gson());
    Gson gson =
        new GsonBuilder()
            .registerTypeAdapter(CloudFunctionsContext.class, typeAdapter)
            .registerTypeAdapter(Event.class, new Event.EventDeserializer())
            .create();
    return gson.fromJson(reader, Event.class);
  }

// Path: functions-framework-api/src/main/java/com/google/cloud/functions/BackgroundFunction.java
// @FunctionalInterface
// public interface BackgroundFunction<T> {
//   /**
//    * Called to service an incoming event. This interface is implemented by user code to provide the
//    * action for a given background function. If this method throws any exception (including any
//    * {@link Error}) then the HTTP response will have a 500 status code.
//    *
//    * @param payload the payload of the event, deserialized from the original JSON string.
//    * @param context the context of the event. This is a set of values that every event has,
//    *     separately from the payload, such as timestamp and event type.
//    * @throws Exception to produce a 500 status code in the HTTP response.
//    */
//   void accept(T payload, Context context) throws Exception;
// }
// 
// Path: functions-framework-api/src/main/java/com/google/cloud/functions/CloudEventsFunction.java
// @FunctionalInterface
// public interface CloudEventsFunction {
//   /**
//    * Called to service an incoming event. This interface is implemented by user code to provide the
//    * action for a given background function. If this method throws any exception (including any
//    * {@link Error}) then the HTTP response will have a 500 status code.
//    *
//    * @param event the event.
//    * @throws Exception to produce a 500 status code in the HTTP response.
//    */
//   void accept(CloudEvent event) throws Exception;
// }
// 
// Path: functions-framework-api/src/main/java/com/google/cloud/functions/Context.java
// public interface Context {
//   /**
//    * Returns event ID.
//    *
//    * @return event ID
//    */
//   String eventId();
// 
//   /**
//    * Returns event timestamp.
//    *
//    * @return event timestamp
//    */
//   String timestamp();
// 
//   /**
//    * Returns event type.
//    *
//    * @return event type
//    */
//   String eventType();
// 
//   /**
//    * Returns event resource.
//    *
//    * @return event resource
//    */
//   String resource();
// 
//   /**
//    * Returns additional attributes from this event. For CloudEvents, the entries in this map will
//    * include the <a
//    * href="https://github.com/cloudevents/spec/blob/v1.0/spec.md#required-attributes">required
//    * attributes</a> and may include <a
//    * href="https://github.com/cloudevents/spec/blob/v1.0/spec.md#required-attributes">optional
//    * attributes</a> and <a
//    * href="https://github.com/cloudevents/spec/blob/v1.0/spec.md#extension-context-attributes">
//    * extension attributes</a>.
//    *
//    * <p>The map returned by this method may be empty but is never null.
//    *
//    * @return additional attributes form this event.
//    */
//   default Map<String, String> attributes() {
//     return Collections.emptyMap();
//   }
// }
// 
// Path: functions-framework-api/src/main/java/com/google/cloud/functions/RawBackgroundFunction.java
// @FunctionalInterface
// public interface RawBackgroundFunction {
//   /**
//    * Called to service an incoming event. This interface is implemented by user code to provide the
//    * action for a given background function. If this method throws any exception (including any
//    * {@link Error}) then the HTTP response will have a 500 status code.
//    *
//    * @param json the payload of the event, as a JSON string.
//    * @param context the context of the event. This is a set of values that every event has,
//    *     separately from the payload, such as timestamp and event type.
//    * @throws Exception to produce a 500 status code in the HTTP response.
//    */
//   void accept(String json, Context context) throws Exception;
// }


// Path: invoker/core/src/main/java/com/google/cloud/functions/invoker/BackgroundFunctionExecutor.java
import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toMap;
import com.google.cloud.functions.BackgroundFunction;
import com.google.cloud.functions.CloudEventsFunction;
import com.google.cloud.functions.Context;
import com.google.cloud.functions.RawBackgroundFunction;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.TypeAdapter;
import io.cloudevents.CloudEvent;
import io.cloudevents.core.message.MessageReader;
import io.cloudevents.http.HttpMessageFactory;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.Reader;
import java.lang.reflect.Type;
import java.time.OffsetDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.TreeMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

    // accept(Object, Context).
    return Arrays.stream(functionClass.getMethods())
        .filter(
            m ->
                m.getName().equals("accept")
                    && m.getParameterCount() == 2
                    && m.getParameterTypes()[1] == Context.class
                    && m.getParameterTypes()[0] != Object.class)
        .map(m -> m.getGenericParameterTypes()[0])
        .findFirst();
  }

  private static Event parseLegacyEvent(HttpServletRequest req) throws IOException {
    try (BufferedReader bodyReader = req.getReader()) {
      return parseLegacyEvent(bodyReader);
    }
  }

  static Event parseLegacyEvent(Reader reader) throws IOException {
    // A Type Adapter is required to set the type of the JsonObject because CloudFunctionsContext
    // is abstract and Gson default behavior instantiates the type provided.
    TypeAdapter<CloudFunctionsContext> typeAdapter = CloudFunctionsContext.typeAdapter(new Gson());
    Gson gson =
        new GsonBuilder()
            .registerTypeAdapter(CloudFunctionsContext.class, typeAdapter)
            .registerTypeAdapter(Event.class, new Event.EventDeserializer())
            .create();
    return gson.fromJson(reader, Event.class);
  }

  private static Context contextFromCloudEvent(CloudEvent cloudEvent) {