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