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