/* * 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.registry.support; import com.weibo.api.motan.common.MotanConstants; import com.weibo.api.motan.common.URLParamType; import com.weibo.api.motan.registry.NotifyListener; import com.weibo.api.motan.registry.Registry; import com.weibo.api.motan.rpc.URL; import com.weibo.api.motan.switcher.SwitcherListener; import com.weibo.api.motan.util.ConcurrentHashSet; import com.weibo.api.motan.util.LoggerUtil; import com.weibo.api.motan.util.MotanSwitcherUtil; import java.util.*; import java.util.concurrent.ConcurrentHashMap; /** * <pre> * Abstract registry。 * * 对进出的url都进行createCopy保护,避免registry中的对象被修改,避免潜在的并发问题。 * * </pre> * * @author fishermen * @version V1.0 created at: 2013-5-28 */ public abstract class AbstractRegistry implements Registry { private ConcurrentHashMap<URL, Map<String, List<URL>>> subscribedCategoryResponses = new ConcurrentHashMap<URL, Map<String, List<URL>>>(); private URL registryUrl; private Set<URL> registeredServiceUrls = new ConcurrentHashSet<URL>(); protected String registryClassName = this.getClass().getSimpleName(); public AbstractRegistry(URL url) { this.registryUrl = url.createCopy(); // register a heartbeat switcher to perceive service state change and change available state MotanSwitcherUtil.registerSwitcherListener(MotanConstants.REGISTRY_HEARTBEAT_SWITCHER, new SwitcherListener() { @Override public void onValueChanged(String key, Boolean value) { if (key != null && value != null) { if (value) { available(null); } else { unavailable(null); } } } }); } @Override public void register(URL url) { if (url == null) { LoggerUtil.warn("[{}] register with malformed param, url is null", registryClassName); return; } LoggerUtil.info("[{}] Url ({}) will register to Registry [{}]", registryClassName, url, registryUrl.getIdentity()); doRegister(removeUnnecessaryParmas(url.createCopy())); registeredServiceUrls.add(url); // available if heartbeat switcher already open if (MotanSwitcherUtil.isOpen(MotanConstants.REGISTRY_HEARTBEAT_SWITCHER)) { available(url); } } @Override public void unregister(URL url) { if (url == null) { LoggerUtil.warn("[{}] unregister with malformed param, url is null", registryClassName); return; } LoggerUtil.info("[{}] Url ({}) will unregister to Registry [{}]", registryClassName, url, registryUrl.getIdentity()); doUnregister(removeUnnecessaryParmas(url.createCopy())); registeredServiceUrls.remove(url); } @Override public void subscribe(URL url, NotifyListener listener) { if (url == null || listener == null) { LoggerUtil.warn("[{}] subscribe with malformed param, url:{}, listener:{}", registryClassName, url, listener); return; } LoggerUtil.info("[{}] Listener ({}) will subscribe to url ({}) in Registry [{}]", registryClassName, listener, url, registryUrl.getIdentity()); doSubscribe(url.createCopy(), listener); } @Override public void unsubscribe(URL url, NotifyListener listener) { if (url == null || listener == null) { LoggerUtil.warn("[{}] unsubscribe with malformed param, url:{}, listener:{}", registryClassName, url, listener); return; } LoggerUtil.info("[{}] Listener ({}) will unsubscribe from url ({}) in Registry [{}]", registryClassName, listener, url, registryUrl.getIdentity()); doUnsubscribe(url.createCopy(), listener); } @SuppressWarnings("unchecked") @Override public List<URL> discover(URL url) { if (url == null) { LoggerUtil.warn("[{}] discover with malformed param, refUrl is null", registryClassName); return Collections.EMPTY_LIST; } url = url.createCopy(); List<URL> results = new ArrayList<URL>(); Map<String, List<URL>> categoryUrls = subscribedCategoryResponses.get(url); if (categoryUrls != null && categoryUrls.size() > 0) { for (List<URL> urls : categoryUrls.values()) { for (URL tempUrl : urls) { results.add(tempUrl.createCopy()); } } } else { List<URL> urlsDiscovered = doDiscover(url); if (urlsDiscovered != null) { for (URL u : urlsDiscovered) { results.add(u.createCopy()); } } } return results; } @Override public URL getUrl() { return registryUrl; } @Override public Collection<URL> getRegisteredServiceUrls() { return registeredServiceUrls; } @Override public void available(URL url) { LoggerUtil.info("[{}] Url ({}) will set to available to Registry [{}]", registryClassName, url, registryUrl.getIdentity()); if (url != null) { doAvailable(removeUnnecessaryParmas(url.createCopy())); } else { doAvailable(null); } } @Override public void unavailable(URL url) { LoggerUtil.info("[{}] Url ({}) will set to unavailable to Registry [{}]", registryClassName, url, registryUrl.getIdentity()); if (url != null) { doUnavailable(removeUnnecessaryParmas(url.createCopy())); } else { doUnavailable(null); } } protected List<URL> getCachedUrls(URL url) { Map<String, List<URL>> rsUrls = subscribedCategoryResponses.get(url); if (rsUrls == null || rsUrls.size() == 0) { return null; } List<URL> urls = new ArrayList<URL>(); for (List<URL> us : rsUrls.values()) { for (URL tempUrl : us) { urls.add(tempUrl.createCopy()); } } return urls; } protected void notify(URL refUrl, NotifyListener listener, List<URL> urls) { if (listener == null || urls == null) { return; } Map<String, List<URL>> nodeTypeUrlsInRs = new HashMap<String, List<URL>>(); for (URL surl : urls) { String nodeType = surl.getParameter(URLParamType.nodeType.getName(), URLParamType.nodeType.getValue()); List<URL> oneNodeTypeUrls = nodeTypeUrlsInRs.get(nodeType); if (oneNodeTypeUrls == null) { nodeTypeUrlsInRs.put(nodeType, new ArrayList<URL>()); oneNodeTypeUrls = nodeTypeUrlsInRs.get(nodeType); } oneNodeTypeUrls.add(surl); } Map<String, List<URL>> curls = subscribedCategoryResponses.get(refUrl); if (curls == null) { subscribedCategoryResponses.putIfAbsent(refUrl, new ConcurrentHashMap<String, List<URL>>()); curls = subscribedCategoryResponses.get(refUrl); } // refresh local urls cache for (String nodeType : nodeTypeUrlsInRs.keySet()) { curls.put(nodeType, nodeTypeUrlsInRs.get(nodeType)); } for (List<URL> us : nodeTypeUrlsInRs.values()) { listener.notify(getUrl(), us); } } /** * 移除不必提交到注册中心的参数。这些参数不需要被client端感知。 * * @param url */ private URL removeUnnecessaryParmas(URL url) { // codec参数不能提交到注册中心,如果client端没有对应的codec会导致client端不能正常请求。 url.getParameters().remove(URLParamType.codec.getName()); return url; } protected abstract void doRegister(URL url); protected abstract void doUnregister(URL url); protected abstract void doSubscribe(URL url, NotifyListener listener); protected abstract void doUnsubscribe(URL url, NotifyListener listener); protected abstract List<URL> doDiscover(URL url); protected abstract void doAvailable(URL url); protected abstract void doUnavailable(URL url); }