Package org.geomajas.internal.service.pipeline

Source Code of org.geomajas.internal.service.pipeline.PipelineServiceImpl$PipelineStamp

/*
* This is part of Geomajas, a GIS framework, http://www.geomajas.org/.
*
* Copyright 2008-2011 Geosparc nv, http://www.geosparc.com/, Belgium.
*
* The program is available in open source according to the GNU Affero
* General Public License. All contributions in this program are covered
* by the Geomajas Contributors License Agreement. For full licensing
* details, see LICENSE.txt in the project root.
*/

package org.geomajas.internal.service.pipeline;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.annotation.PostConstruct;

import org.geomajas.global.ExceptionCode;
import org.geomajas.global.GeomajasException;
import org.geomajas.service.pipeline.PipelineContext;
import org.geomajas.service.pipeline.PipelineHook;
import org.geomajas.service.pipeline.PipelineInfo;
import org.geomajas.service.pipeline.PipelineInterceptor;
import org.geomajas.service.pipeline.PipelineInterceptor.ExecutionMode;
import org.geomajas.service.pipeline.PipelineService;
import org.geomajas.service.pipeline.PipelineStep;
import org.geotools.util.Utilities;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
* Service which allows "executing" a pipeline.
*
* @param <RESPONSE>
*            type of response object for the pipeline
*
* @author Joachim Van der Auwera
* @author Jan De Moerloose
*/
@Component
public class PipelineServiceImpl<RESPONSE> implements PipelineService<RESPONSE> {

  private final Logger log = LoggerFactory.getLogger(PipelineServiceImpl.class);
 
  private static final String INDENT = "   ";

  @Autowired
  private List<PipelineInfo<RESPONSE>> pipelineInfos;

  private Map<PipelineStamp, PipelineInfo<RESPONSE>> pipelineMap;

  /** @inheritDoc */
  public void execute(String key, String layerId, PipelineContext context, RESPONSE response)
      throws GeomajasException {
    execute(getPipeline(key, layerId), context, response);
  }

  /** @inheritDoc */
  public void execute(PipelineInfo<RESPONSE> pipeline, PipelineContext startContext, RESPONSE response)
      throws GeomajasException {
    PipelineContext context = startContext;
    if (null == context) {
      context = createContext();
    }
    long ps = 0;
    if (log.isDebugEnabled()) {
      ps = System.currentTimeMillis();
      log.debug("execute pipeline {}", pipeline.getPipelineName());
    }
    for (PipelineStep<RESPONSE> step : pipeline.getPipeline()) {
      if (context.isFinished()) {
        log.debug("context finished, pipeline {} execution done.", pipeline.getPipelineName());
        break;
      }
      long ts = 0;
      if (log.isDebugEnabled()) {
        log.debug("execute step {} for pipeline {}.", step.getId(), pipeline.getPipelineName());
        ts = System.currentTimeMillis();
      }
      step.execute(context, response);
      if (log.isDebugEnabled()) {
        log.debug("done step {}, time {}s", step.getId(), (System.currentTimeMillis() - ts) / 1000.0);
      }
    }
    if (log.isDebugEnabled()) {
      log.debug("pipeline done, {}, time {}s", pipeline.getPipelineName(),
          (System.currentTimeMillis() - ps) / 1000.0);
    }
  }

  /** @inheritDoc */
  @SuppressWarnings("unchecked")
  public PipelineInfo<RESPONSE> getPipeline(String pipelineName, String layerId) throws GeomajasException {
    PipelineStamp stamp = new PipelineStamp(pipelineName, layerId);
    PipelineInfo<RESPONSE> pipeline = pipelineMap.get(stamp);
    if (null == pipeline) {
      stamp = new PipelineStamp(pipelineName, null);
      pipeline = pipelineMap.get(stamp);
    }
    if (null == pipeline) {
      throw new GeomajasException(ExceptionCode.PIPELINE_UNKNOWN, pipelineName, layerId);
    }
    return pipeline;
  }

  /** @inheritDoc */
  public PipelineContext createContext() {
    return new PipelineContextImpl();
  }

  @SuppressWarnings("unchecked")
  @PostConstruct
  protected void postConstruct() throws GeomajasException {
    // sort pipelines so a pipeline is always before it's delegate (to make sure delegates are not handled before
    // delegating), and some sanity checks
    List<PipelineInfo<RESPONSE>> delegateLast = new ArrayList<PipelineInfo<RESPONSE>>();
    for (PipelineInfo<RESPONSE> pipeline : pipelineInfos) {
      if (null != pipeline.getPipeline() && null != pipeline.getDelegatePipeline()) {
        throw new GeomajasException(ExceptionCode.PIPELINE_DEFINED_AND_DELEGATE, pipeline.getPipelineName());
      }

      List<PipelineInfo<RESPONSE>> missing = new ArrayList<PipelineInfo<RESPONSE>>();
      PipelineInfo<RESPONSE> delegate = pipeline;
      while (delegate != null && !delegateLast.contains(delegate)) {
        missing.add(delegate);
        delegate = delegate.getDelegatePipeline();
      }
      // prepend missing delegate last
      delegateLast.addAll(0, missing);
    }

    // extend the delegating pipelines
    for (PipelineInfo<RESPONSE> pipeline : delegateLast) {
      if (pipeline.getPipeline() == null) {
        log.debug("extending pipeline {} with delegate {}", pipeline.getPipelineName(), pipeline
            .getDelegatePipeline() == null ? "none" : pipeline.getDelegatePipeline().getPipelineName());
        extendPipeline(pipeline);
        for (PipelineStep<RESPONSE> pipelineStep : pipeline.getPipeline()) {
          log.debug("added step {} to pipeline {}", pipelineStep.getId(), pipeline.getPipelineName());
        }
        log.debug("pipeline {} finished", pipeline.getPipelineName());
      } else {
        log.debug("pipeline {} with delegate {} has no extensions", pipeline.getPipelineName(), pipeline
            .getDelegatePipeline() == null ? "none" : pipeline.getDelegatePipeline().getPipelineName());
      }
    }
    // add the interceptors
    for (PipelineInfo<RESPONSE> pipeline : delegateLast) {
      interceptPipeline(pipeline);
    }
    // remove double names
    pipelineMap = new HashMap<PipelineStamp, PipelineInfo<RESPONSE>>();
    for (PipelineInfo<RESPONSE> pipeline : pipelineInfos) {
      // equal stamps will be overwritten here, last one wins !
      pipelineMap.put(new PipelineStamp(pipeline), pipeline);
    }
    if (log.isDebugEnabled()) {
      log.debug("listing pipeline structures");
      for (PipelineInfo<RESPONSE> pipeline : pipelineMap.values()) {
        log.debug("");
        print(pipeline);
      }
    }
  }

  private void print(PipelineInfo<RESPONSE> pipeline) {
    log.debug("<pipeline name = '{}' layer = '{}'>", pipeline.getPipelineName(), pipeline.getLayerId());
    for (PipelineStep<RESPONSE> step : pipeline.getPipeline()) {
      printStep(INDENT, step);
    }
    log.debug("</pipeline>");
  }

  private void printStep(String indent, PipelineStep<RESPONSE> step) {
    if (step instanceof PipelineInterceptorStep) {
      PipelineInterceptorStep<RESPONSE> pis = (PipelineInterceptorStep<RESPONSE>) step;
      log.debug(indent + "<interceptor id = '{}'>", pis.getId());
      for (PipelineStep<RESPONSE> st : pis.getSteps()) {
        printStep(indent + INDENT, st);
      }
      log.debug(indent + "</interceptor>");
    } else {
      log.debug(indent + "<step id = '{}'/>", step.getId());
    }
  }

  @SuppressWarnings("unchecked")
  private void extendPipeline(PipelineInfo<RESPONSE> pipeline) throws GeomajasException {
    log.debug("extending pipeline {}", pipeline.getPipelineName());
    // collect extensions and steps
    List<PipelineStep<RESPONSE>> steps = new ArrayList<PipelineStep<RESPONSE>>();
    Map<String, List<PipelineStep<RESPONSE>>> extensions = new HashMap<String, List<PipelineStep<RESPONSE>>>();
    PipelineInfo<RESPONSE> delegate = pipeline;
    int total = 0;
    while (delegate != null) {
      if (delegate.getExtensions() != null) {
        for (Map.Entry<String, PipelineStep<RESPONSE>> extensionEntry : delegate.getExtensions().entrySet()) {
          if (!extensions.containsKey(extensionEntry.getKey())) {
            extensions.put(extensionEntry.getKey(), new ArrayList<PipelineStep<RESPONSE>>());
          }
          extensions.get(extensionEntry.getKey()).add(extensionEntry.getValue());
          total++;
        }
      }
      if (delegate.getPipeline() != null) {
        steps.addAll(delegate.getPipeline());
      }
      delegate = delegate.getDelegatePipeline();
    }

    // we have everything, lets extend...
    int count = 0;
    for (int i = steps.size() - 1; i >= 0; i--) {
      PipelineStep<RESPONSE> step = steps.get(i);
      if (step instanceof PipelineHook) {
        List<PipelineStep<RESPONSE>> extList = extensions.get(step.getId());
        if (null != extList) {
          steps.addAll(i + 1, extList);
          for (PipelineStep<RESPONSE> ext : extList) {
            log.debug("extending pipeline {} with step {}", pipeline.getPipelineName(), ext.getId());
          }
          count += extList.size();
        }
      }
    }
    if (count != total) {
      throw new GeomajasException(ExceptionCode.PIPELINE_UNSATISFIED_EXTENSION, pipeline.getPipelineName(),
          pipeline.getLayerId());
    }
    pipeline.setPipeline(steps);
  }

  @SuppressWarnings("unchecked")
  private void interceptPipeline(PipelineInfo<RESPONSE> pipeline) throws GeomajasException {
    // collect interceptors
    List<PipelineInterceptor<RESPONSE>> interceptors = new ArrayList<PipelineInterceptor<RESPONSE>>();
    PipelineInfo<RESPONSE> delegate = pipeline;
    while (delegate != null) {
      if (delegate.getInterceptors() != null) {
        // prepend to preserve order
        interceptors.addAll(0, delegate.getInterceptors());
      }
      delegate = delegate.getDelegatePipeline();
    }

    // create a step for each interceptor
    List<PipelineInterceptorStep<RESPONSE>> interceptorSteps = new ArrayList<PipelineInterceptorStep<RESPONSE>>();
    List<PipelineStep<RESPONSE>> steps = new ArrayList<PipelineStep<RESPONSE>>(pipeline.getPipeline());
    for (PipelineInterceptor<RESPONSE> interceptor : interceptors) {
      interceptorSteps.add(new PipelineInterceptorStep<RESPONSE>(interceptor, steps));
    }
    // sort by width, smallest first
    Collections.sort(interceptorSteps, new WidthComparator());

    // construct the nested hierarchy of normal steps and interceptor steps
    for (PipelineInterceptorStep<RESPONSE> interceptorStep : interceptorSteps) {
      log.debug("adding interceptor {} to pipeline {}", interceptorStep.getId(), pipeline.getPipelineName());
      int fromIndex = -1;
      int toIndex = -1;
      // find the from and to step indices (takes nesting into account)
      for (int i = 0 ; i < steps.size() ; i++) {
        PipelineStep<RESPONSE> pipelineStep = steps.get(i);
        if (pipelineStep instanceof PipelineInterceptorStep) {
          PipelineInterceptorStep<RESPONSE> pis = (PipelineInterceptorStep<RESPONSE>) pipelineStep;
          PipelineStep<RESPONSE> fromStep = pis.getFromStep();
          if (fromStep.equals(interceptorStep.getFromStep())) {
            fromIndex = i;
          }
          PipelineStep<RESPONSE> toStep = pis.getToStep();
          if (toStep.equals(interceptorStep.getToStep())) {
            toIndex = i;
          }
        } else {
          if (pipelineStep.equals(interceptorStep.getFromStep())) {
            fromIndex = i;
          }
          if (pipelineStep.equals(interceptorStep.getToStep())) {
            toIndex = i;
          }
        }
      }
      if (fromIndex > toIndex) {
        throw new GeomajasException(ExceptionCode.PIPELINE_INTERCEPTOR_INVALID_NESTING,
            interceptorStep.getId());
      }
      // nest the steps
      interceptorStep.setSteps(new ArrayList<PipelineStep<RESPONSE>>(steps.subList(fromIndex, toIndex + 1)));
      for (int i = toIndex; i >= fromIndex; i--) {
        steps.remove(i);
      }
      steps.add(fromIndex, interceptorStep);
    }
    pipeline.setPipeline(steps);
  }

  /**
   * Pipeline step that executes an interceptor.
   *
   * @author Jan De Moerloose
   *
   * @param <T>
   */
  protected class PipelineInterceptorStep<T> implements PipelineStep<T> {

    private PipelineInterceptor<T> interceptor;

    private List<PipelineStep<T>> steps;

    private PipelineStep<T> fromStep;

    private PipelineStep<T> toStep;

    private int width;

    public PipelineInterceptorStep(PipelineInterceptor<T> interceptor, List<PipelineStep<T>> steps)
        throws GeomajasException {
      this.interceptor = interceptor;
      int fromIndex = -1;
      int toIndex = -1;
      for (int i = 0 ; i < steps.size() ; i++) {
        PipelineStep<T> pipelineStep = steps.get(i);
        if (pipelineStep.getId().equals(interceptor.getFromStepId())) {
          fromIndex = i;
          fromStep = pipelineStep;
        }
        if (pipelineStep.getId().equals(interceptor.getToStepId())) {
          toIndex = i;
          toStep = pipelineStep;
        }
      }
      if (fromIndex == -1) {
        if (null != interceptor.getFromStepId()) {
          throw new GeomajasException(ExceptionCode.PIPELINE_INTERCEPTOR_INVALID_STEP, interceptor.getId(),
              interceptor.getFromStepId());
        } else {
          fromIndex = 0;
          fromStep = steps.get(0);
        }
      }
      if (toIndex == -1) {
        if (null != interceptor.getToStepId()) {
          throw new GeomajasException(ExceptionCode.PIPELINE_INTERCEPTOR_INVALID_STEP, interceptor.getId(),
              interceptor.getToStepId());
        } else {
          int pos = steps.size() - 1;
          toIndex = pos;
          toStep = steps.get(pos);
        }
      }
      if (fromIndex > toIndex) {
        throw new GeomajasException(ExceptionCode.PIPELINE_INTERCEPTOR_STEPS_ORDER, interceptor.getId());
      }
      width = toIndex - fromIndex + 1;
    }

    public void setSteps(List<PipelineStep<T>> steps) {
      this.steps = steps;
    }

    public List<PipelineStep<T>> getSteps() {
      return steps;
    }

    public String getId() {
      return interceptor.getId();
    }

    public int getWidth() {
      return width;
    }

    public PipelineStep<T> getFromStep() {
      return fromStep;
    }

    public PipelineStep<T> getToStep() {
      return toStep;
    }

    public void execute(PipelineContext context, T response) throws GeomajasException {
      log.debug("execute beforeSteps for interceptor {}", interceptor.getId());
      long its = 0;
      if (log.isDebugEnabled()) {
        its  = System.currentTimeMillis();
      }
      ExecutionMode mode = interceptor.beforeSteps(context, response);
      if (mode == null) {
        mode = ExecutionMode.EXECUTE_ALL;
      }
      switch (mode) {
        case EXECUTE_ALL:
        case EXECUTE_STEPS_NOT_AFTER:
          if (!context.isFinished()) {
            for (PipelineStep<T> step : getSteps()) {
              if (context.isFinished()) {
                log.debug("context finished, interceptor {} execution done", interceptor.getId());
                break;
              }
              long ts = 0;
              if (log.isDebugEnabled()) {
                ts = System.currentTimeMillis();
                log.debug("execute step {} for interceptor {}", step.getId(), interceptor.getId());
              }
              step.execute(context, response);
              if (log.isDebugEnabled()) {
                log.debug("done step {}, time {}s", step.getId(),
                    (System.currentTimeMillis() - ts) / 1000.0);
              }
            }
          }
          break;
        default:
          log.debug("skipping steps for interceptor {}", interceptor.getId());
      }
      switch (mode) {
        case EXECUTE_ALL:
        case EXECUTE_SKIP_STEPS:
          interceptor.afterSteps(context, response);
          break;
        default:
          log.debug("skipping afterSteps for interceptor {}", interceptor.getId());
      }
      if (log.isDebugEnabled()) {
        log.debug("beforeSteps done for {}, time {}s", interceptor.getId(),
            (System.currentTimeMillis() - its) / 1000.0);
      }
    }
  }

  /**
   * Comparator for sorting interceptors by width.
   *
   * @author Jan De Moerloose
   */
  protected class WidthComparator implements Comparator<PipelineInterceptorStep<RESPONSE>> {

    public int compare(PipelineInterceptorStep<RESPONSE> o1, PipelineInterceptorStep<RESPONSE> o2) {
      return o1.getWidth() - o2.getWidth();
    }

  }

  /**
   * Stamp for detection of duplicate pipelines.
   *
   * @author Jan De Moerloose
   */
  private static final class PipelineStamp {

    private final String name;

    private final String layerId;

    public PipelineStamp(PipelineInfo info) {
      this.name = info.getPipelineName();
      this.layerId = info.getLayerId();
    }

    public PipelineStamp(String name, String layerId) {
      this.name = name;
      this.layerId = layerId;
    }

    public boolean equals(Object otherObject) {
      if (this == otherObject) {
        return true;
      }
      if (otherObject == null || !(otherObject instanceof PipelineStamp)) {
        return false;
      }
      PipelineStamp otherStamp = (PipelineStamp) otherObject;
      return Utilities.equals(name, otherStamp.name) && Utilities.equals(layerId, otherStamp.layerId);
    }

    public int hashCode() {
      int result = 13;
      result = Utilities.hash(name, result);
      result = Utilities.hash(layerId, result);
      return result;
    }
  }

}
TOP

Related Classes of org.geomajas.internal.service.pipeline.PipelineServiceImpl$PipelineStamp

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.
div>