Package com.google.sitebricks

Source Code of com.google.sitebricks.ScanAndCompileBootstrapper

package com.google.sitebricks;

import com.google.common.collect.HashBiMap;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.google.inject.Binding;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.Stage;
import com.google.sitebricks.compiler.Compilers;
import com.google.sitebricks.compiler.TemplateCompileException;
import com.google.sitebricks.headless.Service;
import com.google.sitebricks.rendering.Decorated;
import com.google.sitebricks.rendering.EmbedAs;
import com.google.sitebricks.rendering.Templates;
import com.google.sitebricks.rendering.With;
import com.google.sitebricks.rendering.control.WidgetRegistry;
import com.google.sitebricks.rendering.resource.ResourcesService;
import com.google.sitebricks.routing.PageBook;
import com.google.sitebricks.routing.PageBook.Page;
import com.google.sitebricks.routing.SystemMetrics;

import java.lang.annotation.Annotation;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;

import static com.google.inject.matcher.Matchers.annotatedWith;
import static com.google.sitebricks.SitebricksModule.BindingKind.ACTION;
import static com.google.sitebricks.SitebricksModule.BindingKind.EMBEDDED;
import static com.google.sitebricks.SitebricksModule.BindingKind.PAGE;
import static com.google.sitebricks.SitebricksModule.BindingKind.SERVICE;
import static com.google.sitebricks.SitebricksModule.BindingKind.STATIC_RESOURCE;

/**
* @author Dhanji R. Prasanna (dhanji@gmail.com)
*/
class ScanAndCompileBootstrapper implements Bootstrapper {
  private final PageBook pageBook;
  private final List<Package> packages;
  private final ResourcesService resourcesService;
  private final WidgetRegistry registry;
  private final SystemMetrics metrics;
  private final Compilers compilers;

  @Inject
  private final Templates templates = null;
 
  @Inject @Bricks
  private final List<SitebricksModule.LinkingBinder> bindings = null;

  @Inject @Bricks
  private final Map<String, Class<? extends Annotation>> methodMap = null;

  @Inject
  private final Stage currentStage = null;

  @Inject
  private final Injector injector = null;

  private final Logger log = Logger.getLogger(ScanAndCompileBootstrapper.class.getName());

  @Inject
  public ScanAndCompileBootstrapper(PageBook pageBook,
                                    @Bricks List<Package> packages,
                                    ResourcesService resourcesService,
                                    WidgetRegistry registry,
                                    SystemMetrics metrics,
                                    Compilers compilers) {

    this.pageBook = pageBook;
    this.packages = packages;
    this.resourcesService = resourcesService;
    this.registry = registry;

    this.metrics = metrics;
    this.compilers = compilers;
  }

  public void start() {
    Set<Class<?>> set = Sets.newHashSet();
    for (Package pkg : packages) {

      //look for any classes annotated with @At, @EmbedAs and @With
      set.addAll(Classes.matching(
          annotatedWith(At.class).or(
          annotatedWith(EmbedAs.class)).or(
          annotatedWith(With.class)).or(
          annotatedWith(Show.class))
      ).in(pkg));
    }

    //we need to scan all the pages first (do not collapse into the next loop)
    Set<PageBook.Page> pagesToCompile = scanPagesToCompile(set);
    collectBindings(bindings, pagesToCompile);
    extendedPages(pagesToCompile);

    // Compile templates for scanned classes (except in dev mode, where faster startup
    // time is more important and compiles are amortized across visits to each page).
    // TODO make this configurable separately to stage for GAE
    if (Stage.DEVELOPMENT != currentStage) {
      compilePages(pagesToCompile);
    }

    // Start all services.
    List<Binding<Aware>> bindings = injector.findBindingsByType(AWARE_TYPE);
    for (Binding<Aware> binding : bindings) {
      binding.getProvider().get().startup();
    }

    //set application mode to started (now debug mechanics can kick in)
    metrics.activate();
  }

  private void extendedPages(Set<Page> pagesToCompile) {
    Set<Page> pageExtensions = Sets.newHashSet();
    for (Page page : pagesToCompile) {
      if (page.pageClass().isAnnotationPresent(Decorated.class)) {
        // recursively add extension pages
        analyseExtension(pageExtensions, page.pageClass());
      }
    }
    pagesToCompile.addAll(pageExtensions);
  }

  //processes all explicit bindings, including static resources.
  private void collectBindings(List<SitebricksModule.LinkingBinder> bindings,
                               Set<PageBook.Page> pagesToCompile) {

    // Reverse the method map for easy lookup of HTTP method annotations.
    Map<Class<? extends Annotation>, String> methodSet = null;

    //go thru bindings and obtain pages from them.
    for (SitebricksModule.LinkingBinder binding : bindings) {

      if (EMBEDDED == binding.bindingKind) {
        if (null == binding.embedAs) {
          // This can happen if embed() is not followed by an .as(..)
          throw new IllegalStateException("embed() missing .as() clause: " + binding.pageClass);
        }
        registry.addEmbed(binding.embedAs);
        pagesToCompile.add(pageBook.embedAs(binding.pageClass, binding.embedAs));

      } else if (PAGE == binding.bindingKind) {
        pagesToCompile.add(pageBook.at(binding.uri, binding.pageClass));

      } else if (STATIC_RESOURCE == binding.bindingKind) {
        //localize the resource to the SitebricksModule's package.
        resourcesService.add(SitebricksModule.class, binding.getResource());
      } else if (SERVICE == binding.bindingKind) {
        pagesToCompile.add(pageBook.serviceAt(binding.uri, binding.pageClass));
      } else if (ACTION == binding.bindingKind) {
        // Lazy create this inverse lookup map, once.
        if (null == methodSet) {
          methodSet = HashBiMap.create(methodMap).inverse();
        }
        pageBook.at(binding.uri, binding.actionDescriptors, methodSet);
      }
    }
  }

  //goes through the set of scanned classes and builds pages out of them.
  private Set<PageBook.Page> scanPagesToCompile(Set<Class<?>> set) {
    Set<Templates.Descriptor> templates = Sets.newHashSet();
    Set<PageBook.Page> pagesToCompile = Sets.newHashSet();
    for (Class<?> pageClass : set) {
      EmbedAs embedAs = pageClass.getAnnotation(EmbedAs.class);
      if (null != embedAs) {
        final String embedName = embedAs.value();
     
        //is this a text rendering or embedding-style widget?
        if (Renderable.class.isAssignableFrom(pageClass)) {
          @SuppressWarnings("unchecked")
          Class<? extends Renderable> renderable = (Class<? extends Renderable>) pageClass;
          registry.add(embedName, renderable);
        } else {
          pagesToCompile.add(embed(embedName, pageClass));
        }
      }
     
      At at = pageClass.getAnnotation(At.class);
      if (null != at) {
        if (pageClass.isAnnotationPresent(Service.class)) {
          pagesToCompile.add(pageBook.serviceAt(at.value(), pageClass));
        } else if (pageClass.isAnnotationPresent(Export.class)) {
          //localize the resource to the SitebricksModule's package.
          resourcesService.add(SitebricksModule.class, pageClass.getAnnotation(Export.class));
        }
        else {
          pagesToCompile.add(pageBook.at(at.value(), pageClass));
        }
      }

      if (pageClass.isAnnotationPresent(Show.class)) {
        // This has a template associated with it.
        templates.add(new Templates.Descriptor(pageClass,
            pageClass.getAnnotation(Show.class).value()));
      }
    }

    // Eagerly load all detected templates in production mode.
    if (Stage.DEVELOPMENT != currentStage) {
      this.templates.loadAll(templates);
    }

    return pagesToCompile;
  }

  private void analyseExtension(Set<PageBook.Page> pagesToCompile, final Class<?> extendClassArgument) {
    // store the page with a special page name used by ExtendWidget
    Class<?> extendClass = extendClassArgument;
    pagesToCompile.add(pageBook.decorate(extendClass));
   
    // recursively analyse super class
    while (extendClass != Object.class) {
      extendClass = extendClass.getSuperclass();
      if (extendClass.isAnnotationPresent(Decorated.class)) {
        analyseExtension(pagesToCompile, extendClass);
      }
      else if (extendClass.isAnnotationPresent(Show.class)) {
        // there is a @Show with no @Extension so this is the outer template
        return;
      }
    }
    throw new IllegalStateException("Could not find super class annotated with @Show on parent of class: " + extendClassArgument);
  }

  private void compilePages(Set<PageBook.Page> pagesToCompile) {
    final List<TemplateCompileException> failures = Lists.newArrayList();

    //perform a compilation pass over all the pages and their templates
    for (PageBook.Page page : pagesToCompile) {
      Class<?> pageClass = page.pageClass();

      // Headless web services need to be analyzed but not page-compiled.
      if (page.isHeadless()) {
        // TODO(dhanji): Feedback errors as return rather than throwing.
        compilers.analyze(pageClass);
        continue;
      }

      if (log.isLoggable(Level.FINEST)) {
        log.finest("Compiling template for page " + pageClass.getName());
      }

      try {
        compilers.compilePage(page);
        compilers.analyze(pageClass);
      } catch (TemplateCompileException e) {
        failures.add(e);
      }
    }

    //log failures if any (we don't abort the app startup)
    if (!failures.isEmpty()) {
      logFailures(failures);
    }
  }

  private PageBook.Page embed(String embedAs, Class<?> page) {
    //store custom page wrapped as an embed widget
    registry.addEmbed(embedAs);

    //store argument name(s) wrapped as an Argument (multiple aliases allowed)
    if (page.isAnnotationPresent(With.class)) {
      for (String callWith : page.getAnnotation(With.class).value()) {
        registry.addArgument(callWith);
      }
    }

    //...add as an unbound (to URI) page
    return pageBook.embedAs(page, embedAs);
  }

  private void logFailures(List<TemplateCompileException> failures) {
    StringBuilder builder = new StringBuilder();
    for (TemplateCompileException failure : failures) {
      builder.append(failure.getMessage());
      builder.append("\n\n");
    }

    log.severe(builder.toString());
  }
}
TOP

Related Classes of com.google.sitebricks.ScanAndCompileBootstrapper

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.