/*
* Copyright 2009-2016 Weibo, Inc.
*
* 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.
*/
package com.weibo.api.motan.config;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
import org.apache.commons.lang3.StringUtils;
import com.weibo.api.motan.cluster.Cluster;
import com.weibo.api.motan.cluster.support.ClusterSupport;
import com.weibo.api.motan.common.MotanConstants;
import com.weibo.api.motan.common.URLParamType;
import com.weibo.api.motan.config.annotation.ConfigDesc;
import com.weibo.api.motan.config.handler.ConfigHandler;
import com.weibo.api.motan.core.extension.ExtensionLoader;
import com.weibo.api.motan.exception.MotanErrorMsgConstant;
import com.weibo.api.motan.exception.MotanFrameworkException;
import com.weibo.api.motan.registry.RegistryService;
import com.weibo.api.motan.rpc.URL;
import com.weibo.api.motan.util.CollectionUtil;
import com.weibo.api.motan.util.NetUtils;
import com.weibo.api.motan.util.StringTools;
/**
*
* Referer config.
*
* @author fishermen
* @version V1.0 created at: 2013-5-17
*/
public class RefererConfig<T> extends AbstractRefererConfig {
private static final long serialVersionUID = -2299754608229467887L;
private Class<T> interfaceClass;
// 具体到方法的配置
protected List<MethodConfig> methods;
// 点对点直连服务提供地址
private String directUrl;
private AtomicBoolean initialized = new AtomicBoolean(false);
private T ref;
private BasicRefererInterfaceConfig basicReferer;
private List<ClusterSupport<T>> clusterSupports;
public List<MethodConfig> getMethods() {
return methods;
}
public void setMethods(List<MethodConfig> methods) {
this.methods = methods;
}
public void setMethods(MethodConfig methods) {
this.methods = Collections.singletonList(methods);
}
public boolean hasMethods() {
return this.methods != null && !this.methods.isEmpty();
}
public T getRef() {
if (ref == null) {
initRef();
}
return ref;
}
@SuppressWarnings({"unchecked", "rawtypes"})
public synchronized void initRef() {
if (initialized.get()) {
return;
}
try {
interfaceClass = (Class) Class.forName(interfaceClass.getName(), true, Thread.currentThread().getContextClassLoader());
} catch (ClassNotFoundException e) {
throw new MotanFrameworkException("ReferereConfig initRef Error: Class not found " + interfaceClass.getName(), e,
MotanErrorMsgConstant.FRAMEWORK_INIT_ERROR);
}
if (CollectionUtil.isEmpty(protocols)) {
throw new MotanFrameworkException(String.format("%s RefererConfig is malformed, for protocol not set correctly!",
interfaceClass.getName()));
}
checkInterfaceAndMethods(interfaceClass, methods);
clusterSupports = new ArrayList<ClusterSupport<T>>(protocols.size());
List<Cluster<T>> clusters = new ArrayList<Cluster<T>>(protocols.size());
String proxy = null;
ConfigHandler configHandler = ExtensionLoader.getExtensionLoader(ConfigHandler.class).getExtension(MotanConstants.DEFAULT_VALUE);
List<URL> registryUrls = loadRegistryUrls();
String localIp = getLocalHostAddress(registryUrls);
for (ProtocolConfig protocol : protocols) {
Map<String, String> params = new HashMap<String, String>();
params.put(URLParamType.nodeType.getName(), MotanConstants.NODE_TYPE_REFERER);
params.put(URLParamType.version.getName(), URLParamType.version.getValue());
params.put(URLParamType.refreshTimestamp.getName(), String.valueOf(System.currentTimeMillis()));
collectConfigParams(params, protocol, basicReferer, extConfig, this);
collectMethodConfigParams(params, this.getMethods());
URL refUrl = new URL(protocol.getName(), localIp, MotanConstants.DEFAULT_INT_VALUE, interfaceClass.getName(), params);
ClusterSupport<T> clusterSupport = createClusterSupport(refUrl, configHandler, registryUrls);
clusterSupports.add(clusterSupport);
clusters.add(clusterSupport.getCluster());
proxy = (proxy == null) ? refUrl.getParameter(URLParamType.proxy.getName(), URLParamType.proxy.getValue()) : proxy;
}
ref = configHandler.refer(interfaceClass, clusters, proxy);
initialized.set(true);
}
private ClusterSupport<T> createClusterSupport(URL refUrl, ConfigHandler configHandler, List<URL> registryUrls) {
List<URL> regUrls = new ArrayList<URL>();
// 如果用户指定directUrls 或者 injvm协议访问,则使用local registry
if (StringUtils.isNotBlank(directUrl) || MotanConstants.PROTOCOL_INJVM.equals(refUrl.getProtocol())) {
URL regUrl =
new URL(MotanConstants.REGISTRY_PROTOCOL_LOCAL, NetUtils.LOCALHOST, MotanConstants.DEFAULT_INT_VALUE,
RegistryService.class.getName());
if (StringUtils.isNotBlank(directUrl)) {
StringBuilder duBuf = new StringBuilder(128);
String[] dus = MotanConstants.COMMA_SPLIT_PATTERN.split(directUrl);
for (String du : dus) {
if (du.contains(":")) {
String[] hostPort = du.split(":");
URL durl = refUrl.createCopy();
durl.setHost(hostPort[0].trim());
durl.setPort(Integer.parseInt(hostPort[1].trim()));
durl.addParameter(URLParamType.nodeType.getName(), MotanConstants.NODE_TYPE_SERVICE);
duBuf.append(StringTools.urlEncode(durl.toFullStr())).append(MotanConstants.COMMA_SEPARATOR);
}
}
if (duBuf.length() > 0) {
duBuf.deleteCharAt(duBuf.length() - 1);
regUrl.addParameter(URLParamType.directUrl.getName(), duBuf.toString());
}
}
regUrls.add(regUrl);
} else { // 通过注册中心配置拼装URL,注册中心可能在本地,也可能在远端
if (registryUrls == null || registryUrls.isEmpty()) {
throw new IllegalStateException(
String.format(
"No registry to reference %s on the consumer %s , please config <motan:registry address=\"...\" /> in your spring config.",
interfaceClass, NetUtils.LOCALHOST));
}
for (URL url : registryUrls) {
regUrls.add(url.createCopy());
}
}
for (URL url : regUrls) {
url.addParameter(URLParamType.embed.getName(), StringTools.urlEncode(refUrl.toFullStr()));
}
return configHandler.buildClusterSupport(interfaceClass, regUrls);
}
public synchronized void destroy() {
if (clusterSupports != null) {
for (ClusterSupport<T> clusterSupport : clusterSupports) {
clusterSupport.destroy();
}
}
ref = null;
initialized.set(false);
}
public void setInterface(Class<T> interfaceClass) {
if (interfaceClass != null && !interfaceClass.isInterface()) {
throw new IllegalStateException("The interface class " + interfaceClass + " is not a interface!");
}
this.interfaceClass = interfaceClass;
}
public Class<?> getInterface() {
return interfaceClass;
}
public String getDirectUrl() {
return directUrl;
}
public void setDirectUrl(String directUrl) {
this.directUrl = directUrl;
}
@ConfigDesc(excluded = true)
public BasicRefererInterfaceConfig getBasicReferer() {
return basicReferer;
}
public void setBasicReferer(BasicRefererInterfaceConfig basicReferer) {
this.basicReferer = basicReferer;
}
public List<ClusterSupport<T>> getClusterSupports() {
return clusterSupports;
}
public AtomicBoolean getInitialized() {
return initialized;
}
}