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.route.context;
19  
20  import lombok.Getter;
21  import org.apache.shardingsphere.infra.datanode.DataNode;
22  import org.apache.shardingsphere.infra.rule.ShardingSphereRule;
23  
24  import java.util.Collection;
25  import java.util.HashMap;
26  import java.util.HashSet;
27  import java.util.LinkedHashMap;
28  import java.util.LinkedHashSet;
29  import java.util.LinkedList;
30  import java.util.List;
31  import java.util.Map;
32  import java.util.Optional;
33  import java.util.Set;
34  import java.util.stream.Collectors;
35  
36  /**
37   * Route context.
38   */
39  @Getter
40  public final class RouteContext {
41      
42      private final Collection<Collection<DataNode>> originalDataNodes = new LinkedList<>();
43      
44      private final Collection<RouteUnit> routeUnits = new LinkedHashSet<>();
45      
46      private final Map<Class<? extends ShardingSphereRule>, RouteStageContext> routeStageContexts = new LinkedHashMap<>();
47      
48      /**
49       * Judge is route for single database and table only or not.
50       *
51       * @return is route for single database and table only or not
52       */
53      public boolean isSingleRouting() {
54          return 1 == routeUnits.size();
55      }
56      
57      /**
58       * Get actual data source names.
59       *
60       * @return actual data source names
61       */
62      public Collection<String> getActualDataSourceNames() {
63          return routeUnits.stream().map(each -> each.getDataSourceMapper().getActualName()).collect(Collectors.toCollection(() -> new HashSet<>(routeUnits.size(), 1L)));
64      }
65      
66      /**
67       * Get actual tables groups.
68       *
69       * <p>
70       * Actual tables in same group are belong one logic name.
71       * </p>
72       *
73       * @param actualDataSourceName actual data source name
74       * @param logicTableNames logic table names
75       * @return actual table groups
76       */
77      public List<Set<String>> getActualTableNameGroups(final String actualDataSourceName, final Set<String> logicTableNames) {
78          return logicTableNames.stream().map(each -> getActualTableNames(actualDataSourceName, each)).filter(each -> !each.isEmpty()).collect(Collectors.toList());
79      }
80      
81      private Set<String> getActualTableNames(final String actualDataSourceName, final String logicTableName) {
82          Set<String> result = new LinkedHashSet<>();
83          for (RouteUnit each : routeUnits) {
84              if (actualDataSourceName.equalsIgnoreCase(each.getDataSourceMapper().getActualName())) {
85                  result.addAll(each.getActualTableNames(logicTableName));
86              }
87          }
88          return result;
89      }
90      
91      /**
92       * Get map relationship between actual data source and logic tables.
93       *
94       * @param actualDataSourceNames actual data source names
95       * @return map relationship between data source and logic tables
96       */
97      public Map<String, Set<String>> getDataSourceLogicTablesMap(final Collection<String> actualDataSourceNames) {
98          Map<String, Set<String>> result = new HashMap<>(actualDataSourceNames.size(), 1F);
99          for (String each : actualDataSourceNames) {
100             Set<String> logicTableNames = getLogicTableNames(each);
101             if (!logicTableNames.isEmpty()) {
102                 result.put(each, logicTableNames);
103             }
104         }
105         return result;
106     }
107     
108     private Set<String> getLogicTableNames(final String actualDataSourceName) {
109         Set<String> result = new HashSet<>();
110         for (RouteUnit each : routeUnits) {
111             if (actualDataSourceName.equalsIgnoreCase(each.getDataSourceMapper().getActualName())) {
112                 result.addAll(each.getLogicTableNames());
113             }
114         }
115         return result;
116     }
117     
118     /**
119      * Find table mapper.
120      *
121      * @param logicDataSourceName logic data source name
122      * @param actualTableName actual table name
123      * @return table mapper
124      */
125     public Optional<RouteMapper> findTableMapper(final String logicDataSourceName, final String actualTableName) {
126         for (RouteUnit each : routeUnits) {
127             Optional<RouteMapper> result = each.findTableMapper(logicDataSourceName, actualTableName);
128             if (result.isPresent()) {
129                 return result;
130             }
131         }
132         return Optional.empty();
133     }
134     
135     /**
136      * Put route unit.
137      *
138      * @param dataSourceMapper database mapper
139      * @param tableMappers table mapper collection
140      */
141     public void putRouteUnit(final RouteMapper dataSourceMapper, final Collection<RouteMapper> tableMappers) {
142         Collection<RouteUnit> targets = getTargetRouteUnits(dataSourceMapper);
143         if (targets.isEmpty()) {
144             RouteUnit unit = new RouteUnit(dataSourceMapper, new LinkedHashSet<>());
145             unit.getTableMappers().addAll(tableMappers);
146             routeUnits.add(unit);
147         } else {
148             Collection<RouteUnit> toBeAdded = new LinkedList<>();
149             Collection<RouteUnit> toBeRemoved = new LinkedList<>();
150             for (RouteUnit each : targets) {
151                 RouteUnit unit = new RouteUnit(dataSourceMapper, new LinkedHashSet<>());
152                 unit.getTableMappers().addAll(each.getTableMappers());
153                 unit.getTableMappers().addAll(tableMappers);
154                 toBeAdded.add(unit);
155                 toBeRemoved.add(each);
156             }
157             boolean success = routeUnits.addAll(toBeAdded);
158             if (success) {
159                 routeUnits.removeAll(toBeRemoved);
160             }
161         }
162     }
163     
164     private Collection<RouteUnit> getTargetRouteUnits(final RouteMapper dataSourceMapper) {
165         Collection<RouteUnit> result = new LinkedList<>();
166         for (RouteUnit each : routeUnits) {
167             if (each.getDataSourceMapper().equals(dataSourceMapper)) {
168                 result.add(each);
169             }
170         }
171         return result;
172     }
173     
174     /**
175      * Judge whether route context contains table sharding or not.
176      * 
177      * @return whether route context contains table sharding or not
178      */
179     public boolean containsTableSharding() {
180         for (RouteUnit each : routeUnits) {
181             for (RouteMapper tableMapper : each.getTableMappers()) {
182                 if (!tableMapper.getActualName().equals(tableMapper.getLogicName())) {
183                     return true;
184                 }
185             }
186         }
187         return false;
188     }
189 }