001package io.ebean.config; 002 003/** 004 * Helper to find classes taking into account the context class loader. 005 */ 006public class ClassLoadConfig { 007 008 protected final ClassLoaderContext context; 009 010 /** 011 * Construct with the default classLoader search with context classLoader first. 012 */ 013 public ClassLoadConfig() { 014 this(null); 015 } 016 017 /** 018 * Specify the classLoader to use for class detection and new instance creation. 019 */ 020 public ClassLoadConfig(ClassLoader classLoader) { 021 this.context = new ClassLoaderContext(classLoader); 022 } 023 024 /** 025 * Return true if the Joda types are available and should be supported. 026 */ 027 public boolean isJodaTimePresent() { 028 return isPresent("org.joda.time.LocalDateTime"); 029 } 030 031 /** 032 * Return true if javax validation annotations like Size and NotNull are present. 033 */ 034 public boolean isJavaxValidationAnnotationsPresent() { 035 return isPresent("javax.validation.constraints.NotNull"); 036 } 037 038 /** 039 * Return true if jakarta validation annotations like Size and NotNull are present. 040 */ 041 public boolean isJakartaValidationAnnotationsPresent() { 042 return isPresent("jakarta.validation.constraints.NotNull"); 043 } 044 045 /** 046 * Return true if javax PostConstruct annotation is present (maybe not in java9). 047 * If not we don't support PostConstruct lifecycle events. 048 */ 049 public boolean isJavaxPostConstructPresent() { 050 return isPresent("javax.annotation.PostConstruct"); 051 } 052 053 /** 054 * Return true if Jackson annotations like JsonIgnore are present. 055 */ 056 public boolean isJacksonAnnotationsPresent() { 057 return isPresent("com.fasterxml.jackson.annotation.JsonIgnore"); 058 } 059 060 public boolean isJacksonCorePresent() { 061 return isPresent("com.fasterxml.jackson.core.JsonParser"); 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 try { 076 Class<?> cls = forName(className); 077 return cls.getDeclaredConstructor().newInstance(); 078 } catch (Exception e) { 079 throw new IllegalArgumentException("Error constructing " + className, e); 080 } 081 } 082 083 /** 084 * Return true if the given class is present. 085 */ 086 public boolean isPresent(String className) { 087 try { 088 forName(className); 089 return true; 090 } catch (Throwable ex) { 091 // Class or one of its dependencies is not present... 092 return false; 093 } 094 } 095 096 /** 097 * Load a class taking into account a context class loader (if present). 098 */ 099 protected Class<?> forName(String name) throws ClassNotFoundException { 100 return context.forName(name); 101 } 102 103 /** 104 * Return the classLoader to use for service loading etc. 105 */ 106 public ClassLoader getClassLoader() { 107 return context.getClassLoader(); 108 } 109 110 /** 111 * Wraps the preferred, caller and context class loaders. 112 */ 113 protected static class ClassLoaderContext { 114 115 /** 116 * Optional - if set only use this classLoader (no fallback). 117 */ 118 protected final ClassLoader preferredLoader; 119 protected final ClassLoader contextLoader; 120 protected final ClassLoader callerLoader; 121 122 ClassLoaderContext(ClassLoader preferredLoader) { 123 this.preferredLoader = preferredLoader; 124 this.callerLoader = DatabaseConfig.class.getClassLoader(); 125 this.contextLoader = contextLoader(); 126 } 127 128 ClassLoader contextLoader() { 129 ClassLoader loader = Thread.currentThread().getContextClassLoader(); 130 return (loader != null) ? loader : callerLoader; 131 } 132 133 Class<?> forName(String name) throws ClassNotFoundException { 134 if (preferredLoader != null) { 135 // only use the explicitly set classLoader 136 return classForName(name, preferredLoader); 137 } 138 try { 139 // try the context loader first 140 return classForName(name, contextLoader); 141 } catch (ClassNotFoundException e) { 142 if (callerLoader == contextLoader) { 143 throw e; 144 } else { 145 // fallback to the caller classLoader 146 return classForName(name, callerLoader); 147 } 148 } 149 } 150 151 Class<?> classForName(String name, ClassLoader classLoader) throws ClassNotFoundException { 152 return Class.forName(name, true, classLoader); 153 } 154 155 ClassLoader getClassLoader() { 156 return preferredLoader != null ? preferredLoader : contextLoader; 157 } 158 } 159} 160