View Javadoc
1   /*
2    *    Copyright 2015-2023 the original author or authors.
3    *
4    *    Licensed under the Apache License, Version 2.0 (the "License");
5    *    you may not use this file except in compliance with the License.
6    *    You may obtain a copy of the License at
7    *
8    *       https://www.apache.org/licenses/LICENSE-2.0
9    *
10   *    Unless required by applicable law or agreed to in writing, software
11   *    distributed under the License is distributed on an "AS IS" BASIS,
12   *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   *    See the License for the specific language governing permissions and
14   *    limitations under the License.
15   */
16  package org.mybatis.caches.redis;
17  
18  import java.io.IOException;
19  import java.io.InputStream;
20  import java.util.Arrays;
21  import java.util.Map;
22  import java.util.Properties;
23  
24  import org.apache.ibatis.cache.CacheException;
25  import org.apache.ibatis.io.Resources;
26  import org.apache.ibatis.reflection.MetaObject;
27  import org.apache.ibatis.reflection.SystemMetaObject;
28  
29  /**
30   * Converter from the Config to a proper {@link RedisConfig}.
31   *
32   * @author Eduardo Macarron
33   */
34  final class RedisConfigurationBuilder {
35  
36    /**
37     * This class instance.
38     */
39    private static final RedisConfigurationBuilder INSTANCE = new RedisConfigurationBuilder();
40  
41    protected static final String SYSTEM_PROPERTY_REDIS_PROPERTIES_FILENAME = "redis.properties.filename";
42  
43    protected static final String REDIS_RESOURCE = "redis.properties";
44  
45    /**
46     * Hidden constructor, this class can't be instantiated.
47     */
48    private RedisConfigurationBuilder() {
49    }
50  
51    /**
52     * Return this class instance.
53     *
54     * @return this class instance.
55     */
56    public static RedisConfigurationBuilder getInstance() {
57      return INSTANCE;
58    }
59  
60    /**
61     * Parses the Config and builds a new {@link RedisConfig}.
62     *
63     * @return the converted {@link RedisConfig}.
64     */
65    public RedisConfig parseConfiguration() {
66      return parseConfiguration(getClass().getClassLoader());
67    }
68  
69    /**
70     * Parses the Config and builds a new {@link RedisConfig}.
71     *
72     * @param the
73     *          {@link ClassLoader} used to load the {@code memcached.properties} file in classpath.
74     *
75     * @return the converted {@link RedisConfig}.
76     */
77    public RedisConfig parseConfiguration(ClassLoader classLoader) {
78      Properties config = new Properties();
79  
80      String redisPropertiesFilename = System.getProperty(SYSTEM_PROPERTY_REDIS_PROPERTIES_FILENAME, REDIS_RESOURCE);
81  
82      try (InputStream input = classLoader.getResourceAsStream(redisPropertiesFilename)) {
83        if (input != null) {
84          config.load(input);
85        }
86      } catch (IOException e) {
87        throw new RuntimeException(
88            "An error occurred while reading classpath property '" + redisPropertiesFilename + "', see nested exceptions",
89            e);
90      }
91  
92      RedisConfig jedisConfig = new RedisConfig();
93      setConfigProperties(config, jedisConfig);
94      return jedisConfig;
95    }
96  
97    private void setConfigProperties(Properties properties, RedisConfig jedisConfig) {
98      if (properties != null) {
99        MetaObject metaCache = SystemMetaObject.forObject(jedisConfig);
100       for (Map.Entry<Object, Object> entry : properties.entrySet()) {
101         String name = (String) entry.getKey();
102         // All prefix of 'redis.' on property values
103         if (name != null && name.startsWith("redis.")) {
104           name = name.substring(6);
105         } else {
106           // Skip non prefixed properties
107           continue;
108         }
109         String value = (String) entry.getValue();
110         if ("serializer".equals(name)) {
111           if ("kryo".equalsIgnoreCase(value)) {
112             jedisConfig.setSerializer(KryoSerializer.INSTANCE);
113           } else if (!"jdk".equalsIgnoreCase(value)) {
114             // Custom serializer is not supported yet.
115             throw new CacheException("Unknown serializer: '" + value + "'");
116           }
117         } else if (Arrays.asList("sslSocketFactory", "sslParameters", "hostnameVerifier").contains(name)) {
118           setInstance(metaCache, name, value);
119         } else if (metaCache.hasSetter(name)) {
120           Class<?> type = metaCache.getSetterType(name);
121           if (String.class == type) {
122             metaCache.setValue(name, value);
123           } else if (int.class == type || Integer.class == type) {
124             metaCache.setValue(name, Integer.valueOf(value));
125           } else if (long.class == type || Long.class == type) {
126             metaCache.setValue(name, Long.valueOf(value));
127           } else if (short.class == type || Short.class == type) {
128             metaCache.setValue(name, Short.valueOf(value));
129           } else if (byte.class == type || Byte.class == type) {
130             metaCache.setValue(name, Byte.valueOf(value));
131           } else if (float.class == type || Float.class == type) {
132             metaCache.setValue(name, Float.valueOf(value));
133           } else if (boolean.class == type || Boolean.class == type) {
134             metaCache.setValue(name, Boolean.valueOf(value));
135           } else if (double.class == type || Double.class == type) {
136             metaCache.setValue(name, Double.valueOf(value));
137           } else {
138             throw new CacheException("Unsupported property type: '" + name + "' of type " + type);
139           }
140         }
141       }
142     }
143   }
144 
145   protected void setInstance(MetaObject metaCache, String name, String value) {
146     if (value == null || value.isEmpty()) {
147       return;
148     }
149     Object instance;
150     try {
151       Class<?> clazz = Resources.classForName(value);
152       instance = clazz.getDeclaredConstructor().newInstance();
153     } catch (Exception e) {
154       throw new CacheException("Could not instantiate class: '" + value + "'.", e);
155     }
156     metaCache.setValue(name, instance);
157   }
158 
159 }