Package com.google.appengine.tools.development.agent.runtime

Source Code of com.google.appengine.tools.development.agent.runtime.RuntimeHelper

// Copyright 2012 Google Inc. All Rights Reserved.
package com.google.appengine.tools.development.agent.runtime;

import com.google.apphosting.api.AppEngineInternal;

import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;

/**
* The only reason this class is split out from {@link Runtime} is so that we
* can have tests that don't need to worry about Runtime's crazy static
* initialization business.
*
*/
class RuntimeHelper {
  private static final Logger logger = Logger.getLogger(RuntimeHelper.class.getName());

  static final Map<String, AppEngineInternal> internalAnnotationCache =
      Collections.synchronizedMap(new HashMap<String, AppEngineInternal>());

  private static final boolean appEngineInternalOnClasspath;

  static {
    boolean exists = false;
    try {
      AppEngineInternal.class.getName();
      exists = true;
    } catch (NoClassDefFoundError ncdf) {
    }
    appEngineInternalOnClasspath = exists;
  }

  private RuntimeHelper() { }

  /**
   * Check to see if the class identified by {@code classStr} is a restricted
   * class. If it is, either output a warning or throw
   * {@link NoClassDefFoundError}, depending on the value of
   * {@code violationIsError}.
   *
   * @param violationIsError If {@code true}, throw
   * {@link NoClassDefFoundError} if the class is restricted, otherwise log a
   * warning.
   * @param classStr The class to check.
   * @param callingClassStr The class that is referencing {@code classStr}.
   * @param callingClassCodeSource The codesource of the class that is
   * referencing {@code classStr}.
   */
  public static void checkRestricted(boolean violationIsError, String classStr,
      String callingClassStr, String callingClassCodeSource) {
    if (!appEngineInternalOnClasspath) {
      return;
    }
    if (classStr.startsWith("java.") || classStr.startsWith("javax.")) {
      return;
    }
    try {
      Class<?> cls = Class.forName(classStr);
      AppEngineInternal anno = getAppEngineInternalAnnotation(cls);
      if (anno != null) {
        String errorMsg = String.format("Class %s loaded from %s has a dependency on class %s "
            + "loaded from %s, which is not part of App Engine's supported API.", callingClassStr,
            callingClassCodeSource, cls.getName(), cls.getProtectionDomain().getCodeSource());
        if (!anno.message().isEmpty()) {
          errorMsg += "\n" + anno.message();
        }
        if (violationIsError) {
          throw new NoClassDefFoundError(errorMsg);
        } else {
          logger.warning(errorMsg + "\nYou are strongly discouraged "
              + "from using this class - your app may stop working in production at any moment.");
        }
      }
    } catch (ClassNotFoundException e) {
    }
  }

  /**
   * @param cls The for which we are trying to locate the annotation.
   * @return The annotation. Can be null.
   */
  static AppEngineInternal getAppEngineInternalAnnotation(Class<?> cls) {
    List<String> namesExamined = new LinkedList<String>();
    String name = cls.getName();
    boolean firstPass = true;
    while (name != null) {
      if (internalAnnotationCache.containsKey(name)) {
        AppEngineInternal anno = internalAnnotationCache.get(name);
        updateInternalAnnotationCache(namesExamined, anno);
        return anno;
      }
      try {
        namesExamined.add(name);
        if (!firstPass) {
          cls = Class.forName(name + ".package-info");
        } else {
        }
        AppEngineInternal anno = cls.getAnnotation(AppEngineInternal.class);
        if (anno != null) {
          updateInternalAnnotationCache(namesExamined, anno);
          return anno;
        }
      } catch (ClassNotFoundException cnfe) {
      }
      name = getOwningPackage(name);
      firstPass = false;
    }
    updateInternalAnnotationCache(namesExamined, null);
    return null;
  }

  /**
   * Update the internal annotation cache. Since the cache represents a tree
   * hierarchy, we want to update the entire branch we just walked. Consider
   * the case of starting at package a.b.c.d and finding the annotation at
   * a.b. We want to cache the annotation at a.b, a.b.c and a.b.c.d. This
   * way if our next lookup just starts at a.b.c we will find it immediately.
   *
   * @param namesToUpdate The list of names to update with the annotation.
   * @param anno The annotation that we found. May be null.
   */
  static void updateInternalAnnotationCache(List<String> namesToUpdate,
      AppEngineInternal anno) {
    for (String name : namesToUpdate) {
      internalAnnotationCache.put(name, anno);
    }
  }

  /**
   * Get the name of the package that owns the provided resource.
   * The resource can be another package or a class name. Returns {@code null}
   * if there is no owning package.
   */
  static String getOwningPackage(String resource) {
    int lastDot = resource.lastIndexOf('.');
    if (lastDot == -1) {
      return null;
    }
    return resource.substring(0, lastDot);
  }
}
TOP

Related Classes of com.google.appengine.tools.development.agent.runtime.RuntimeHelper

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.