001package io.ebean.config;
002
003
004import java.io.IOException;
005import java.net.URL;
006import java.util.Enumeration;
007
008/**
009 * Helper to find classes taking into account the context class loader.
010 */
011public class ClassLoadConfig {
012
013  protected final ClassLoaderContext context;
014
015  /**
016   * Construct with the default classLoader search with context classLoader first.
017   */
018  public ClassLoadConfig() {
019    this(null);
020  }
021
022  /**
023   * Specify the classLoader to use for class detection and new instance creation.
024   */
025  public ClassLoadConfig(ClassLoader classLoader) {
026    this.context = new ClassLoaderContext(classLoader);
027  }
028
029  /**
030   * Return true if the Joda types are available and should be supported.
031   */
032  public boolean isJodaTimePresent() {
033    return isPresent("org.joda.time.LocalDateTime");
034  }
035
036  /**
037   * Return true if javax validation annotations like Size and NotNull are present.
038   */
039  public boolean isJavaxValidationAnnotationsPresent() {
040    return isPresent("javax.validation.constraints.NotNull");
041  }
042
043  /**
044   * Return true if javax PostConstruct annotation is present (maybe not in java9).
045   * If not we don't support PostConstruct lifecycle events.
046   */
047  public boolean isJavaxPostConstructPresent() {
048    return isPresent("javax.annotation.PostConstruct");
049  }
050
051  /**
052   * Return true if Jackson annotations like JsonIgnore are present.
053   */
054  public boolean isJacksonAnnotationsPresent() {
055    return isPresent("com.fasterxml.jackson.annotation.JsonIgnore");
056  }
057
058  public boolean isJacksonCorePresent() {
059    return isPresent("com.fasterxml.jackson.core.JsonParser");
060  }
061
062  /**
063   * Return true if Jackson ObjectMapper is present.
064   */
065  public boolean isJacksonObjectMapperPresent() {
066    return isPresent("com.fasterxml.jackson.databind.ObjectMapper");
067  }
068
069  /**
070   * Return a new instance of the class using the default constructor.
071   */
072  public Object newInstance(String className) {
073
074    try {
075      Class<?> cls = forName(className);
076      return cls.newInstance();
077    } catch (Exception e) {
078      throw new IllegalArgumentException("Error constructing " + className, e);
079    }
080  }
081
082  /**
083   * Return the resources for the given name.
084   */
085  public Enumeration<URL> getResources(String name) throws IOException {
086    return context.getResources(name);
087  }
088
089  /**
090   * Return true if the given class is present.
091   */
092  public boolean isPresent(String className) {
093    try {
094      forName(className);
095      return true;
096    } catch (Throwable ex) {
097      // Class or one of its dependencies is not present...
098      return false;
099    }
100  }
101
102  /**
103   * Load a class taking into account a context class loader (if present).
104   */
105  protected Class<?> forName(String name) throws ClassNotFoundException {
106    return context.forName(name);
107  }
108
109  /**
110   * Return the classLoader to use for service loading etc.
111   */
112  public ClassLoader getClassLoader() {
113    return context.getClassLoader();
114  }
115
116  /**
117   * Wraps the preferred, caller and context class loaders.
118   */
119  protected static class ClassLoaderContext {
120
121    /**
122     * Optional - if set only use this classLoader (no fallback).
123     */
124    protected final ClassLoader preferredLoader;
125
126    protected final ClassLoader contextLoader;
127
128    protected final ClassLoader callerLoader;
129
130    ClassLoaderContext(ClassLoader preferredLoader) {
131      this.preferredLoader = preferredLoader;
132      this.callerLoader = DatabaseConfig.class.getClassLoader();
133      this.contextLoader = contextLoader();
134    }
135
136    ClassLoader contextLoader() {
137      ClassLoader loader = Thread.currentThread().getContextClassLoader();
138      return (loader != null) ? loader : callerLoader;
139    }
140
141    Enumeration<URL> getResources(String name) throws IOException {
142      if (preferredLoader != null) {
143        return preferredLoader.getResources(name);
144      }
145      return contextLoader().getResources(name);
146    }
147
148    Class<?> forName(String name) throws ClassNotFoundException {
149
150      if (preferredLoader != null) {
151        // only use the explicitly set classLoader
152        return classForName(name, preferredLoader);
153      }
154      try {
155        // try the context loader first
156        return classForName(name, contextLoader);
157      } catch (ClassNotFoundException e) {
158        if (callerLoader == contextLoader) {
159          throw e;
160        } else {
161          // fallback to the caller classLoader
162          return classForName(name, callerLoader);
163        }
164      }
165    }
166
167    Class<?> classForName(String name, ClassLoader classLoader) throws ClassNotFoundException {
168      return Class.forName(name, true, classLoader);
169    }
170
171    ClassLoader getClassLoader() {
172      return preferredLoader != null ? preferredLoader : contextLoader;
173    }
174  }
175}
176