@Override
  public void describeTo(StringBuilder stringBuilder) {
    ClosureUtil.SourceInfo sourceInfo = ClosureUtil.getSourceInfo(invoker.getClosure());
    if (sourceInfo == null) {
      ClassPool pool = ClassPool.getDefault();
      try {
        CtClass ctClass = pool.get(invoker.getClosure().getClass().getName());
        CtMethod ctMethod = ctClass.getDeclaredMethod("doCall");
        int lineNumber = ctMethod.getMethodInfo().getLineNumber(0);

        ClassFile classFile = ctClass.getClassFile();
        String sourceFile = classFile.getSourceFile();

        if (lineNumber != -1 && sourceFile != null) {
          stringBuilder
              .append("closure at line ")
              .append(lineNumber)
              .append(" of ")
              .append(sourceFile);
        } else {
          stringBuilder.append("closure ").append(invoker.getClosure().getClass().getName());
        }
      } catch (NotFoundException e) {
        stringBuilder.append(invoker.getClosure().getClass().getName());
      }
    } else {
      stringBuilder
          .append("closure at line ")
          .append(sourceInfo.getLineNumber())
          .append(" of ")
          .append(sourceInfo.getUri());
    }
  }
예제 #2
0
 static GroovyEmbeddedApp of(
     @DelegatesTo(value = GroovyRatpackServerSpec.class, strategy = Closure.DELEGATE_FIRST)
         Closure<?> definition)
     throws Exception {
   return from(
       EmbeddedApp.of(
           s -> ClosureUtil.configureDelegateFirst(GroovyRatpackServerSpec.from(s), definition)));
 }
예제 #3
0
 static GroovyEmbeddedApp fromServer(
     ServerConfig serverConfig,
     @DelegatesTo(value = GroovyRatpackServerSpec.class, strategy = Closure.DELEGATE_FIRST)
         Closure<?> definition) {
   return from(
       EmbeddedApp.fromServer(
           serverConfig,
           s -> ClosureUtil.configureDelegateFirst(GroovyRatpackServerSpec.from(s), definition)));
 }
/**
 * A highly configurable {@link ratpack.test.embed.EmbeddedApplication} implementation that allows
 * the application to be defined in code at runtime.
 *
 * <p>This implementation is usually sufficient for testing Ratpack modules or extensions.
 *
 * <pre class="tested">
 * import ratpack.test.embed.PathBaseDirBuilder
 * import ratpack.groovy.test.TestHttpClients
 * import ratpack.session.SessionModule
 *
 * import java.nio.file.Files
 *
 * import static ratpack.groovy.test.embed.EmbeddedApplications.embeddedApp
 *
 * def tmp = Files.createTempDirectory("ratpack-test")
 * def baseDir = new PathBaseDirBuilder(tmp)
 *
 * // Create a file within the application base dir
 * baseDir.file "public/foo.txt", "bar"
 *
 * def app = embeddedApp(baseDir) {
 *   // Configure the launch config
 *   launchConfig {
 *     other "some.other.property": "value"
 *     development true
 *   }
 *
 *   // Configure the module registry
 *   bindings {
 *     add new SessionModule()
 *   }
 *
 *   // Use the GroovyChain DSL for defining the application handlers
 *   handlers {
 *     get {
 *       render "root"
 *     }
 *     assets "public"
 *   }
 * }
 *
 * // Test the application with the test http client
 * def client = TestHttpClients.testHttpClient(app)
 *
 * assert client.getText() == "root"
 * assert client.getText("foo.txt") == "bar"
 *
 * // Cleanup
 * app.close()
 * </pre>
 *
 * @see ratpack.test.embed.PathBaseDirBuilder
 * @see ratpack.groovy.test.embed.EmbeddedApplications
 */
public class ClosureBackedEmbeddedApplication extends LaunchConfigEmbeddedApplication {

  private Closure<?> handlersClosure = ClosureUtil.noop();
  private Closure<?> bindingsClosure = ClosureUtil.noop();
  private Closure<?> launchConfigClosure = ClosureUtil.noop();

  private final List<Module> modules = new LinkedList<>();

  private Factory<Path> baseDirFactory;

  /** Constructor. */
  public ClosureBackedEmbeddedApplication() {}

  /**
   * Constructor.
   *
   * <p>Useful for deferring the base dir determination
   *
   * @param baseDirFactory The factory for the base dir
   */
  public ClosureBackedEmbeddedApplication(Factory<Path> baseDirFactory) {
    this.baseDirFactory = baseDirFactory;
  }

  /**
   * Constructor.
   *
   * @param baseDirBuilder the builder whose {@link BaseDirBuilder#build()} method will be called to
   *     provide the base dir for this app
   */
  public ClosureBackedEmbeddedApplication(final BaseDirBuilder baseDirBuilder) {
    this(
        new Factory<Path>() {
          @Override
          public Path create() {
            return baseDirBuilder.build();
          }
        });
  }

  /**
   * A mutable list of modules to use in this application.
   *
   * @return A mutable list of modules to use in this application
   */
  public List<Module> getModules() {
    return modules;
  }

  /**
   * Constructs the launch config using the other {@code create*} methods.
   *
   * <p>A launch config will be created using {@link LaunchConfigBuilder#baseDir(java.io.File)},
   * with the path returned by the factory given at construction. The launch config will also
   * default to using a port value of {@code 0} (i.e. an ephemeral port).
   *
   * <p>Register modules and objects using the {@link #bindings(groovy.lang.Closure)} method.
   *
   * <p>Define the handlers using the {@link #handlers(groovy.lang.Closure)} method.
   *
   * <p>Prefer overriding specific {@code create*} methods instead of this one.
   *
   * @return The created launch config.
   */
  @Override
  protected LaunchConfig createLaunchConfig() {
    LaunchConfigBuilder launchConfigBuilder;
    if (this.baseDirFactory != null) {
      Path baseDirPath = baseDirFactory.create();

      launchConfigBuilder = LaunchConfigBuilder.baseDir(baseDirPath).port(0);
    } else {
      launchConfigBuilder = LaunchConfigBuilder.noBaseDir().port(0);
    }

    configureDelegateFirst(launchConfigBuilder, launchConfigClosure);

    final Action<? super BindingsSpec> bindingsAction = createBindingsAction();

    return launchConfigBuilder.build(
        new HandlerFactory() {
          public Handler create(LaunchConfig launchConfig) throws Exception {
            GuiceBackedHandlerFactory handlerFactory = createHandlerFactory(launchConfig);
            Function<? super Module, ? extends Injector> injectorFactory =
                createInjectorFactory(launchConfig);
            Function<? super Injector, ? extends Handler> handlerTransformer =
                createHandlerTransformer(launchConfig);

            return handlerFactory.create(bindingsAction, injectorFactory, handlerTransformer);
          }
        });
  }

  /**
   * Specifies the handlers of the application.
   *
   * <p>The given closure will not be executed until this application is started.
   *
   * <p>Subsequent calls to this method will <i>replace</i> the previous definition. Calling this
   * method after the application has started has no effect.
   *
   * @param closure The definition of the application handlers
   */
  public void handlers(
      @DelegatesTo(value = GroovyChain.class, strategy = Closure.DELEGATE_FIRST)
          Closure<?> closure) {
    this.handlersClosure = closure;
  }

  /**
   * Specifies the bindings of the application.
   *
   * <p>The given closure will not be executed until this application is started.
   *
   * <p>Subsequent calls to this method will <i>replace</i> the previous definition. Calling this
   * method after the application has started has no effect.
   *
   * @param closure The definition of the application handlers
   */
  public void bindings(
      @DelegatesTo(value = GroovyBindingsSpec.class, strategy = Closure.DELEGATE_FIRST)
          Closure<?> closure) {
    this.bindingsClosure = closure;
  }

  /**
   * Modifies the launch config of the application.
   *
   * <p>The given closure will not be executed until this application is started.
   *
   * <p>Subsequent calls to this method will <i>replace</i> the previous definition. Calling this
   * method after the application has started has no effect.
   *
   * @param closure The definition of the application handlers
   */
  public void launchConfig(
      @DelegatesTo(value = LaunchConfigBuilder.class, strategy = Closure.DELEGATE_FIRST)
          Closure<?> closure) {
    this.launchConfigClosure = closure;
  }

  /**
   * Creates a module to injector transformer based on the given launch config.
   *
   * <p>Delegates to {@link Guice#newInjectorFactory(ratpack.launch.LaunchConfig)}.
   *
   * @param launchConfig The launch config
   * @return the result of {@link Guice#newInjectorFactory(ratpack.launch.LaunchConfig)} with the
   *     given launch config
   */
  protected Function<? super Module, ? extends Injector> createInjectorFactory(
      LaunchConfig launchConfig) {
    return Guice.newInjectorFactory(launchConfig);
  }

  /**
   * Returns the factory to use to create the actual handler.
   *
   * <p>This implementation does not add any implicit behaviour. Subclasses may wish to provide
   * different implementations that do perform implicit behaviour (e.g. implied modules)
   *
   * @param launchConfig The launch config
   * @return The guice handler factory
   */
  protected GuiceBackedHandlerFactory createHandlerFactory(LaunchConfig launchConfig) {
    return new DefaultGuiceBackedHandlerFactory(launchConfig);
  }

  /**
   * Provides the module registry configuration action.
   *
   * <p>This implementation creates an action that registers {@link #getModules()} in order, then
   * executes the closure given with {@link #bindings(groovy.lang.Closure)}.
   *
   * @return The module registry configuration action
   */
  protected Action<? super BindingsSpec> createBindingsAction() {
    return new Action<BindingsSpec>() {
      @Override
      public void execute(BindingsSpec bindingsSpec) throws Exception {
        bindingsSpec.add(modules);
        configureDelegateFirst(new DefaultGroovyBindingsSpec(bindingsSpec), bindingsClosure);
      }
    };
  }

  /**
   * Provides the object that, given the {@link com.google.inject.Injector} created by the module
   * definition, creates the application handler.
   *
   * <p>This implementation uses the closure given to {@link #handlers(groovy.lang.Closure)} to
   * build a handler using the {@link Groovy#chain(ratpack.launch.LaunchConfig,
   * ratpack.registry.Registry, groovy.lang.Closure)} method. An registry based on the injector
   * backs the chain.
   *
   * @param launchConfig the launch config
   * @return A transformer that creates the application handler, given an injector
   */
  protected Function<? super Injector, ? extends Handler> createHandlerTransformer(
      final LaunchConfig launchConfig) {
    return new Function<Injector, Handler>() {
      @Override
      public Handler apply(Injector injector) throws Exception {
        return Groovy.chain(launchConfig, Guice.registry(injector), handlersClosure);
      }
    };
  }
}
예제 #5
0
 default void test(
     @DelegatesTo(value = TestHttpClient.class, strategy = Closure.DELEGATE_FIRST) Closure<?> test)
     throws Exception {
   test(ClosureUtil.delegatingAction(TestHttpClient.class, test));
 }