/*
* 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 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.exception.MotanServiceException;
import com.weibo.api.motan.registry.RegistryService;
import com.weibo.api.motan.rpc.Exporter;
import com.weibo.api.motan.rpc.URL;
import com.weibo.api.motan.util.ConcurrentHashSet;
import com.weibo.api.motan.util.LoggerUtil;
import com.weibo.api.motan.util.NetUtils;
import com.weibo.api.motan.util.StringTools;
import org.apache.commons.lang3.StringUtils;
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* @author fishermen
* @version V1.0 created at: 2013-5-16
*/
public class ServiceConfig<T> extends AbstractServiceConfig {
private static final long serialVersionUID = -3342374271064293224L;
private static ConcurrentHashSet<String> existingServices = new ConcurrentHashSet<String>();
// 具体到方法的配置
protected List<MethodConfig> methods;
// 接口实现类引用
private T ref;
// service 对应的exporters,用于管理service服务的生命周期
private List<Exporter<T>> exporters = new CopyOnWriteArrayList<Exporter<T>>();
private Class<T> interfaceClass;
private BasicServiceInterfaceConfig basicService;
private AtomicBoolean exported = new AtomicBoolean(false);
// service的用于注册的url,用于管理service注册的生命周期,url为regitry url,内部嵌套service url。
private ConcurrentHashSet<URL> registereUrls = new ConcurrentHashSet<URL>();
public static ConcurrentHashSet<String> getExistingServices() {
return existingServices;
}
public Class<?> getInterface() {
return interfaceClass;
}
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 List<MethodConfig> getMethods() {
return methods;
}
public void setMethods(MethodConfig methods) {
this.methods = Collections.singletonList(methods);
}
public void setMethods(List<MethodConfig> methods) {
this.methods = methods;
}
public boolean hasMethods() {
return this.methods != null && this.methods.size() > 0;
}
public T getRef() {
return ref;
}
public void setRef(T ref) {
this.ref = ref;
}
public List<Exporter<T>> getExporters() {
return Collections.unmodifiableList(exporters);
}
protected boolean serviceExists(URL url) {
return existingServices.contains(url.getIdentity());
}
public synchronized void export() {
if (exported.get()) {
LoggerUtil.warn(String.format("%s has already been expoted, so ignore the export request!", interfaceClass.getName()));
return;
}
checkInterfaceAndMethods(interfaceClass, methods);
List<URL> registryUrls = loadRegistryUrls();
if (registryUrls == null || registryUrls.size() == 0) {
throw new IllegalStateException("Should set registry config for service:" + interfaceClass.getName());
}
Map<String, Integer> protocolPorts = getProtocolAndPort();
for (ProtocolConfig protocolConfig : protocols) {
Integer port = protocolPorts.get(protocolConfig.getId());
if (port == null) {
throw new MotanServiceException(String.format("Unknow port in service:%s, protocol:%s", interfaceClass.getName(),
protocolConfig.getId()));
}
doExport(protocolConfig, port, registryUrls);
}
afterExport();
}
public synchronized void unexport() {
if (!exported.get()) {
return;
}
try {
ConfigHandler configHandler =
ExtensionLoader.getExtensionLoader(ConfigHandler.class).getExtension(MotanConstants.DEFAULT_VALUE);
configHandler.unexport(exporters, registereUrls);
} finally {
afterUnexport();
}
}
@SuppressWarnings("unchecked")
private void doExport(ProtocolConfig protocolConfig, int port, List<URL> registryURLs) {
String protocolName = protocolConfig.getName();
if (protocolName == null || protocolName.length() == 0) {
protocolName = URLParamType.protocol.getValue();
}
String hostAddress = host;
if (StringUtils.isBlank(hostAddress) && basicService != null) {
hostAddress = basicService.getHost();
}
if (NetUtils.isInvalidLocalHost(hostAddress)) {
hostAddress = getLocalHostAddress(registryURLs);
}
Map<String, String> map = new HashMap<String, String>();
map.put(URLParamType.nodeType.getName(), MotanConstants.NODE_TYPE_SERVICE);
map.put(URLParamType.refreshTimestamp.getName(), String.valueOf(System.currentTimeMillis()));
collectConfigParams(map, protocolConfig, basicService, extConfig, this);
collectMethodConfigParams(map, this.getMethods());
URL serviceUrl = new URL(protocolName, hostAddress, port, interfaceClass.getName(), map);
if (serviceExists(serviceUrl)) {
LoggerUtil.warn(String.format("%s configService is malformed, for same service (%s) already exists ", interfaceClass.getName(),
serviceUrl.getIdentity()));
throw new MotanFrameworkException(String.format("%s configService is malformed, for same service (%s) already exists ",
interfaceClass.getName(), serviceUrl.getIdentity()), MotanErrorMsgConstant.FRAMEWORK_INIT_ERROR);
}
List<URL> urls = new ArrayList<URL>();
// injvm 协议只支持注册到本地,其他协议可以注册到local、remote
if (MotanConstants.PROTOCOL_INJVM.equals(protocolConfig.getId())) {
URL localRegistryUrl = null;
for (URL ru : registryURLs) {
if (MotanConstants.REGISTRY_PROTOCOL_LOCAL.equals(ru.getProtocol())) {
localRegistryUrl = ru.createCopy();
break;
}
}
if (localRegistryUrl == null) {
localRegistryUrl =
new URL(MotanConstants.REGISTRY_PROTOCOL_LOCAL, hostAddress, MotanConstants.DEFAULT_INT_VALUE,
RegistryService.class.getName());
}
urls.add(localRegistryUrl);
} else {
for (URL ru : registryURLs) {
urls.add(ru.createCopy());
}
}
for (URL u : urls) {
u.addParameter(URLParamType.embed.getName(), StringTools.urlEncode(serviceUrl.toFullStr()));
registereUrls.add(u.createCopy());
}
ConfigHandler configHandler = ExtensionLoader.getExtensionLoader(ConfigHandler.class).getExtension(MotanConstants.DEFAULT_VALUE);
exporters.add(configHandler.export(interfaceClass, ref, urls));
initLocalAppInfo(serviceUrl);
}
private void afterExport() {
exported.set(true);
for (Exporter<T> ep : exporters) {
existingServices.add(ep.getProvider().getUrl().getIdentity());
}
}
private void afterUnexport() {
exported.set(false);
for (Exporter<T> ep : exporters) {
existingServices.remove(ep.getProvider().getUrl().getIdentity());
exporters.remove(ep);
}
exporters.clear();
registereUrls.clear();
}
@ConfigDesc(excluded = true)
public BasicServiceInterfaceConfig getBasicService() {
return basicService;
}
public void setBasicService(BasicServiceInterfaceConfig basicService) {
this.basicService = basicService;
}
public Map<String, Integer> getProtocolAndPort() {
if (StringUtils.isBlank(export)) {
throw new MotanServiceException("export should not empty in service config:" + interfaceClass.getName());
}
return ConfigUtil.parseExport(this.export);
}
@ConfigDesc(excluded = true)
public String getHost() {
return host;
}
public void setHost(String host) {
this.host = host;
}
public AtomicBoolean getExported() {
return exported;
}
public ConcurrentHashSet<URL> getRegistereUrls() {
return registereUrls;
}
}