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