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