class AbstractProtocol implements Protocol {
    protected ConcurrentHashMap<String, Exporter<?>> exporterMap = new ConcurrentHashMap<String, Exporter<?>>();

    public Map<String, Exporter<?>> getExporterMap() {
        return Collections.unmodifiableMap(exporterMap);
    }

    @SuppressWarnings("unchecked")
    @Override
    public <T> Exporter<T> export(Provider<T> provider, URL url) {
        if (url == null) {
            throw new MotanFrameworkException(this.getClass().getSimpleName() + " export Error: url is null",
                    MotanErrorMsgConstant.FRAMEWORK_INIT_ERROR);
        }

        if (provider == null) {
            throw new MotanFrameworkException(this.getClass().getSimpleName() + " export Error: provider is null, url=" + url,
                    MotanErrorMsgConstant.FRAMEWORK_INIT_ERROR);
        }

        String protocolKey = MotanFrameworkUtil.getProtocolKey(url);

        synchronized (exporterMap) {
            Exporter<T> exporter = (Exporter<T>) exporterMap.get(protocolKey);

            if (exporter != null) {
                throw new MotanFrameworkException(this.getClass().getSimpleName() + " export Error: service already exist, url=" + url,
                        MotanErrorMsgConstant.FRAMEWORK_INIT_ERROR);
            }

            exporter = createExporter(provider, url);
            exporter.init();

            protocolKey =  MotanFrameworkUtil.getProtocolKey(url);// rebuild protocolKey，maybe port change when using random port
            exporterMap.put(protocolKey, exporter);

            LoggerUtil.info(this.getClass().getSimpleName() + " export Success: url=" + url);

            return exporter;
        }


    }

    public <T> Referer<T> refer(Class<T> clz, URL url) {
        return refer(clz, url, url);
    }

    @Override
    public <T> Referer<T> refer(Class<T> clz, URL url, URL serviceUrl) {<FILL_FUNCTION_BODY>}

    protected abstract <T> Exporter<T> createExporter(Provider<T> provider, URL url);

    protected abstract <T> Referer<T> createReferer(Class<T> clz, URL url, URL serviceUrl);

    @Override
    public void destroy() {
        for (String key : exporterMap.keySet()) {
            Node node = exporterMap.remove(key);

            if (node != null) {
                try {
                    node.destroy();

                    LoggerUtil.info(this.getClass().getSimpleName() + " destroy node Success: " + node);
                } catch (Throwable t) {
                    LoggerUtil.error(this.getClass().getSimpleName() + " destroy Error", t);
                }
            }
        }
    }
}

        if (url == null) {
            throw new MotanFrameworkException(this.getClass().getSimpleName() + " refer Error: url is null",
                    MotanErrorMsgConstant.FRAMEWORK_INIT_ERROR);
        }

        if (clz == null) {
            throw new MotanFrameworkException(this.getClass().getSimpleName() + " refer Error: class is null, url=" + url,
                    MotanErrorMsgConstant.FRAMEWORK_INIT_ERROR);
        }
        long start = System.currentTimeMillis();
        Referer<T> referer = createReferer(clz, url, serviceUrl);
        referer.init();

        LoggerUtil.info(this.getClass().getSimpleName() + " refer Success: url=" + url + ", cost:" + (System.currentTimeMillis() - start));

        return referer;
    
<no_super_class>