Package co.cask.cdap.internal.app.runtime.procedure

Source Code of co.cask.cdap.internal.app.runtime.procedure.ProcedureHandlerMethod

/*
* Copyright © 2014 Cask Data, Inc.
*
* Licensed 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 co.cask.cdap.internal.app.runtime.procedure;

import co.cask.cdap.api.annotation.Handle;
import co.cask.cdap.api.procedure.Procedure;
import co.cask.cdap.api.procedure.ProcedureRequest;
import co.cask.cdap.api.procedure.ProcedureResponder;
import co.cask.cdap.api.procedure.ProcedureResponse;
import co.cask.cdap.app.program.Program;
import co.cask.cdap.common.lang.InstantiatorFactory;
import co.cask.cdap.common.lang.PropertyFieldSetter;
import co.cask.cdap.common.logging.LoggingContextAccessor;
import co.cask.cdap.internal.app.runtime.DataFabricFacade;
import co.cask.cdap.internal.app.runtime.DataSetFieldSetter;
import co.cask.cdap.internal.app.runtime.MetricsFieldSetter;
import co.cask.cdap.internal.lang.Reflections;
import co.cask.tephra.TransactionExecutor;
import co.cask.tephra.TransactionFailureException;
import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.reflect.TypeToken;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.Set;

/**
*
*/
final class ProcedureHandlerMethod implements HandlerMethod {

  private static final Logger LOG = LoggerFactory.getLogger(ProcedureHandlerMethod.class);

  private static final String HANDLER_METHOD_PREFIX = "handle";
  private static final String ANY_METHOD = "";

  private final Procedure procedure;
  private final DataFabricFacade dataFabricFacade;
  private final Map<String, HandlerMethod> handlers;
  private final BasicProcedureContext context;

  ProcedureHandlerMethod(Program program, DataFabricFacade dataFabricFacade,
                         BasicProcedureContext context) throws ClassNotFoundException {

    this.dataFabricFacade = dataFabricFacade;
    this.context = context;

    try {
      TypeToken<? extends Procedure> procedureType = TypeToken.of(program.<Procedure>getMainClass());
      procedure = new InstantiatorFactory(false).get(procedureType).create();
      Reflections.visit(procedure, TypeToken.of(procedure.getClass()),
                        new PropertyFieldSetter(context.getSpecification().getProperties()),
                        new DataSetFieldSetter(context),
                        new MetricsFieldSetter(context.getMetrics()));

      handlers = createHandlerMethods(procedure, procedureType, dataFabricFacade);

      // TODO: It's a bit hacky, since we know there is one instance per execution handler thread.
      LoggingContextAccessor.setLoggingContext(context.getLoggingContext());

    } catch (Throwable t) {
      // make sure the context releases all resources, datasets, ...
      context.close();
      throw Throwables.propagate(t);
    }
  }

  public Procedure getProcedure() {
    return procedure;
  }

  public BasicProcedureContext getContext() {
    return context;
  }

  public void init() {
    try {
      dataFabricFacade.createTransactionExecutor().execute(new TransactionExecutor.Subroutine() {
        @Override
        public void apply() throws Exception {
          LOG.info("Initializing procedure: " + context);
          procedure.initialize(context);
          LOG.info("Procedure initialized: " + context);
        }
      });
    } catch (TransactionFailureException e) {
      Throwable cause = e.getCause() == null ? e : e.getCause();
      LOG.error("Procedure throws exception during init.", cause);
      // make sure the context releases all resources, datasets, ...
      context.close();
      throw Throwables.propagate(cause);
    } catch (InterruptedException e) {
      context.close();
      Thread.currentThread().interrupt();
      throw Throwables.propagate(e);
    }
  }

  @Override
  public void handle(ProcedureRequest request, ProcedureResponder responder) {
    context.getProgramMetrics().increment("query.requests", 1);
    HandlerMethod handlerMethod = handlers.get(request.getMethod());
    if (handlerMethod == null) {
      LOG.error("Unsupport procedure method " + request.getMethod() + " on procedure " + procedure.getClass());
      context.getProgramMetrics().increment("query.failures", 1);
      try {
        responder.stream(new ProcedureResponse(ProcedureResponse.Code.NOT_FOUND));
      } catch (IOException e) {
        throw Throwables.propagate(e);
      }
      return;
    }

    try {
      ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader();
      Thread.currentThread().setContextClassLoader(context.getProgram().getClassLoader());
      handlerMethod.handle(request, responder);
      Thread.currentThread().setContextClassLoader(oldClassLoader);
      context.getProgramMetrics().increment("query.processed", 1);
    } catch (Throwable t) {
      context.getProgramMetrics().increment("query.failures", 1);
      throw Throwables.propagate(t);
    }
  }

  private Map<String, HandlerMethod> createHandlerMethods(Procedure procedure,
                                                          TypeToken<? extends Procedure> procedureType,
                                                          DataFabricFacade dataFabricFacade) {

    ImmutableMap.Builder<String, HandlerMethod> result = ImmutableMap.builder();

    // Walk up the hierarchy of procedure class.
    for (TypeToken<?> type : procedureType.getTypes().classes()) {
      if (type.getRawType().equals(Object.class)) {
        break;
      }

      // Gather all handler method
      for (Method method : type.getRawType().getDeclaredMethods()) {
        Handle handleAnnotation = method.getAnnotation(Handle.class);
        if (!method.getName().startsWith(HANDLER_METHOD_PREFIX) && handleAnnotation == null) {
          continue;
        }

        Set<String> methodNames;
        if (handleAnnotation == null || handleAnnotation.value().length == 0) {
          methodNames = ImmutableSet.of(ANY_METHOD);
        } else {
          methodNames = ImmutableSet.copyOf(handleAnnotation.value());
        }

        for (String methodName : methodNames) {
          result.put(methodName, new ReflectionHandlerMethod(procedure, method, dataFabricFacade));
        }
      }
    }

    return result.build();
  }

  private void setField(Procedure procedure, Field field, Object value) {
    if (!field.isAccessible()) {
      field.setAccessible(true);
    }
    try {
      field.set(procedure, value);
    } catch (IllegalAccessException e) {
      throw Throwables.propagate(e);
    }
  }
}
TOP

Related Classes of co.cask.cdap.internal.app.runtime.procedure.ProcedureHandlerMethod

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.