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