1 package com.vladmihalcea.hibernate.type.util;
2
3 import com.fasterxml.jackson.databind.ObjectMapper;
4 import com.vladmihalcea.hibernate.util.ClassLoaderUtils;
5 import org.hibernate.cfg.Environment;
6
7 import java.io.File;
8 import java.io.IOException;
9 import java.io.InputStream;
10 import java.io.Serializable;
11 import java.net.MalformedURLException;
12 import java.net.URL;
13 import java.util.Properties;
14 import java.util.function.Supplier;
15
16 import static com.vladmihalcea.hibernate.util.LogUtils.LOGGER;
17
18 /**
19  * <code>Configuration</code> - It allows declarative configuration through the <code>hibernate.properties</code> file
20  * or the <code>hibernate-types.properties</code> file.
21  *
22  * The properties from <code>hibernate-types.properties</code> can override the ones from the <code>hibernate.properties</code> file.
23  *
24  * It loads the {@link Properties} configuration file and makes them available to other components.
25  *
26  * @author Vlad Mihalcea
27  * @since 2.1.0
28  */

29 public class Configuration implements Serializable {
30
31     public static final Configuration INSTANCE = new Configuration();
32
33     public static final String PROPERTIES_FILE_PATH = "hibernate-types.properties.path";
34     public static final String PROPERTIES_FILE_NAME = "hibernate-types.properties";
35     public static final String APPLICATION_PROPERTIES_FILE_NAME = "application.properties";
36
37     /**
38      * Each Property has a well-defined key.
39      */

40     public enum PropertyKey {
41         JACKSON_OBJECT_MAPPER("hibernate.types.jackson.object.mapper"),
42         JSON_SERIALIZER("hibernate.types.json.serializer"),
43         PRINT_BANNER("hibernate.types.print.banner");
44
45         private final String key;
46
47         PropertyKey(String key) {
48             this.key = key;
49         }
50
51         public String getKey() {
52             return key;
53         }
54     }
55
56     private final ObjectMapperWrapper objectMapperWrapper;
57
58     private final Properties properties = Environment.getProperties();
59
60     private Configuration() {
61         load();
62
63         Object objectMapperPropertyInstance = instantiateClass(PropertyKey.JACKSON_OBJECT_MAPPER);
64
65         ObjectMapperWrapper objectMapperWrapper = new ObjectMapperWrapper();
66
67         if (objectMapperPropertyInstance != null) {
68             if (objectMapperPropertyInstance instanceof ObjectMapperSupplier) {
69                 ObjectMapperSupplier objectMapperSupplier = (ObjectMapperSupplier) objectMapperPropertyInstance;
70                 if (objectMapperSupplier != null) {
71                     objectMapperWrapper = new ObjectMapperWrapper(objectMapperSupplier);
72                 }
73             } else if (objectMapperPropertyInstance instanceof Supplier) {
74                 Supplier<ObjectMapper> objectMapperSupplier = (Supplier<ObjectMapper>) objectMapperPropertyInstance;
75                 objectMapperWrapper = new ObjectMapperWrapper((ObjectMapperSupplier) () -> objectMapperSupplier.get());
76             } else if (objectMapperPropertyInstance instanceof ObjectMapper) {
77                 ObjectMapper objectMapper = (ObjectMapper) objectMapperPropertyInstance;
78                 objectMapperWrapper = new ObjectMapperWrapper(objectMapper);
79             }
80         }
81
82         Object jsonSerializerPropertyInstance = instantiateClass(PropertyKey.JSON_SERIALIZER);
83
84         if (jsonSerializerPropertyInstance != null) {
85             JsonSerializer jsonSerializer = null;
86
87             if (jsonSerializerPropertyInstance instanceof JsonSerializerSupplier) {
88                 jsonSerializer = ((JsonSerializerSupplier) jsonSerializerPropertyInstance).get();
89             } else if (jsonSerializerPropertyInstance instanceof Supplier) {
90                 Supplier<JsonSerializer> jsonSerializerSupplier = (Supplier<JsonSerializer>) jsonSerializerPropertyInstance;
91                 jsonSerializer = jsonSerializerSupplier.get();
92             } else if (jsonSerializerPropertyInstance instanceof JsonSerializer) {
93                 jsonSerializer = (JsonSerializer) jsonSerializerPropertyInstance;
94             }
95
96             if (jsonSerializer != null) {
97                 objectMapperWrapper.setJsonSerializer(jsonSerializer);
98             }
99         }
100
101         this.objectMapperWrapper = objectMapperWrapper;
102     }
103
104     /**
105      * Load {@link Properties} from the resolved {@link InputStream}
106      */

107     private void load() {
108         String[] propertiesFilePaths = new String[] {
109             APPLICATION_PROPERTIES_FILE_NAME,
110             PROPERTIES_FILE_NAME,
111             System.getProperty(PROPERTIES_FILE_NAME),
112
113         };
114
115         for (String propertiesFilePath : propertiesFilePaths) {
116             if (propertiesFilePath != null) {
117                 InputStream propertiesInputStream = null;
118                 try {
119                     propertiesInputStream = propertiesInputStream(propertiesFilePath);
120                     if (propertiesInputStream != null) {
121                         properties.load(propertiesInputStream);
122                     }
123                 } catch (IOException e) {
124                     LOGGER.error("Can't load properties", e);
125                 } finally {
126                     try {
127                         if (propertiesInputStream != null) {
128                             propertiesInputStream.close();
129                         }
130                     } catch (IOException e) {
131                         LOGGER.error("Can't close the properties InputStream", e);
132                     }
133                 }
134             }
135         }
136     }
137
138     /**
139      * Get {@link Properties} file {@link InputStream}
140      *
141      * @param propertiesFilePath properties file path
142      * @return {@link Properties} file {@link InputStream}
143      * @throws IOException the file couldn't be loaded properly
144      */

145     private InputStream propertiesInputStream(String propertiesFilePath) throws IOException {
146         if (propertiesFilePath != null) {
147             URL propertiesFileUrl;
148             try {
149                 propertiesFileUrl = new URL(propertiesFilePath);
150             } catch (MalformedURLException ignore) {
151                 propertiesFileUrl = ClassLoaderUtils.getResource(propertiesFilePath);
152                 if (propertiesFileUrl == null) {
153                     File f = new File(propertiesFilePath);
154                     if (f.exists() && f.isFile()) {
155                         try {
156                             propertiesFileUrl = f.toURI().toURL();
157                         } catch (MalformedURLException e) {
158                             LOGGER.error(
159                                 "The property " + propertiesFilePath + " can't be resolved to either a URL, " +
160                                     "a classpath resource or a File"
161                             );
162                         }
163                     }
164                 }
165             }
166             if (propertiesFileUrl != null) {
167                 return propertiesFileUrl.openStream();
168             }
169         }
170         return ClassLoaderUtils.getResourceAsStream(propertiesFilePath);
171     }
172
173     /**
174      * Get all properties.
175      *
176      * @return properties.
177      */

178     public Properties getProperties() {
179         return properties;
180     }
181
182     /**
183      * Get {@link ObjectMapperWrapper} reference
184      *
185      * @return {@link ObjectMapperWrapper} reference
186      */

187     public ObjectMapperWrapper getObjectMapperWrapper() {
188         return objectMapperWrapper;
189     }
190
191     /**
192      * Get Integer property value
193      *
194      * @param propertyKey property key
195      * @return Integer property value
196      */

197     public Integer integerProperty(PropertyKey propertyKey) {
198         Integer value = null;
199         String property = properties.getProperty(propertyKey.getKey());
200         if (property != null) {
201             value = Integer.valueOf(property);
202         }
203         return value;
204     }
205
206     /**
207      * Get Long property value
208      *
209      * @param propertyKey property key
210      * @return Long property value
211      */

212     public Long longProperty(PropertyKey propertyKey) {
213         Long value = null;
214         String property = properties.getProperty(propertyKey.getKey());
215         if (property != null) {
216             value = Long.valueOf(property);
217         }
218         return value;
219     }
220
221     /**
222      * Get Boolean property value
223      *
224      * @param propertyKey property key
225      * @return Boolean property value
226      */

227     public Boolean booleanProperty(PropertyKey propertyKey) {
228         Boolean value = null;
229         String property = properties.getProperty(propertyKey.getKey());
230         if (property != null) {
231             value = Boolean.valueOf(property);
232         }
233         return value;
234     }
235
236     /**
237      * Get Class property value
238      *
239      * @param propertyKey property key
240      * @param <T> class generic type
241      * @return Class property value
242      */

243     public <T> Class<T> classProperty(PropertyKey propertyKey) {
244         Class<T> clazz = null;
245         String property = properties.getProperty(propertyKey.getKey());
246         if (property != null) {
247             try {
248                 return ClassLoaderUtils.loadClass(property);
249             } catch (ClassNotFoundException e) {
250                 LOGGER.error("Couldn't load the " + property + class given by the " + propertyKey + " property", e);
251             }
252         }
253         return clazz;
254     }
255
256     /**
257      * Instantiate class associated to the given property key
258      *
259      * @param propertyKey property key
260      * @param <T>         class parameter type
261      * @return class instance
262      */

263     private <T> T instantiateClass(PropertyKey propertyKey) {
264         T object = null;
265         String property = properties.getProperty(propertyKey.getKey());
266         if (property != null) {
267             try {
268                 Class<T> clazz = ClassLoaderUtils.loadClass(property);
269                 LOGGER.debug("Instantiate {}", clazz);
270                 object = clazz.newInstance();
271             } catch (ClassNotFoundException e) {
272                 LOGGER.error("Couldn't load the " + property + class given by the " + propertyKey + " property", e);
273             } catch (InstantiationException e) {
274                 LOGGER.error("Couldn't instantiate the " + property + class given by the " + propertyKey + " property", e);
275             } catch (IllegalAccessException e) {
276                 LOGGER.error("Couldn't access the " + property + class given by the " + propertyKey + " property", e);
277             }
278         }
279         return object;
280     }
281 }
282