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