text: /*
   * Licensed to the Apache Software Foundation (ASF) under one
   * or more contributor license agreements.  See the NOTICE file
   * distributed with this work for additional information
   * regarding copyright ownership.  The ASF licenses this file
   * to you 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 org.apache.tinkerpop.gremlin.structure;
  
  import org.apache.commons.configuration2.Configuration;
  import org.apache.commons.configuration2.builder.fluent.Configurations;
  import org.apache.tinkerpop.gremlin.GraphProvider;
  import org.apache.tinkerpop.gremlin.process.computer.GraphComputer;
  import org.apache.tinkerpop.gremlin.process.remote.RemoteConnection;
  import org.apache.tinkerpop.gremlin.process.traversal.AnonymousTraversalSource;
  import org.apache.tinkerpop.gremlin.process.traversal.TraversalSource;
  import org.apache.tinkerpop.gremlin.structure.util.GraphFactory;
  import org.apache.tinkerpop.gremlin.structure.util.StringFactory;
  
  import java.lang.reflect.Constructor;
  import java.util.Collections;
  import java.util.Iterator;
  
  /**
   * {@code RemoteGraph} is only required for integrating with the test suite as there must be a {@link Graph} instance
   * for the test suite to bind to. Test suites that use this must ensure that the {@link TraversalSource} be
   * generated from their {@link GraphProvider} in via {@link AnonymousTraversalSource#withRemote(RemoteConnection)} or
   * similar overload. See {@code RemoteGraphProvider} in the gremlin-server module for an example.
   *
   * @author Stephen Mallette (http://stephen.genoprime.com)
   */
  @Graph.OptIn(Graph.OptIn.SUITE_PROCESS_STANDARD)
  @Graph.OptIn(Graph.OptIn.SUITE_PROCESS_COMPUTER)
  @Graph.OptOut(
          test = "org.apache.tinkerpop.gremlin.process.traversal.step.map.ShortestPathTest",
          method = "*",
          reason = "https://issues.apache.org/jira/browse/TINKERPOP-1976")
  @Graph.OptOut(
          test = "org.apache.tinkerpop.gremlin.process.traversal.step.map.ConnectedComponentTest",
          method = "*",
          reason = "https://issues.apache.org/jira/browse/TINKERPOP-1976")
  @Graph.OptOut(
          test = "org.apache.tinkerpop.gremlin.process.traversal.CoreTraversalTest",
          method = "*",
          reason = "The test suite does not support profiling or lambdas and for groovy tests: 'Could not locate method: GraphTraversalSource.withStrategies([{traversalCategory=interface org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategy$DecorationStrategy}])'")
  @Graph.OptOut(
          test = "org.apache.tinkerpop.gremlin.process.traversal.step.map.ProgramTest",
          method = "*",
          reason = "RemoteGraph retrieves detached vertices that can't be attached to a remote OLAP graph")
  @Graph.OptOut(
          test = "org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.ElementIdStrategyProcessTest",
          method = "*",
          reason = "RemoteGraph does not support ElementIdStrategy at this time - it requires a lambda in construction which is not serializable")
  @Graph.OptOut(
          test = "org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.EventStrategyProcessTest",
          method = "*",
          reason = "RemoteGraph does not support EventStrategy at this time - some of its members are not serializable")
  @Graph.OptOut(
          test = "org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.PartitionStrategyProcessTest",
          method = "*",
          reason = "RemoteGraph does not support PartitionStrategy at this time")
  @Graph.OptOut(
          test = "org.apache.tinkerpop.gremlin.process.computer.ranking.pagerank.PageRankVertexProgramTest",
          method = "*",
          reason = "RemoteGraph does not support direct Graph.compute() access")
  @Graph.OptOut(
          test = "org.apache.tinkerpop.gremlin.process.computer.search.path.ShortestPathVertexProgramTest",
          method = "*",
          reason = "RemoteGraph does not support direct Graph.compute() access")
  @Graph.OptOut(
          test = "org.apache.tinkerpop.gremlin.process.computer.clone.CloneVertexProgramTest",
          method = "*",
          reason = "RemoteGraph does not support direct Graph.compute() access")
  @Graph.OptOut(
          test = "org.apache.tinkerpop.gremlin.process.computer.GraphComputerTest",
          method = "*",
          reason = "RemoteGraph does not support direct Graph.compute() access")
  @Graph.OptOut(
          test = "org.apache.tinkerpop.gremlin.process.traversal.TraversalInterruptionTest",
          method = "*",
          reason = "The interruption model in the test can't guarantee interruption at the right time with RemoteGraph.")
  @Graph.OptOut(
          test = "org.apache.tinkerpop.gremlin.process.traversal.TraversalInterruptionComputerTest",
          method = "*",
          reason = "The interruption model in the test can't guarantee interruption at the right time with RemoteGraph.")
  @Graph.OptOut(
          test = "org.apache.tinkerpop.gremlin.process.traversal.step.ComplexTest",
          method = "classicRecommendation",
          reason = "Test asserts traversal side-effects which are not supported by remote traversals.")
  @Graph.OptOut(
          test = "org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.GroupTest",
          method = "g_V_group_byXlabelX_byXbothE_groupXaX_byXlabelX_byXweight_sumX_weight_sumX",
          reason = "Test asserts traversal side-effects which are not supported by remote traversals.")
  public class RemoteGraph implements Graph {
  
      private final RemoteConnection connection;
      private final Configuration conf;
  
      private RemoteGraph(final RemoteConnection connection, final Configuration conf) {
          this.connection = connection;
          this.conf = conf;
      }
  
      /**
       * Creates a new {@link RemoteGraph} instance using the specified configuration, which allows {@link RemoteGraph}
       * to be compliant with {@link GraphFactory}. Expects key for {@link RemoteConnection#GREMLIN_REMOTE_CONNECTION_CLASS}
       * as well as any configuration required by the underlying {@link RemoteConnection} which will be instantiated.
       * Note that the {@code Configuration} object is passed down without change to the creation of the
       * {@link RemoteConnection} instance.
       */
      public static RemoteGraph open(final Configuration conf) {
          if (!conf.containsKey(RemoteConnection.GREMLIN_REMOTE_CONNECTION_CLASS))
              throw new IllegalArgumentException("Configuration must contain the '" + RemoteConnection.GREMLIN_REMOTE_CONNECTION_CLASS + "' key");
  
          final RemoteConnection remoteConnection;
          try {
              final Class<? extends RemoteConnection> clazz = Class.forName(conf.getString(RemoteConnection.GREMLIN_REMOTE_CONNECTION_CLASS)).asSubclass(RemoteConnection.class);
              final Constructor<? extends RemoteConnection> ctor = clazz.getConstructor(Configuration.class);
              remoteConnection = ctor.newInstance(conf);
          } catch (Exception ex) {
              throw new IllegalStateException(ex);
          }
  
          return new RemoteGraph(remoteConnection, conf);
      }
  
      public static RemoteGraph open(final String configFile) throws Exception {
          final Configurations configs = new Configurations();
          return open(configs.properties((configFile)));
      }
  
      /**
       * Creates a new {@link RemoteGraph} instance. {@link RemoteGraph} will attempt to call the
       * {@link RemoteConnection#close()} method when the {@link #close()} method is called on this class.
       *
       * @param connection the {@link RemoteConnection} instance to use
       *                   {@link RemoteConnection}
       */
      public static RemoteGraph open(final RemoteConnection connection, final Configuration conf) {
          return new RemoteGraph(connection, conf);
      }
  
      public RemoteConnection getConnection() {
          return connection;
      }
  
      /**
       * Closes the underlying {@link RemoteConnection}.
       */
      @Override
      public void close() throws Exception {
          connection.close();
      }
  
      @Override
      public Vertex addVertex(final Object... keyValues) {
          throw new UnsupportedOperationException(String.format("RemoteGraph is a proxy to %s - this method is not supported", connection));
      }
  
      @Override
      public <C extends GraphComputer> C compute(final Class<C> graphComputerClass) throws IllegalArgumentException {
          throw new UnsupportedOperationException(String.format("RemoteGraph is a proxy to %s - this method is not supported", connection));
      }
  
      @Override
      public GraphComputer compute() throws IllegalArgumentException {
          throw new UnsupportedOperationException(String.format("RemoteGraph is a proxy to %s - this method is not supported", connection));
      }
  
      /**
       * This method returns an empty {@link Iterator} - it is not meant to be called directly.
       */
      @Override
      public Iterator<Vertex> vertices(final Object... vertexIds) {
          return Collections.emptyIterator();
      }
  
      /**
       * This method returns an empty {@link Iterator} - it is not meant to be called directly.
       */
      @Override
      public Iterator<Edge> edges(final Object... edgeIds) {
          return Collections.emptyIterator();
      }
  
      @Override
      public Transaction tx() {
          throw new UnsupportedOperationException(String.format("RemoteGraph is a proxy to %s - this method is not supported", connection));
      }
  
      @Override
      public Variables variables() {
          throw new UnsupportedOperationException(String.format("RemoteGraph is a proxy to %s - this method is not supported", connection));
      }
  
      @Override
      public Configuration configuration() {
          return conf;
      }
  
      @Override
      public Features features() {
          return RemoteFeatures.INSTANCE;
      }
  
      @Override
      public String toString() {
          return StringFactory.graphString(this, connection.toString());
      }
  
      public static class RemoteFeatures implements Features {
          static RemoteFeatures INSTANCE = new RemoteFeatures();
  
          private RemoteFeatures() {
          }
  
          @Override
          public GraphFeatures graph() {
              return RemoteGraphFeatures.INSTANCE;
          }
      }
  
      public static class RemoteGraphFeatures implements Features.GraphFeatures {
  
          static RemoteGraphFeatures INSTANCE = new RemoteGraphFeatures();
  
          private RemoteGraphFeatures() {
          }
  
          @Override
          public boolean supportsTransactions() {
              return false;
          }
  
          @Override
          public boolean supportsThreadedTransactions() {
              return false;
          }
      }
  }
  