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

com.cinchapi.concourse.server.plugin.ConcourseRuntime Maven / Gradle / Ivy

/*
 * Copyright (c) 2013-2018 Cinchapi 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 com.cinchapi.concourse.server.plugin;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.nio.ByteBuffer;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.LogManager;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

import com.cinchapi.common.base.CheckedExceptions;
import com.cinchapi.concourse.lang.Criteria;
import com.cinchapi.concourse.lang.Language;
import com.cinchapi.concourse.server.plugin.data.ObjectResultDataset;
import com.cinchapi.concourse.server.plugin.data.TObjectResultDataset;
import com.cinchapi.concourse.server.plugin.io.PluginSerializer;
import com.cinchapi.concourse.thrift.ComplexTObject;
import com.cinchapi.concourse.util.ConcurrentMaps;
import com.cinchapi.concourse.util.Convert;
import com.google.common.collect.Lists;
import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.matcher.Matchers;

/**
 * Every Plugin application has a single instance of the
 * {@link ConcourseRuntime} class that allows the Plugin to interact with the
 * local Concourse Server node from which the Plugin was launched.
 * 
 * 

* The current runtime can be obtained using provided the {@link #getRuntime()} * method. For convenience, each {@link Plugin} provides access to the runtime * via the {@link Plugin#runtime} variable which is available to all * implementing applications. *

* * @author Jeff Nelson */ public class ConcourseRuntime extends StatefulConcourseService { static { // turn off logging from java.util.logging LogManager.getLogManager().reset(); } /** * Responsible for taking arbitrary objects and turning them into binary so * they can be sent across the wire. */ private static PluginSerializer serializer = new PluginSerializer(); /** * Return the runtime instance associated with the current plugin. * * @return the {@link ConcourseRuntime} instance associated with the current * plugin */ @NoGuice public static ConcourseRuntime getRuntime() { return INSTANCE; } /** * Return a {@link ConcourseRuntime} that is configured to intercept method * calls and proxy them through the {@link #invokeServer(String, Object...)} * method. * * @return a {@link ConcourseRuntime} instance */ @NoGuice private static ConcourseRuntime init() { Injector injector = Guice.createInjector(new ServerInvokerModule()); ConcourseRuntime runtime = injector.getInstance(ConcourseRuntime.class); return runtime; } /** * Invoke {@code method} with {@code args} on the local Concourse Server * instance that is associated with this {@link ConcourseRuntime runtime}. * * @param method the name of the method to invoke * @param args the args to pass to the method * @return the result of the method invocation */ @NoGuice @SuppressWarnings("unchecked") private static T invokeServer(String method, Object... args) { try { ConcourseRuntimeAuthorized thread = (ConcourseRuntimeAuthorized) Thread .currentThread(); List targs = Lists .newArrayListWithCapacity(args.length); Collection valueTransform = VALUE_TRANSFORM.get(method); Collection criteriaTransform = CRITERIA_TRANSFORM .get(method); for (int i = 0; i < args.length; ++i) { // Must go through each parameters and transform generic value // objects into TObjects and all Criteria into TCriteria. Object arg = args[i]; if(valueTransform.contains(i)) { if(arg instanceof List) { arg = Convert.javaListToThrift((List) arg); } else if(arg instanceof Set) { arg = Convert.javaSetToThrift((Set) arg); } else if(arg instanceof Map) { arg = Convert.javaMapToThrift((Map) arg); } else { arg = Convert.javaToThrift(arg); } } else if(criteriaTransform.contains(i)) { arg = Language.translateToThriftCriteria((Criteria) arg); } targs.add(ComplexTObject.fromJavaObject(arg)); } // Send a RemoteMethodRequest to the server, asking that the locally // invoked method be executed. The result will be placed on the // current thread's response queue RemoteMethodResponse response; synchronized (thread.accessToken()) { RemoteMethodRequest request = new RemoteMethodRequest(method, thread.accessToken(), thread.transactionToken(), thread.environment(), targs); ByteBuffer buffer = serializer.serialize(request); thread.outgoing().write(buffer); response = ConcurrentMaps.waitAndRemove(thread.responses(), thread.accessToken()); } if(!response.isError()) { Object ret = response.response.getJavaObject(); if(ret instanceof ByteBuffer) { // CON-509: PluginSerializable objects will be wrapped // within a ComplexTObject as BINARY data ret = serializer.deserialize((ByteBuffer) ret); } if(RETURN_TRANSFORM.contains(method)) { // Must transform the TObject(s) from the server into // standard java objects to conform with the // StatefulConcourseService interface. if(ret instanceof TObjectResultDataset) { ret = new ObjectResultDataset( (TObjectResultDataset) ret); } else { ret = Convert.possibleThriftToJava(ret); } } return (T) ret; } else { throw CheckedExceptions.wrapAsRuntimeException(response.error); } } catch (ClassCastException e) { throw new RuntimeException("Illegal attempt to use " + ConcourseRuntime.class.getSimpleName() + " from an unsupported thread"); } } /** * Singleton instance so that multiple plugins in the same distribution do * not unnecessarily create multiple runtime objects. */ private static final ConcourseRuntime INSTANCE = init(); /** * Construct a new instance. */ @Inject protected ConcourseRuntime() { // NOTE: Routing to the correct Concourse Server instance is handled via // communication channels stored in each thread that accesses this // instance. } /** * An internal annotation used to denote that a method call should not be * proxied via the {@link ServerInvoker}. */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface NoGuice {} /** * An object that intercepts method invocations and proxies them to the * {@link ConcourseRuntime#invokeServer(String, Object...)} method. * * @author Jeff Nelson */ static class ServerInvoker implements MethodInterceptor { @Override public Object invoke(MethodInvocation invocation) throws Throwable { return invokeServer(invocation.getMethod().getName(), invocation.getArguments()); } } /** * A module that binds all methods not annotated with {@link NoGuice} to use * the {@link ServerInvoker} intercepter. * * @author Jeff Nelson */ static class ServerInvokerModule extends AbstractModule { @Override protected void configure() { bindInterceptor(Matchers.subclassesOf(ConcourseRuntime.class), Matchers.not(Matchers.annotatedWith(NoGuice.class)), new ServerInvoker()); } } }