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