View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *     http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.shardingsphere.infra.datasource.pool.props.creator;
19  
20  import lombok.AccessLevel;
21  import lombok.NoArgsConstructor;
22  import org.apache.shardingsphere.infra.datasource.pool.CatalogSwitchableDataSource;
23  import org.apache.shardingsphere.infra.datasource.pool.config.ConnectionConfiguration;
24  import org.apache.shardingsphere.infra.datasource.pool.config.DataSourceConfiguration;
25  import org.apache.shardingsphere.infra.datasource.pool.config.PoolConfiguration;
26  import org.apache.shardingsphere.infra.datasource.pool.creator.DataSourcePoolReflection;
27  import org.apache.shardingsphere.infra.datasource.pool.metadata.DataSourcePoolMetaData;
28  import org.apache.shardingsphere.infra.datasource.pool.props.domain.DataSourcePoolProperties;
29  import org.apache.shardingsphere.infra.datasource.pool.props.domain.custom.CustomDataSourcePoolProperties;
30  import org.apache.shardingsphere.infra.datasource.pool.props.domain.synonym.ConnectionPropertySynonyms;
31  import org.apache.shardingsphere.infra.datasource.pool.props.domain.synonym.PoolPropertySynonyms;
32  import org.apache.shardingsphere.infra.spi.type.typed.TypedSPILoader;
33  
34  import javax.sql.DataSource;
35  import java.util.LinkedHashMap;
36  import java.util.Map;
37  import java.util.Map.Entry;
38  import java.util.Optional;
39  import java.util.Properties;
40  
41  /**
42   * Data source pool properties creator.
43   */
44  @NoArgsConstructor(access = AccessLevel.PRIVATE)
45  public final class DataSourcePoolPropertiesCreator {
46      
47      /**
48       * Create data source properties.
49       *
50       * @param config data source configuration
51       * @return created data source properties
52       */
53      public static DataSourcePoolProperties create(final DataSourceConfiguration config) {
54          return new DataSourcePoolProperties(config.getConnection().getDataSourceClassName(), createProperties(config));
55      }
56      
57      /**
58       * Create data source properties.
59       *
60       * @param dataSource data source
61       * @return created data source properties
62       */
63      public static DataSourcePoolProperties create(final DataSource dataSource) {
64          DataSource realDataSource = dataSource instanceof CatalogSwitchableDataSource ? ((CatalogSwitchableDataSource) dataSource).getDataSource() : dataSource;
65          return new DataSourcePoolProperties(realDataSource.getClass().getName(), createProperties(realDataSource));
66      }
67      
68      @SuppressWarnings({"unchecked", "rawtypes"})
69      private static Map<String, Object> createProperties(final DataSourceConfiguration config) {
70          Map<String, Object> result = new LinkedHashMap<>();
71          result.put("dataSourceClassName", config.getConnection().getDataSourceClassName());
72          result.put("url", config.getConnection().getUrl());
73          result.put("username", config.getConnection().getUsername());
74          result.put("password", config.getConnection().getPassword());
75          result.put("connectionTimeoutMilliseconds", config.getPool().getConnectionTimeoutMilliseconds());
76          result.put("idleTimeoutMilliseconds", config.getPool().getIdleTimeoutMilliseconds());
77          result.put("maxLifetimeMilliseconds", config.getPool().getMaxLifetimeMilliseconds());
78          result.put("maxPoolSize", config.getPool().getMaxPoolSize());
79          result.put("minPoolSize", config.getPool().getMinPoolSize());
80          result.put("readOnly", config.getPool().getReadOnly());
81          if (null != config.getPool().getCustomProperties()) {
82              result.putAll((Map) config.getPool().getCustomProperties());
83          }
84          return result;
85      }
86      
87      private static Map<String, Object> createProperties(final DataSource dataSource) {
88          Map<String, Object> props = new DataSourcePoolReflection(dataSource).convertToProperties();
89          Map<String, Object> result = new LinkedHashMap<>(props.size(), 1F);
90          Optional<DataSourcePoolMetaData> metaData = TypedSPILoader.findService(DataSourcePoolMetaData.class, dataSource.getClass().getName());
91          for (Entry<String, Object> entry : props.entrySet()) {
92              String propertyName = entry.getKey();
93              Object propertyValue = entry.getValue();
94              if (!metaData.isPresent() || isValidProperty(propertyName, propertyValue, metaData.get()) && !metaData.get().getTransientFieldNames().contains(propertyName)) {
95                  result.put(propertyName, propertyValue);
96              }
97          }
98          return result;
99      }
100     
101     private static boolean isValidProperty(final String key, final Object value, final DataSourcePoolMetaData metaData) {
102         return null == value || !metaData.getSkippedProperties().containsKey(key) || !value.equals(metaData.getSkippedProperties().get(key));
103     }
104     
105     /**
106      * Create data source configuration.
107      *
108      * @param props data source pool properties
109      * @return created data source configuration
110      */
111     public static DataSourceConfiguration createConfiguration(final DataSourcePoolProperties props) {
112         return new DataSourceConfiguration(getConnectionConfiguration(props.getConnectionPropertySynonyms()), getPoolConfiguration(props.getPoolPropertySynonyms(), props.getCustomProperties()));
113     }
114     
115     private static ConnectionConfiguration getConnectionConfiguration(final ConnectionPropertySynonyms connectionPropSynonyms) {
116         Map<String, Object> standardProps = connectionPropSynonyms.getStandardProperties();
117         return new ConnectionConfiguration(
118                 (String) standardProps.get("dataSourceClassName"), (String) standardProps.get("url"), (String) standardProps.get("username"), (String) standardProps.get("password"));
119     }
120     
121     private static PoolConfiguration getPoolConfiguration(final PoolPropertySynonyms poolPropSynonyms, final CustomDataSourcePoolProperties customProps) {
122         Map<String, Object> standardProps = poolPropSynonyms.getStandardProperties();
123         Long connectionTimeoutMilliseconds = toLong(standardProps, "connectionTimeoutMilliseconds");
124         Long idleTimeoutMilliseconds = toLong(standardProps, "idleTimeoutMilliseconds");
125         Long maxLifetimeMilliseconds = toLong(standardProps, "maxLifetimeMilliseconds");
126         Integer maxPoolSize = toInt(standardProps, "maxPoolSize");
127         Integer minPoolSize = toInt(standardProps, "minPoolSize");
128         Boolean readOnly = toBoolean(standardProps, "readOnly");
129         Properties newCustomProps = new Properties();
130         newCustomProps.putAll(customProps.getProperties());
131         return new PoolConfiguration(connectionTimeoutMilliseconds, idleTimeoutMilliseconds, maxLifetimeMilliseconds, maxPoolSize, minPoolSize, readOnly, newCustomProps);
132     }
133     
134     private static Long toLong(final Map<String, Object> props, final String name) {
135         if (!props.containsKey(name)) {
136             return null;
137         }
138         try {
139             return Long.parseLong(String.valueOf(props.get(name)));
140         } catch (final NumberFormatException ex) {
141             return null;
142         }
143     }
144     
145     private static Integer toInt(final Map<String, Object> props, final String name) {
146         if (!props.containsKey(name)) {
147             return null;
148         }
149         try {
150             return Integer.parseInt(String.valueOf(props.get(name)));
151         } catch (final NumberFormatException ex) {
152             return null;
153         }
154     }
155     
156     @SuppressWarnings("SameParameterValue")
157     private static Boolean toBoolean(final Map<String, Object> props, final String name) {
158         return props.containsKey(name) ? Boolean.parseBoolean(String.valueOf(props.get(name))) : null;
159     }
160 }