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.mask.rule;
19  
20  import com.google.common.base.Preconditions;
21  import org.apache.shardingsphere.infra.algorithm.core.config.AlgorithmConfiguration;
22  import org.apache.shardingsphere.infra.rule.PartialRuleUpdateSupported;
23  import org.apache.shardingsphere.infra.rule.attribute.RuleAttributes;
24  import org.apache.shardingsphere.infra.rule.scope.DatabaseRule;
25  import org.apache.shardingsphere.infra.spi.type.typed.TypedSPILoader;
26  import org.apache.shardingsphere.mask.api.config.MaskRuleConfiguration;
27  import org.apache.shardingsphere.mask.api.config.rule.MaskTableRuleConfiguration;
28  import org.apache.shardingsphere.mask.rule.attribute.MaskTableMapperRuleAttribute;
29  import org.apache.shardingsphere.mask.spi.MaskAlgorithm;
30  
31  import java.util.Collection;
32  import java.util.HashSet;
33  import java.util.Map.Entry;
34  import java.util.Optional;
35  import java.util.concurrent.ConcurrentHashMap;
36  import java.util.concurrent.atomic.AtomicReference;
37  import java.util.stream.Collectors;
38  
39  /**
40   * Mask rule.
41   */
42  public final class MaskRule implements DatabaseRule, PartialRuleUpdateSupported<MaskRuleConfiguration> {
43      
44      private final AtomicReference<MaskRuleConfiguration> configuration = new AtomicReference<>();
45      
46      private final ConcurrentHashMap<String, MaskAlgorithm<?, ?>> maskAlgorithms;
47      
48      private final ConcurrentHashMap<String, MaskTable> tables;
49      
50      private final AtomicReference<RuleAttributes> attributes = new AtomicReference<>();
51      
52      @SuppressWarnings("unchecked")
53      public MaskRule(final MaskRuleConfiguration ruleConfig) {
54          configuration.set(ruleConfig);
55          maskAlgorithms = ruleConfig.getMaskAlgorithms().entrySet().stream()
56                  .collect(Collectors.toMap(Entry::getKey, entry -> TypedSPILoader.getService(MaskAlgorithm.class, entry.getValue().getType(), entry.getValue().getProps()),
57                          (oldValue, currentValue) -> oldValue, ConcurrentHashMap::new));
58          tables = ruleConfig.getTables().stream()
59                  .collect(Collectors.toMap(each -> each.getName().toLowerCase(), each -> new MaskTable(each, maskAlgorithms), (oldValue, currentValue) -> oldValue, ConcurrentHashMap::new));
60          attributes.set(new RuleAttributes(new MaskTableMapperRuleAttribute(tables.keySet())));
61      }
62      
63      /**
64       * Find mask table.
65       *
66       * @param tableName table name
67       * @return found mask table
68       */
69      public Optional<MaskTable> findMaskTable(final String tableName) {
70          return Optional.ofNullable(tables.get(tableName));
71      }
72      
73      @Override
74      public RuleAttributes getAttributes() {
75          return attributes.get();
76      }
77      
78      @Override
79      public MaskRuleConfiguration getConfiguration() {
80          return configuration.get();
81      }
82      
83      @Override
84      public void updateConfiguration(final MaskRuleConfiguration toBeUpdatedRuleConfig) {
85          configuration.set(toBeUpdatedRuleConfig);
86      }
87      
88      @Override
89      public boolean partialUpdate(final MaskRuleConfiguration toBeUpdatedRuleConfig) {
90          Collection<String> toBeAddedTableNames = toBeUpdatedRuleConfig.getTables().stream().map(MaskTableRuleConfiguration::getName).collect(Collectors.toList());
91          toBeAddedTableNames.removeAll(tables.keySet());
92          if (!toBeAddedTableNames.isEmpty()) {
93              toBeAddedTableNames.forEach(each -> addTableRule(each, toBeUpdatedRuleConfig));
94              attributes.set(new RuleAttributes(new MaskTableMapperRuleAttribute(tables.keySet())));
95              return true;
96          }
97          Collection<String> toBeRemovedTableNames = new HashSet<>(tables.keySet());
98          toBeRemovedTableNames.removeAll(toBeUpdatedRuleConfig.getTables().stream().map(MaskTableRuleConfiguration::getName).collect(Collectors.toList()));
99          if (!toBeRemovedTableNames.isEmpty()) {
100             toBeRemovedTableNames.stream().map(String::toLowerCase).forEach(tables::remove);
101             attributes.set(new RuleAttributes(new MaskTableMapperRuleAttribute(tables.keySet())));
102             // TODO check and remove unused INLINE mask algorithms
103             return true;
104         }
105         // TODO Process update table
106         // TODO Process CRUD mask algorithms
107         return false;
108     }
109     
110     private void addTableRule(final String tableName, final MaskRuleConfiguration toBeUpdatedRuleConfig) {
111         MaskTableRuleConfiguration tableRuleConfig = getTableRuleConfiguration(tableName, toBeUpdatedRuleConfig);
112         for (Entry<String, AlgorithmConfiguration> entry : toBeUpdatedRuleConfig.getMaskAlgorithms().entrySet()) {
113             maskAlgorithms.computeIfAbsent(entry.getKey(), key -> TypedSPILoader.getService(MaskAlgorithm.class, entry.getValue().getType(), entry.getValue().getProps()));
114         }
115         tables.put(tableName.toLowerCase(), new MaskTable(tableRuleConfig, maskAlgorithms));
116     }
117     
118     private MaskTableRuleConfiguration getTableRuleConfiguration(final String tableName, final MaskRuleConfiguration toBeUpdatedRuleConfig) {
119         Optional<MaskTableRuleConfiguration> result = toBeUpdatedRuleConfig.getTables().stream().filter(table -> table.getName().equals(tableName)).findFirst();
120         Preconditions.checkState(result.isPresent());
121         return result.get();
122     }
123 }