/* * Copyright 1999-2015 dangdang.com. * <p> * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * </p> */ package com.dangdang.ddframe.rdb.sharding.config.common.internal.algorithm; import com.dangdang.ddframe.rdb.sharding.api.ShardingValue; import com.dangdang.ddframe.rdb.sharding.router.strategy.MultipleKeysShardingAlgorithm; import com.google.common.base.Joiner; import com.google.common.base.Preconditions; import com.google.common.base.Strings; import com.google.common.collect.Sets; import groovy.lang.Binding; import groovy.lang.Closure; import groovy.lang.GroovyShell; import groovy.util.Expando; import org.slf4j.LoggerFactory; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Set; /** * 基于闭包的数据源划分算法. * * @author gaohongtao */ public class ClosureShardingAlgorithm implements MultipleKeysShardingAlgorithm { private final Closure<?> closureTemplate; public ClosureShardingAlgorithm(final String expression, final String logRoot) { Preconditions.checkArgument(!Strings.isNullOrEmpty(expression)); Preconditions.checkArgument(!Strings.isNullOrEmpty(logRoot)); Binding binding = new Binding(); binding.setVariable("log", LoggerFactory.getLogger(Joiner.on(".").join("com.dangdang.ddframe.rdb.sharding.configFile", logRoot.trim()))); closureTemplate = (Closure) new GroovyShell(binding).evaluate(Joiner.on("").join("{it -> \"", expression.trim(), "\"}")); } @Override public Collection<String> doSharding(final Collection<String> availableTargetNames, final Collection<ShardingValue<?>> shardingValues) { List<Set<Comparable>> valuesDim = new ArrayList<>(); List<String> columnNames = new ArrayList<>(shardingValues.size()); for (ShardingValue<?> each : shardingValues) { columnNames.add(each.getColumnName()); switch (each.getType()) { case SINGLE: valuesDim.add(Sets.newHashSet((Comparable) each.getValue())); break; case LIST: valuesDim.add(Sets.<Comparable>newHashSet(each.getValues())); break; case RANGE: throw new UnsupportedOperationException("Inline expression does not support BETWEEN, please use Java API Config"); default: throw new UnsupportedOperationException(each.getType().name()); } } Set<List<Comparable>> cartesianValues = Sets.cartesianProduct(valuesDim); List<String> result = new ArrayList<>(cartesianValues.size()); for (List<Comparable> each : cartesianValues) { result.add(cloneClosure(columnNames, each).call().toString()); } return result; } private Closure<?> cloneClosure(final List<String> columnNames, final List<Comparable> values) { Closure<?> result = closureTemplate.rehydrate(new Expando(), null, null); result.setResolveStrategy(Closure.DELEGATE_ONLY); result.setProperty("log", closureTemplate.getProperty("log")); for (int i = 0; i < values.size(); i++) { result.setProperty(columnNames.get(i), new ShardingValueWrapper(values.get(i))); } return result; } }