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 jakarta validation annotations like Size and NotNull are present.
045   */
046  public boolean isJakartaValidationAnnotationsPresent() {
047    return isPresent("jakarta.validation.constraints.NotNull");
048  }
049
050  /**
051   * Return true if javax PostConstruct annotation is present (maybe not in java9).
052   * If not we don't support PostConstruct lifecycle events.
053   */
054  public boolean isJavaxPostConstructPresent() {
055    return isPresent("javax.annotation.PostConstruct");
056  }
057
058  /**
059   * Return true if Jackson annotations like JsonIgnore are present.
060   */
061  public boolean isJacksonAnnotationsPresent() {
062    return isPresent("com.fasterxml.jackson.annotation.JsonIgnore");
063  }
064
065  public boolean isJacksonCorePresent() {
066    return isPresent("com.fasterxml.jackson.core.JsonParser");
067  }
068
069  /**
070   * Return true if Jackson ObjectMapper is present.
071   */
072  public boolean isJacksonObjectMapperPresent() {
073    return isPresent("com.fasterxml.jackson.databind.ObjectMapper");
074  }
075
076  /**
077   * Return a new instance of the class using the default constructor.
078   */
079  public Object newInstance(String className) {
080
081    try {
082      Class<?> cls = forName(className);
083      return cls.newInstance();
084    } catch (Exception e) {
085      throw new IllegalArgumentException("Error constructing " + className, e);
086    }
087  }
088
089  /**
090   * Return the resources for the given name.
091   */
092  public Enumeration<URL> getResources(String name) throws IOException {
093    return context.getResources(name);
094  }
095
096  /**
097   * Return true if the given class is present.
098   */
099  public boolean isPresent(String className) {
100    try {
101      forName(className);
102      return true;
103    } catch (Throwable ex) {
104      // Class or one of its dependencies is not present...
105      return false;
106    }
107  }
108
109  /**
110   * Load a class taking into account a context class loader (if present).
111   */
112  protected Class<?> forName(String name) throws ClassNotFoundException {
113    return context.forName(name);
114  }
115
116  /**
117   * Return the classLoader to use for service loading etc.
118   */
119  public ClassLoader getClassLoader() {
120    return context.getClassLoader();
121  }
122
123  /**
124   * Wraps the preferred, caller and context class loaders.
125   */
126  protected static class ClassLoaderContext {
127
128    /**
129     * Optional - if set only use this classLoader (no fallback).
130     */
131    protected final ClassLoader preferredLoader;
132
133    protected final ClassLoader contextLoader;
134
135    protected final ClassLoader callerLoader;
136
137    ClassLoaderContext(ClassLoader preferredLoader) {
138      this.preferredLoader = preferredLoader;
139      this.callerLoader = DatabaseConfig.class.getClassLoader();
140      this.contextLoader = contextLoader();
141    }
142
143    ClassLoader contextLoader() {
144      ClassLoader loader = Thread.currentThread().getContextClassLoader();
145      return (loader != null) ? loader : callerLoader;
146    }
147
148    Enumeration<URL> getResources(String name) throws IOException {
149      if (preferredLoader != null) {
150        return preferredLoader.getResources(name);
151      }
152      return contextLoader().getResources(name);
153    }
154
155    Class<?> forName(String name) throws ClassNotFoundException {
156
157      if (preferredLoader != null) {
158        // only use the explicitly set classLoader
159        return classForName(name, preferredLoader);
160      }
161      try {
162        // try the context loader first
163        return classForName(name, contextLoader);
164      } catch (ClassNotFoundException e) {
165        if (callerLoader == contextLoader) {
166          throw e;
167        } else {
168          // fallback to the caller classLoader
169          return classForName(name, callerLoader);
170        }
171      }
172    }
173
174    Class<?> classForName(String name, ClassLoader classLoader) throws ClassNotFoundException {
175      return Class.forName(name, true, classLoader);
176    }
177
178    ClassLoader getClassLoader() {
179      return preferredLoader != null ? preferredLoader : contextLoader;
180    }
181  }
182}
183