001package com.avaje.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 Jackson annotations like JsonIgnore are present.
059   */
060  public boolean isJacksonAnnotationsPresent() {
061    return isPresent("com.fasterxml.jackson.annotation.JsonIgnore");
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
076    try {
077      Class<?> cls = forName(className);
078      return cls.newInstance();
079    } catch (Exception e) {
080      throw new IllegalArgumentException("Error constructing " + className, e);
081    }
082  }
083
084  /**
085   * Return the resources for the given name.
086   */
087  public Enumeration<URL> getResources(String name) throws IOException {
088    return context.getResources(name);
089  }
090
091  /**
092   * Return true if the given class is present.
093   */
094  protected boolean isPresent(String className) {
095    try {
096      forName(className);
097      return true;
098    } catch (Throwable ex) {
099      // Class or one of its dependencies is not present...
100      return false;
101    }
102  }
103
104  /**
105   * Load a class taking into account a context class loader (if present).
106   */
107  protected Class<?> forName(String name) throws ClassNotFoundException {
108    return context.forName(name);
109  }
110
111  /**
112   * Return the classLoader to use for service loading etc.
113   */
114  public ClassLoader getClassLoader() {
115    return context.getClassLoader();
116  }
117
118  /**
119   * Wraps the preferred, caller and context class loaders.
120   */
121  protected class ClassLoaderContext {
122
123    /**
124     * Optional - if set only use this classLoader (no fallback).
125     */
126    protected final ClassLoader preferredLoader;
127
128    protected final ClassLoader contextLoader;
129
130    protected final ClassLoader callerLoader;
131
132    ClassLoaderContext(ClassLoader preferredLoader) {
133      this.preferredLoader = preferredLoader;
134      this.callerLoader = ServerConfig.class.getClassLoader();
135      this.contextLoader = contextLoader();
136    }
137
138    ClassLoader contextLoader() {
139      ClassLoader loader = Thread.currentThread().getContextClassLoader();
140      return (loader != null) ? loader: callerLoader;
141    }
142
143    Enumeration<URL> getResources(String name) throws IOException {
144      if (preferredLoader != null) {
145        return preferredLoader.getResources(name);
146      }
147      return contextLoader().getResources(name);
148    }
149
150    Class<?> forName(String name) throws ClassNotFoundException {
151
152      if (preferredLoader != null) {
153        // only use the explicitly set classLoader
154        return classForName(name, preferredLoader);
155      }
156      try {
157        // try the context loader first
158        return classForName(name, contextLoader);
159      } catch (ClassNotFoundException e) {
160        if (callerLoader == contextLoader) {
161          throw e;
162        } else {
163          // fallback to the caller classLoader
164          return classForName(name, callerLoader);
165        }
166      }
167    }
168
169    Class<?> classForName(String name, ClassLoader classLoader) throws ClassNotFoundException {
170      return Class.forName(name, true, classLoader);
171    }
172
173    ClassLoader getClassLoader() {
174      return preferredLoader != null ? preferredLoader : contextLoader;
175    }
176  }
177}
178