All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.mx.path.gateway.Gateway Maven / Gradle / Ivy

There is a newer version: 4.2.0
Show newest version
package com.mx.path.gateway;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

import lombok.Getter;
import lombok.Setter;
import lombok.Singular;
import lombok.experimental.SuperBuilder;

import com.google.common.collect.ImmutableList;
import com.mx.path.connect.messaging.remote.RemoteService;
import com.mx.path.core.common.collection.ObjectMap;
import com.mx.path.core.common.event.EventBus;
import com.mx.path.core.common.reflection.Annotations;
import com.mx.path.core.context.RequestContext;
import com.mx.path.core.context.facility.Facilities;
import com.mx.path.gateway.accessor.Accessor;
import com.mx.path.gateway.accessor.AccessorResponse;
import com.mx.path.gateway.behavior.GatewayBehavior;
import com.mx.path.gateway.behavior.StartBehavior;
import com.mx.path.gateway.configuration.AccessorDescriber;
import com.mx.path.gateway.configuration.RootGateway;
import com.mx.path.gateway.context.GatewayRequestContext;
import com.mx.path.gateway.event.AfterAccessorEvent;
import com.mx.path.gateway.event.BeforeAccessorEvent;
import com.mx.path.gateway.service.GatewayService;

@SuperBuilder
public abstract class Gateway {
  @Getter
  private String clientId;

  @Getter
  private T baseAccessor;

  @Setter
  private Gateway parent;

  @Getter
  @Setter
  private RemoteService remote;

  @Getter
  @Singular
  private List behaviors = Collections.emptyList();

  @Getter
  @Singular
  private List services;

  public Gateway() {
  }

  public Gateway(String clientId) {
    this.clientId = clientId;
  }

  public final boolean isTopLevel() {
    return Annotations.hasAnnotation(getClass(), RootGateway.class);
  }

  /**
   * Use reflection to discover all child gateways belonging to this
   *
   * 

Gateways must be exposed via a getter * * @return List of BaseGateway */ public ImmutableList gateways() { return ImmutableList.copyOf(Arrays.stream(getClass().getMethods()) .filter(method -> Gateway.class.isAssignableFrom(method.getReturnType())) .filter(method -> method.getReturnType() != Gateway.class) .map(method -> { try { return (Gateway) method.invoke(this); } catch (InvocationTargetException | IllegalAccessException e) { return null; } }) .filter(Objects::nonNull) .collect(Collectors.toList())); } /** * Registers all remote gateways */ public void registerRemotes() { if (remote != null) { remote.register(); } gateways().forEach(Gateway::registerRemotes); } /** * Start all services */ public void startServices() { services.forEach(service -> { if (service.getGateway() == null) { service.setGateway(this); } service.start(); }); gateways().forEach(Gateway::startServices); } /** * Describe this gateway * * @return new description */ public final ObjectMap describe() { ObjectMap description = new ObjectMap(); describe(description); return description; } /** * Fill in description * *

Override and call super to get complete description. * * @param description description object */ public void describe(ObjectMap description) { if (isTopLevel()) { Facilities.describe(clientId, description.createMap("facilities")); } else { try { Method getAccessorMethod = this.getClass().getDeclaredMethod("getAccessor"); Accessor accessor = (Accessor) getAccessorMethod.invoke(this); describeAccessors(accessor, description.createMap("accessor")); } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException ignored) { } } description.put("services", services.stream().map(GatewayService::describe).collect(Collectors.toList())); description.put("behaviors", behaviors.stream().map(GatewayBehavior::describe).collect(Collectors.toList())); if (baseAccessor == null) { throw new RuntimeException("Base accessor not provided to gateway " + getClass().getTypeName()); } } /** * Generate description for all accessors in given * * @param accessorToDescribe accessor to describe * @param description description being built */ public void describeAccessors(Accessor accessorToDescribe, ObjectMap description) { AccessorDescriber accessorDescriber = new AccessorDescriber(); accessorDescriber.describe(accessorToDescribe, description); } @SuppressWarnings("unchecked") public final T getParent() { return (T) parent; } /** * Build the behavior call stack (decorator). * * todo: We had this setup as a lazy loaded singleton to avoid rebuilding it with every request. * It was causing collisions. Not sure why. Just building fresh, with every request, for now. * The collision exposed itself when behaviors "cross-called" other gateway actions. * Seemed like the first gateway to execute had it's behavior set in stone. Doesn't seem like that should * have happened. ¯\_(ツ)_/¯ * * @return head of behavior stack */ protected final GatewayBehavior buildStack() { GatewayBehavior stack = new StartBehavior(); GatewayBehavior previous = stack; for (GatewayBehavior behavior : behaviors) { previous.setNextBehavior(behavior); previous = behavior; } return stack; } protected final AccessorResponse executeBehaviorStack(Class responseType, GatewayRequestContext request, GatewayBehavior terminatingBehavior) { return buildStack().execute(responseType, request, terminatingBehavior); } public final Gateway root() { if (parent != null) { return parent.root(); } return isTopLevel() ? this : null; } /** * Emit afterAccessorEvent * * @param gateway current gateway * @param callingAccessor the current accessor * @param requestContext request context */ public final void afterAccessor(Gateway gateway, Accessor callingAccessor, RequestContext requestContext) { if (requestContext.getClientId() == null) { return; } EventBus eventBus = Facilities.getEventBus(requestContext.getClientId()); if (eventBus == null) { return; } eventBus.post(AfterAccessorEvent.builder() .currentAccessor(callingAccessor) .gateway(gateway) .requestContext(requestContext) .build()); } /** * Emit beforeAccessorEvent * * @param gateway current gateway * @param callingAccessor current accessor * @param requestContext request context */ public final void beforeAccessor(Gateway gateway, Accessor callingAccessor, RequestContext requestContext) { if (requestContext.getClientId() == null) { return; } EventBus eventBus = Facilities.getEventBus(requestContext.getClientId()); if (eventBus == null) { return; } eventBus.post(BeforeAccessorEvent.builder() .currentAccessor(callingAccessor) .gateway(gateway) .requestContext(requestContext) .build()); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy