tmpBuf.put(tmp, 0, readBytes);
        return readBytes;
    }

package io.netty.buffer;

import io.netty.util.internal.ObjectPool;
import io.netty.util.internal.ObjectPool.Handle;
import io.netty.util.internal.ObjectPool.ObjectCreator;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.ByteBuffer;

final class PooledDirectByteBuf extends PooledByteBuf<ByteBuffer> {

    private static final ObjectPool<PooledDirectByteBuf> RECYCLER = ObjectPool.newPool(new ObjectCreator<PooledDirectByteBuf>() {

        @Override
        public PooledDirectByteBuf newObject(Handle<PooledDirectByteBuf> handle) {
            return new PooledDirectByteBuf(handle, 0);
        }
    });

    static PooledDirectByteBuf newInstance(int maxCapacity) {
        PooledDirectByteBuf buf = RECYCLER.get();
        buf.reuse(maxCapacity);
        return buf;
    }

    private PooledDirectByteBuf(Handle<PooledDirectByteBuf> recyclerHandle, int maxCapacity) {
        super(recyclerHandle, maxCapacity);
    }

    @Override
    protected ByteBuffer newInternalNioBuffer(ByteBuffer memory) {
        return memory.duplicate();
    }

    @Override
    public boolean isDirect() {
        return true;
    }

    @Override
    protected byte _getByte(int index) {
        return memory.get(idx(index));
    }

    @Override
    protected short _getShort(int index) {
        return memory.getShort(idx(index));
    }

    @Override
    protected short _getShortLE(int index) {
        return ByteBufUtil.swapShort(_getShort(index));
    }

    @Override
    protected int _getUnsignedMedium(int index) {
        index = idx(index);
        return (memory.get(index) & 0xff) << 16 | (memory.get(index + 1) & 0xff) << 8 | memory.get(index + 2) & 0xff;
    }

    @Override
    protected int _getUnsignedMediumLE(int index) {
        index = idx(index);
        return memory.get(index) & 0xff | (memory.get(index + 1) & 0xff) << 8 | (memory.get(index + 2) & 0xff) << 16;
    }

    @Override
    protected int _getInt(int index) {
        return memory.getInt(idx(index));
    }

    @Override
    protected int _getIntLE(int index) {
        return ByteBufUtil.swapInt(_getInt(index));
    }

    @Override
    protected long _getLong(int index) {
        return memory.getLong(idx(index));
    }

    @Override
    protected long _getLongLE(int index) {
        return ByteBufUtil.swapLong(_getLong(index));
    }

    @Override
    public ByteBuf getBytes(int index, ByteBuf dst, int dstIndex, int length) {
        checkDstIndex(index, length, dstIndex, dst.capacity());
        if (dst.hasArray()) {
            getBytes(index, dst.array(), dst.arrayOffset() + dstIndex, length);
        } else if (dst.nioBufferCount() > 0) {
            for (ByteBuffer bb : dst.nioBuffers(dstIndex, length)) {
                int bbLen = bb.remaining();
                getBytes(index, bb);
                index += bbLen;
            }
        } else {
            dst.setBytes(dstIndex, this, index, length);
        }
        return this;
    }

    @Override
    public ByteBuf getBytes(int index, byte[] dst, int dstIndex, int length) {
        checkDstIndex(index, length, dstIndex, dst.length);
        _internalNioBuffer(index, length, true).get(dst, dstIndex, length);
        return this;
    }

    @Override
    public ByteBuf readBytes(byte[] dst, int dstIndex, int length) {
        checkDstIndex(length, dstIndex, dst.length);
        _internalNioBuffer(readerIndex, length, false).get(dst, dstIndex, length);
        readerIndex += length;
        return this;
    }

    @Override
    public ByteBuf getBytes(int index, ByteBuffer dst) {
        dst.put(duplicateInternalNioBuffer(index, dst.remaining()));
        return this;
    }

    @Override
    public ByteBuf readBytes(ByteBuffer dst) {
        int length = dst.remaining();
        checkReadableBytes(length);
        dst.put(_internalNioBuffer(readerIndex, length, false));
        readerIndex += length;
        return this;
    }

    @Override
    public ByteBuf getBytes(int index, OutputStream out, int length) throws IOException {
        getBytes(index, out, length, false);
        return this;
    }

    private void getBytes(int index, OutputStream out, int length, boolean internal) throws IOException {
        checkIndex(index, length);
        if (length == 0) {
            return;
        }
        ByteBufUtil.readBytes(alloc(), internal ? internalNioBuffer() : memory.duplicate(), idx(index), length, out);
    }

    @Override
    public ByteBuf readBytes(OutputStream out, int length) throws IOException {
        checkReadableBytes(length);
        getBytes(readerIndex, out, length, true);
        readerIndex += length;
        return this;
    }

    @Override
    protected void _setByte(int index, int value) {
        memory.put(idx(index), (byte) value);
    }

    @Override
    protected void _setShort(int index, int value) {
        memory.putShort(idx(index), (short) value);
    }

    @Override
    protected void _setShortLE(int index, int value) {
        _setShort(index, ByteBufUtil.swapShort((short) value));
    }

    @Override
    protected void _setMedium(int index, int value) {
        index = idx(index);
        memory.put(index, (byte) (value >>> 16));
        memory.put(index + 1, (byte) (value >>> 8));
        memory.put(index + 2, (byte) value);
    }

    @Override
    protected void _setMediumLE(int index, int value) {
        index = idx(index);
        memory.put(index, (byte) value);
        memory.put(index + 1, (byte) (value >>> 8));
        memory.put(index + 2, (byte) (value >>> 16));
    }

    @Override
    protected void _setInt(int index, int value) {
        memory.putInt(idx(index), value);
    }

    @Override
    protected void _setIntLE(int index, int value) {
        _setInt(index, ByteBufUtil.swapInt(value));
    }

    @Override
    protected void _setLong(int index, long value) {
        memory.putLong(idx(index), value);
    }

    @Override
    protected void _setLongLE(int index, long value) {
        _setLong(index, ByteBufUtil.swapLong(value));
    }

    @Override
    public ByteBuf setBytes(int index, ByteBuf src, int srcIndex, int length) {
        checkSrcIndex(index, length, srcIndex, src.capacity());
        if (src.hasArray()) {
            setBytes(index, src.array(), src.arrayOffset() + srcIndex, length);
        } else if (src.nioBufferCount() > 0) {
            for (ByteBuffer bb : src.nioBuffers(srcIndex, length)) {
                int bbLen = bb.remaining();
                setBytes(index, bb);
                index += bbLen;
            }
        } else {
            src.getBytes(srcIndex, this, index, length);
        }
        return this;
    }

    @Override
    public ByteBuf setBytes(int index, byte[] src, int srcIndex, int length) {
        checkSrcIndex(index, length, srcIndex, src.length);
        _internalNioBuffer(index, length, false).put(src, srcIndex, length);
        return this;
    }

    @Override
    public ByteBuf setBytes(int index, ByteBuffer src) {
        int length = src.remaining();
        checkIndex(index, length);
        ByteBuffer tmpBuf = internalNioBuffer();
        if (src == tmpBuf) {
            src = src.duplicate();
        }
        index = idx(index);
        tmpBuf.limit(index + length).position(index);
        tmpBuf.put(src);
        return this;
    }

    @Override
    public int setBytes(int index, InputStream in, int length) throws IOException {
        checkIndex(index, length);
        byte[] tmp = ByteBufUtil.threadLocalTempArray(length);
        int readBytes = in.read(tmp, 0, length);
        if (readBytes <= 0) {
            return readBytes;
        }
        ByteBuffer tmpBuf = internalNioBuffer();
        tmpBuf.position(idx(index));

    @Override
    public ByteBuf copy(int index, int length) {
        checkIndex(index, length);
        ByteBuf copy = alloc().directBuffer(length, maxCapacity());
        return copy.writeBytes(this, index, length);
    }

    @Override
    public boolean hasArray() {
        return false;
    }

    @Override
    public byte[] array() {
        throw new UnsupportedOperationException("direct buffer");
    }

    @Override
    public int arrayOffset() {
        throw new UnsupportedOperationException("direct buffer");
    }

    @Override
    public boolean hasMemoryAddress() {
        return false;
    }

    @Override
    public long memoryAddress() {
        throw new UnsupportedOperationException();
    }
}


package io.netty.buffer;

import io.netty.util.internal.ObjectPool;
import io.netty.util.internal.ObjectPool.Handle;
import io.netty.util.internal.ObjectPool.ObjectCreator;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.ByteBuffer;

final class PooledDirectByteBuf extends PooledByteBuf<ByteBuffer> {

    private static final ObjectPool<PooledDirectByteBuf> RECYCLER = ObjectPool.newPool(new ObjectCreator<PooledDirectByteBuf>() {

        @Override
        public PooledDirectByteBuf newObject(Handle<PooledDirectByteBuf> handle) {
            return new PooledDirectByteBuf(handle, 0);
        }
    });

    static PooledDirectByteBuf newInstance(int maxCapacity) {
        PooledDirectByteBuf buf = RECYCLER.get();
        buf.reuse(maxCapacity);
        return buf;
    }

    private PooledDirectByteBuf(Handle<PooledDirectByteBuf> recyclerHandle, int maxCapacity) {
        super(recyclerHandle, maxCapacity);
    }

    @Override
    protected ByteBuffer newInternalNioBuffer(ByteBuffer memory) {
        return memory.duplicate();
    }

    @Override
    public boolean isDirect() {
        return true;
    }

    @Override
    protected byte _getByte(int index) {
        return memory.get(idx(index));
    }

    @Override
    protected short _getShort(int index) {
        return memory.getShort(idx(index));
    }

    @Override
    protected short _getShortLE(int index) {
        return ByteBufUtil.swapShort(_getShort(index));
    }

    @Override
    protected int _getUnsignedMedium(int index) {
        index = idx(index);
        return (memory.get(index) & 0xff) << 16 | (memory.get(index + 1) & 0xff) << 8 | memory.get(index + 2) & 0xff;
    }

    @Override
    protected int _getUnsignedMediumLE(int index) {
        index = idx(index);
        return memory.get(index) & 0xff | (memory.get(index + 1) & 0xff) << 8 | (memory.get(index + 2) & 0xff) << 16;
    }

    @Override
    protected int _getInt(int index) {
        return memory.getInt(idx(index));
    }

    @Override
    protected int _getIntLE(int index) {
        return ByteBufUtil.swapInt(_getInt(index));
    }

    @Override
    protected long _getLong(int index) {
        return memory.getLong(idx(index));
    }

    @Override
    protected long _getLongLE(int index) {
        return ByteBufUtil.swapLong(_getLong(index));
    }

    @Override
    public ByteBuf getBytes(int index, ByteBuf dst, int dstIndex, int length) {
        checkDstIndex(index, length, dstIndex, dst.capacity());
        if (dst.hasArray()) {
            getBytes(index, dst.array(), dst.arrayOffset() + dstIndex, length);
        } else if (dst.nioBufferCount() > 0) {
            for (ByteBuffer bb : dst.nioBuffers(dstIndex, length)) {
                int bbLen = bb.remaining();
                getBytes(index, bb);
                index += bbLen;
            }
        } else {
            dst.setBytes(dstIndex, this, index, length);
        }
        return this;
    }

    @Override
    public ByteBuf getBytes(int index, byte[] dst, int dstIndex, int length) {
        checkDstIndex(index, length, dstIndex, dst.length);
        _internalNioBuffer(index, length, true).get(dst, dstIndex, length);
        return this;
    }

    @Override
    public ByteBuf readBytes(byte[] dst, int dstIndex, int length) {
        checkDstIndex(length, dstIndex, dst.length);
        _internalNioBuffer(readerIndex, length, false).get(dst, dstIndex, length);
        readerIndex += length;
        return this;
    }

    @Override
    public ByteBuf getBytes(int index, ByteBuffer dst) {
        dst.put(duplicateInternalNioBuffer(index, dst.remaining()));
        return this;
    }

    @Override
    public ByteBuf readBytes(ByteBuffer dst) {
        int length = dst.remaining();
        checkReadableBytes(length);
        dst.put(_internalNioBuffer(readerIndex, length, false));
        readerIndex += length;
        return this;
    }

    @Override
    public ByteBuf getBytes(int index, OutputStream out, int length) throws IOException {
        getBytes(index, out, length, false);
        return this;
    }

    private void getBytes(int index, OutputStream out, int length, boolean internal) throws IOException {
        checkIndex(index, length);
        if (length == 0) {
            return;
        }
        ByteBufUtil.readBytes(alloc(), internal ? internalNioBuffer() : memory.duplicate(), idx(index), length, out);
    }

    @Override
    public ByteBuf readBytes(OutputStream out, int length) throws IOException {
        checkReadableBytes(length);
        getBytes(readerIndex, out, length, true);
        readerIndex += length;
        return this;
    }

    @Override
    protected void _setByte(int index, int value) {
        memory.put(idx(index), (byte) value);
    }

    @Override
    protected void _setShort(int index, int value) {
        memory.putShort(idx(index), (short) value);
    }

    @Override
    protected void _setShortLE(int index, int value) {
        _setShort(index, ByteBufUtil.swapShort((short) value));
    }

    @Override
    protected void _setMedium(int index, int value) {
        index = idx(index);
        memory.put(index, (byte) (value >>> 16));
        memory.put(index + 1, (b

        memory.put(index + 2, (byte) value);
    }

    @Override
    protected void _setMediumLE(int index, int value) {
        index = idx(index);
        memory.put(index, (byte) value);
        memory.put(index + 1, (byte) (value >>> 8));
        memory.put(index + 2, (byte) (value >>> 16));
    }

    @Override
    protected void _setInt(int index, int value) {
        memory.putInt(idx(index), value);
    }

    @Override
    protected void _setIntLE(int index, int value) {
        _setInt(index, ByteBufUtil.swapInt(value));
    }

    @Override
    protected void _setLong(int index, long value) {
        memory.putLong(idx(index), value);
    }

    @Override
    protected void _setLongLE(int index, long value) {
        _setLong(index, ByteBufUtil.swapLong(value));
    }

    @Override
    public ByteBuf setBytes(int index, ByteBuf src, int srcIndex, int length) {
        checkSrcIndex(index, length, srcIndex, src.capacity());
        if (src.hasArray()) {
            setBytes(index, src.array(), src.arrayOffset() + srcIndex, length);
        } else if (src.nioBufferCount() > 0) {
            for (ByteBuffer bb : src.nioBuffers(srcIndex, length)) {
                int bbLen = bb.remaining();
                setBytes(index, bb);
                index += bbLen;
            }
        } else {
            src.getBytes(srcIndex, this, index, length);
        }
        return this;
    }

    @Override
    public ByteBuf setBytes(int index, byte[] src, int srcIndex, int length) {
        checkSrcIndex(index, length, srcIndex, src.length);
        _internalNioBuffer(index, length, false).put(src, srcIndex, length);
        return this;
    }

    @Override
    public ByteBuf setBytes(int index, ByteBuffer src) {
        int length = src.remaining();
        checkIndex(index, length);
        ByteBuffer tmpBuf = internalNioBuffer();
        if (src == tmpBuf) {
            src = src.duplicate();
        }
        index = idx(index);
        tmpBuf.limit(index + length).position(index);
        tmpBuf.put(src);
        return this;
    }

    @Override
    public int setBytes(int index, InputStream in, int length) throws IOException {
        checkIndex(index, length);
        byte[] tmp = ByteBufUtil.threadLocalTempArray(length);
        int readBytes = in.read(tmp, 0, length);
        if (readBytes <= 0) {
            return readBytes;
        }
        ByteBuffer tmpBuf = internalNioBuffer();
        tmpBuf.position(idx(index));
        tmpBuf.put(tmp, 0, readBytes);
        return readBytes;
    }

    @Override
    public ByteBuf copy(int index, int length) {
        checkIndex(index, length);
        ByteBuf copy = alloc().directBuffer(length, maxCapacity());
        return copy.writeBytes(this, index, length);
    }

    @Override
    public boolean hasArray() {
        return false;
    }

    @Override
    public byte[] array() {
        throw new UnsupportedOperationException("direct buffer");
    }

    @Override
    public int arrayOffset() {
        throw new UnsupportedOperationException("direct buffer");
    }

    @Override
    public boolean hasMemoryAddress() {
        return false;
    }

    @Override
    public long memoryAddress() {
        throw new UnsupportedOperationException();
    }
}


if (dst.nioBufferCount() > 0) {
            for (ByteBuffer bb : dst.nioBuffers(dstIndex, length)) {
                int bbLen = bb.remaining();
                getBytes(index, bb);
                index += bbLen;
            }
        } else {
            dst.setBytes(dstIndex, this, index, length);
        }

package io.netty.buffer;

import io.netty.util.internal.ObjectPool;
import io.netty.util.internal.ObjectPool.Handle;
import io.netty.util.internal.ObjectPool.ObjectCreator;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.ByteBuffer;

final class PooledDirectByteBuf extends PooledByteBuf<ByteBuffer> {

    private static final ObjectPool<PooledDirectByteBuf> RECYCLER = ObjectPool.newPool(new ObjectCreator<PooledDirectByteBuf>() {

        @Override
        public PooledDirectByteBuf newObject(Handle<PooledDirectByteBuf> handle) {
            return new PooledDirectByteBuf(handle, 0);
        }
    });

    static PooledDirectByteBuf newInstance(int maxCapacity) {
        PooledDirectByteBuf buf = RECYCLER.get();
        buf.reuse(maxCapacity);
        return buf;
    }

    private PooledDirectByteBuf(Handle<PooledDirectByteBuf> recyclerHandle, int maxCapacity) {
        super(recyclerHandle, maxCapacity);
    }

    @Override
    protected ByteBuffer newInternalNioBuffer(ByteBuffer memory) {
        return memory.duplicate();
    }

    @Override
    public boolean isDirect() {
        return true;
    }

    @Override
    protected byte _getByte(int index) {
        return memory.get(idx(index));
    }

    @Override
    protected short _getShort(int index) {
        return memory.getShort(idx(index));
    }

    @Override
    protected short _getShortLE(int index) {
        return ByteBufUtil.swapShort(_getShort(index));
    }

    @Override
    protected int _getUnsignedMedium(int index) {
        index = idx(index);
        return (memory.get(index) & 0xff) << 16 | (memory.get(index + 1) & 0xff) << 8 | memory.get(index + 2) & 0xff;
    }

    @Override
    protected int _getUnsignedMediumLE(int index) {
        index = idx(index);
        return memory.get(index) & 0xff | (memory.get(index + 1) & 0xff) << 8 | (memory.get(index + 2) & 0xff) << 16;
    }

    @Override
    protected int _getInt(int index) {
        return memory.getInt(idx(index));
    }

    @Override
    protected int _getIntLE(int index) {
        return ByteBufUtil.swapInt(_getInt(index));
    }

    @Override
    protected long _getLong(int index) {
        return memory.getLong(idx(index));
    }

    @Override
    protected long _getLongLE(int index) {
        return ByteBufUtil.swapLong(_getLong(index));
    }

    @Override
    public ByteBuf getBytes(int index, ByteBuf dst, int dstIndex, int length) {
        checkDstIndex(index, length, dstIndex, dst.capacity());
        if (dst.hasArray()) {
            getBytes(index, dst.array(), dst.arrayOffset() + dstIndex, length);
        } else

        return this;
    }

    @Override
    public ByteBuf getBytes(int index, byte[] dst, int dstIndex, int length) {
        checkDstIndex(index, length, dstIndex, dst.length);
        _internalNioBuffer(index, length, true).get(dst, dstIndex, length);
        return this;
    }

    @Override
    public ByteBuf readBytes(byte[] dst, int dstIndex, int length) {
        checkDstIndex(length, dstIndex, dst.length);
        _internalNioBuffer(readerIndex, length, false).get(dst, dstIndex, length);
        readerIndex += length;
        return this;
    }

    @Override
    public ByteBuf getBytes(int index, ByteBuffer dst) {
        dst.put(duplicateInternalNioBuffer(index, dst.remaining()));
        return this;
    }

    @Override
    public ByteBuf readBytes(ByteBuffer dst) {
        int length = dst.remaining();
        checkReadableBytes(length);
        dst.put(_internalNioBuffer(readerIndex, length, false));
        readerIndex += length;
        return this;
    }

    @Override
    public ByteBuf getBytes(int index, OutputStream out, int length) throws IOException {
        getBytes(index, out, length, false);
        return this;
    }

    private void getBytes(int index, OutputStream out, int length, boolean internal) throws IOException {
        checkIndex(index, length);
        if (length == 0) {
            return;
        }
        ByteBufUtil.readBytes(alloc(), internal ? internalNioBuffer() : memory.duplicate(), idx(index), length, out);
    }

    @Override
    public ByteBuf readBytes(OutputStream out, int length) throws IOException {
        checkReadableBytes(length);
        getBytes(readerIndex, out, length, true);
        readerIndex += length;
        return this;
    }

    @Override
    protected void _setByte(int index, int value) {
        memory.put(idx(index), (byte) value);
    }

    @Override
    protected void _setShort(int index, int value) {
        memory.putShort(idx(index), (short) value);
    }

    @Override
    protected void _setShortLE(int index, int value) {
        _setShort(index, ByteBufUtil.swapShort((short) value));
    }

    @Override
    protected void _setMedium(int index, int value) {
        index = idx(index);
        memory.put(index, (byte) (value >>> 16));
        memory.put(index + 1, (byte) (value >>> 8));
        memory.put(index + 2, (byte) value);
    }

    @Override
    protected void _setMediumLE(int index, int value) {
        index = idx(index);
        memory.put(index, (byte) value);
        memory.put(index + 1, (byte) (value >>> 8));
        memory.put(index + 2, (byte) (value >>> 16));
    }

    @Override
    protected void _setInt(int index, int value) {
        memory.putInt(idx(index), value);
    }

    @Override
    protected void _setIntLE(int index, int value) {
        _setInt(index, ByteBufUtil.swapInt(value));
    }

    @Override
    protected void _setLong(int index, long value) {
        memory.putLong(idx(index), value);
    }

    @Override
    protected void _setLongLE(int index, long value) {
        _setLong(index, ByteBufUtil.swapLong(value));
    }

    @Override
    public ByteBuf setBytes(int index, ByteBuf src, int srcIndex, int length) {
        checkSrcIndex(index, length, srcIndex, src.capacity());
        if (src.hasArray()) {
            setBytes(index, src.array(), src.arrayOffset() + srcIndex, length);
        } else if (src.nioBufferCount() > 0) {
            for (ByteBuffer bb : src.nioBuffers(srcIndex, length)) {
                int bbLen = bb.remaining();
                setBytes(index, bb);
                index += bbLen;
            }
        } else {
            src.getBytes(srcIndex, this, index, length);
        }
        return this;
    }

    @Override
    public ByteBuf setBytes(int index, byte[] src, int srcIndex, int length) {
        checkSrcIndex(index, length, srcIndex, src.length);
        _internalNioBuffer(index, length, false).put(src, srcIndex, length);
        return this;
    }

    @Override
    public ByteBuf setBytes(int index, ByteBuffer src) {
        int length = src.remaining();
        checkIndex(index, length);
        ByteBuffer tmpBuf = internalNioBuffer();
        if (src == tmpBuf) {
            src = src.duplicate();
        }
        index = idx(index);
        tmpBuf.limit(index + length).position(index);
        tmpBuf.put(src);
        return this;
    }

    @Override
    public int setBytes(int index, InputStream in, int length) throws IOException {
        checkIndex(index, length);
        byte[] tmp = ByteBufUtil.threadLocalTempArray(length);
        int readBytes = in.read(tmp, 0, length);
        if (readBytes <= 0) {
            return readBytes;
        }
        ByteBuffer tmpBuf = internalNioBuffer();
        tmpBuf.position(idx(index));
        tmpBuf.put(tmp, 0, readBytes);
        return readBytes;
    }

    @Override
    public ByteBuf copy(int index, int length) {
        checkIndex(index, length);
        ByteBuf copy = alloc().directBuffer(length, maxCapacity());
        return copy.writeBytes(this, index, length);
    }

    @Override
    public boolean hasArray() {
        return false;
    }

    @Override
    public byte[] array() {
        throw new UnsupportedOperationException("direct buffer");
    }

    @Override
    public int arrayOffset() {
        throw new UnsupportedOperationException("direct buffer");
    }

    @Override
    public boolean hasMemoryAddress() {
        return false;
    }

    @Override
    public long memoryAddress() {
        throw new UnsupportedOperationException();
    }
}


private static Set<URLDefinition> collectExtensionUrls(String resourceName, ClassLoader classLoader)

package ltd.beihu.core.tools.spi;

import ltd.beihu.core.tools.utils.Assert;
import ltd.beihu.core.tools.utils.IDUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

public class ServiceLoader {

    private static final Logger LOGGER = LoggerFactory.getLogger(ServiceLoader.class);

    private static final String LTS_DIRECTORY = "META-INF/ceres/";

    private static final String LTS_INTERNAL = "internal";

    private static final String LTS_INTERNAL_DIRECTORY = LTS_DIRECTORY + LTS_INTERNAL + "/";

    private static final String DEFAULT_IDENTITY = IDUtils.UUID();

    private static final ConcurrentMap<Class<?>, ServiceProvider> serviceMap = new ConcurrentHashMap<Class<?>, ServiceProvider>();

    private static final ConcurrentMap<IdentityUniqueKey, Object> cachedObjectMap = new ConcurrentHashMap<IdentityUniqueKey, Object>();

    public static <T> T loadDefault(Class<T> clazz) {
        return load(clazz, "");
    }

    public static <T> T load(Class<T> clazz, String name) {
        return load(clazz, name, DEFAULT_IDENTITY);
    }

    @SuppressWarnings("unchecked")
    public static <T> T load(Class<T> clazz, String name, String identity) {
        try {
            ServiceProvider serviceProvider = getServiceProvider(clazz);
            if (StringUtils.isEmpty(name)) {
                // 加载默认的
                name = serviceProvider.defaultName;
            }
            ServiceDefinition definition = serviceProvider.nameMaps.get(name);
            if (definition == null) {
                throw new IllegalStateException("Service loader could not load name:" + name + "  class:" + clazz.getName() + "'s ServiceProvider from '" + LTS_DIRECTORY + "' or '" + LTS_INTERNAL_DIRECTORY + "' It may be empty or does not exist.");
            }
            // 用来保证每个节点都是一个各自的对象
            IdentityUniqueKey uniqueKey = new IdentityUniqueKey(identity, definition);
            Object obj = cachedObjectMap.get(uniqueKey);
            if (obj != null) {
                return (T) obj;
            }
            synchronized (definition) {
                obj = cachedObjectMap.get(uniqueKey);
                if (obj != null) {
                    return (T) obj;
                }
                String className = definition.clazz;
                ClassLoader classLoader = definition.classLoader;
                T srv = clazz.cast(ClassLoaderUtil.newInstance(classLoader, className));
                cachedObjectMap.putIfAbsent(uniqueKey, srv);
                return srv;
            }
        } catch (Exception e) {
            throw new IllegalStateException("Service loader could not load name:" + name + "  class:" + clazz.getName() + "'s ServiceProvider from '" + LTS_DIRECTORY + "' or '" + LTS_INTERNAL_DIRECTORY + "' It may be empty or does not exist.");
        }
    }

    private static ServiceProvider getServiceProvider(Class<?> clazz) {
        ServiceProvider serviceProvider = serviceMap.get(clazz);
        if (serviceProvider == null) {
            getServiceProviders(clazz);
            serviceProvider = serviceMap.get(clazz);
        }
        return serviceProvider;
    }

    public static Set<String> getServiceProviders(final Class<?> clazz) {
        if (clazz == null)
            throw new IllegalArgumentException("type == null");
        if (!clazz.isInterface()) {
            throw new IllegalArgumentException(" type(" + clazz + ") is not interface!");
        }
        if (!clazz.isAnnotationPresent(SPI.class)) {
            throw new IllegalArgumentException("type(" + clazz + ") is not extension, because WITHOUT @" + SPI.class.getSimpleName() + " Annotation!");
        }
        SPI spi = clazz.getAnnotation(SPI.class);
        String defaultName = spi.dftValue();
        String dynamicConfigKey = spi.key();
        final Set<URLDefinition> urlDefinitions = new HashSet<URLDefinition>();
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        urlDefinitions.addAll(collectExtensionUrls(LTS_DIRECTORY + clazz.getName(), classLoader));
        urlDefinitions.addAll(collectExtensionUrls(LTS_INTERNAL_DIRECTORY + clazz.getName(), classLoader));
        final ConcurrentMap<String, ServiceDefinition> serviceDefinitions = new ConcurrentHashMap<String, ServiceDefinition>();
        for (URLDefinition urlDefinition : urlDefinitions) {
            serviceDefinitions.putAll(parse(urlDefinition));
        }
        if (serviceDefinitions.isEmpty()) {
            throw new IllegalStateException("Service loader could not load " + clazz.getName() + "'s ServiceProvider from '" + LTS_DIRECTORY + "' or '" + LTS_INTERNAL_DIRECTORY + "' It may be empty or does not exist.");
        }
        ServiceProvider serviceProvider = new ServiceProvider(clazz, dynamicConfigKey, defaultName, serviceDefinitions);
        // 先移除
        serviceMap.remove(clazz);
        serviceMap.put(clazz, serviceProvider);
        return serviceDefinitions.keySet();
    }

    private static Map<String, ServiceDefinition> parse(URLDefinition urlDefinition) {
        final Map<String, ServiceDefinition> nameClassMap = new HashMap<String, ServiceDefinition>();
        try {
            BufferedReader r = null;
            try {
                URL url = urlDefinition.uri.toURL();
                r = new BufferedReader(new InputStreamReader(url.openStream(), "UTF-8"));
                while (true) {
                    String line = r.readLine();
                    if (line == null) {
                        break;
                    }
                    int comment = line.indexOf('#');
                    if (comment >= 0) {
                        line = line.substring(0, comment);
                    }
                    line = line.trim();
                    if (line.length() == 0) {
                        continue;
                    }
                    int i = line.indexOf('=');
                    if (i > 0) {
                        String name = line.substring(0, i).trim();
                        String clazz = line.substring(i + 1).trim();
                        nameClassMap.put(name, new ServiceDefinition(name, clazz, urlDefinition.classLoader));
                    }
                }
            } finally {
                if (r != null) {
                    r.close();
                }
            }
        } catch (Exception e) {
            LOGGER.error("parse " + urlDefinition.uri + " error:" + e.getMessage(), e);
        }
        return nameClassMap;
    }

{
        try {
            final Enumeration<URL> configs;
            if (classLoader != null) {
                configs = classLoader.getResources(resourceName);
            } else {
                configs = ClassLoader.getSystemResources(resourceName);
            }
            Set<URLDefinition> urlDefinitions = new HashSet<URLDefinition>();
            while (configs.hasMoreElements()) {
                URL url = configs.nextElement();
                final URI uri = url.toURI();
                ClassLoader highestClassLoader = findHighestReachableClassLoader(url, classLoader, resourceName);
                urlDefinitions.add(new URLDefinition(uri, highestClassLoader));
            }
            return urlDefinitions;
        } catch (Exception e) {
            LOGGER.error(e.getMessage(), e);
        }
        return Collections.emptySet();
    }

    private static ClassLoader findHighestReachableClassLoader(URL url, ClassLoader classLoader, String resourceName) {
        if (classLoader.getParent() == null) {
            return classLoader;
        }
        ClassLoader highestClassLoader = classLoader;
        ClassLoader current = classLoader;
        while (current.getParent() != null) {
            ClassLoader parent = current.getParent();
            try {
                Enumeration<URL> resources = parent.getResources(resourceName);
                if (resources != null) {
                    while (resources.hasMoreElements()) {
                        URL resourceURL = resources.nextElement();
                        if (url.toURI().equals(resourceURL.toURI())) {
                            highestClassLoader = parent;
                        }
                    }
                }
            } catch (IOException ignore) {
            } catch (URISyntaxException ignore) {
            }
            current = current.getParent();
        }
        return highestClassLoader;
    }

    private static final class URLDefinition {

        private final URI uri;

        private final ClassLoader classLoader;

        private URLDefinition(URI url, ClassLoader classLoader) {
            this.uri = url;
            this.classLoader = classLoader;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) {
                return true;
            }
            if (o == null || getClass() != o.getClass()) {
                return false;
            }
            URLDefinition that = (URLDefinition) o;
            return uri != null ? uri.equals(that.uri) : that.uri == null;
        }

        @Override
        public int hashCode() {
            return uri != null ? uri.hashCode() : 0;
        }
    }

    private static final class ServiceDefinition {

        private final String name;

        private final String clazz;

        private final ClassLoader classLoader;

        private ServiceDefinition(String name, String clazz, ClassLoader classLoader) {
            Assert.notNull(name, "name");
            Assert.notNull(clazz, "clazz");
            Assert.notNull(classLoader, "classLoader");
            this.name = name;
            this.clazz = clazz;
            this.classLoader = classLoader;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o)
                return true;
            if (o == null || getClass() != o.getClass())
                return false;
            ServiceDefinition that = (ServiceDefinition) o;
            if (name != null ? !name.equals(that.name) : that.name != null)
                return false;
            if (clazz != null ? !clazz.equals(that.clazz) : that.clazz != null)
                return false;
            return classLoader != null ? classLoader.equals(that.classLoader) : that.classLoader == null;
        }

        @Override
        public int hashCode() {
            int result = name != null ? name.hashCode() : 0;
            result = 31 * result + (clazz != null ? clazz.hashCode() : 0);
            result = 31 * result + (classLoader != null ? classLoader.hashCode() : 0);
            return result;
        }
    }

    private static class IdentityUniqueKey {

        private String identity;

        private ServiceDefinition definition;

        public IdentityUniqueKey(String identity, ServiceDefinition definition) {
            this.identity = identity;
            this.definition = definition;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o)
                return true;
            if (o == null || getClass() != o.getClass())
                return false;
            IdentityUniqueKey that = (IdentityUniqueKey) o;
            if (identity != null ? !identity.equals(that.identity) : that.identity != null)
                return false;
            return definition != null ? definition.equals(that.definition) : that.definition == null;
        }

        @Override
        public int hashCode() {
            int result = identity != null ? identity.hashCode() : 0;
            result = 31 * result + (definition != null ? definition.hashCode() : 0);
            return result;
        }
    }

    private static final class ServiceProvider {

        private final Class<?> clazz;

        private final String defaultName;

        private final String dynamicConfigKey;

        private final ConcurrentMap<String, ServiceDefinition> nameMaps;

        public ServiceProvider(Class<?> clazz, String dynamicConfigKey, String defaultName, ConcurrentMap<String, ServiceDefinition> nameMaps) {
            this.clazz = clazz;
            this.dynamicConfigKey = dynamicConfigKey;
            this.defaultName = defaultName;
            this.nameMaps = nameMaps;
        }
    }
}


package ltd.beihu.core.tools.spi;

import ltd.beihu.core.tools.utils.Assert;
import ltd.beihu.core.tools.utils.IDUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

public class ServiceLoader {

    private static final Logger LOGGER = LoggerFactory.getLogger(ServiceLoader.class);

    private static final String LTS_DIRECTORY = "META-INF/ceres/";

    private static final String LTS_INTERNAL = "internal";

    private static final String LTS_INTERNAL_DIRECTORY = LTS_DIRECTORY + LTS_INTERNAL + "/";

    private static final String DEFAULT_IDENTITY = IDUtils.UUID();

    private static final ConcurrentMap<Class<?>, ServiceProvider> serviceMap = new ConcurrentHashMap<Class<?>, ServiceProvider>();

    private static final ConcurrentMap<IdentityUniqueKey, Object> cachedObjectMap = new ConcurrentHashMap<IdentityUniqueKey, Object>();

    public static <T> T loadDefault(Class<T> clazz)

    public static <T> T load(Class<T> clazz, String name) {
        return load(clazz, name, DEFAULT_IDENTITY);
    }

    @SuppressWarnings("unchecked")
    public static <T> T load(Class<T> clazz, String name, String identity) {
        try {
            ServiceProvider serviceProvider = getServiceProvider(clazz);
            if (StringUtils.isEmpty(name)) {
                // 加载默认的
                name = serviceProvider.defaultName;
            }
            ServiceDefinition definition = serviceProvider.nameMaps.get(name);
            if (definition == null) {
                throw new IllegalStateException("Service loader could not load name:" + name + "  class:" + clazz.getName() + "'s ServiceProvider from '" + LTS_DIRECTORY + "' or '" + LTS_INTERNAL_DIRECTORY + "' It may be empty or does not exist.");
            }
            // 用来保证每个节点都是一个各自的对象
            IdentityUniqueKey uniqueKey = new IdentityUniqueKey(identity, definition);
            Object obj = cachedObjectMap.get(uniqueKey);
            if (obj != null) {
                return (T) obj;
            }
            synchronized (definition) {
                obj = cachedObjectMap.get(uniqueKey);
                if (obj != null) {
                    return (T) obj;
                }
                String className = definition.clazz;
                ClassLoader classLoader = definition.classLoader;
                T srv = clazz.cast(ClassLoaderUtil.newInstance(classLoader, className));
                cachedObjectMap.putIfAbsent(uniqueKey, srv);
                return srv;
            }
        } catch (Exception e) {
            throw new IllegalStateException("Service loader could not load name:" + name + "  class:" + clazz.getName() + "'s ServiceProvider from '" + LTS_DIRECTORY + "' or '" + LTS_INTERNAL_DIRECTORY + "' It may be empty or does not exist.");
        }
    }

    private static ServiceProvider getServiceProvider(Class<?> clazz) {
        ServiceProvider serviceProvider = serviceMap.get(clazz);
        if (serviceProvider == null) {
            getServiceProviders(clazz);
            serviceProvider = serviceMap.get(clazz);
        }
        return serviceProvider;
    }

    public static Set<String> getServiceProviders(final Class<?> clazz) {
        if (clazz == null)
            throw new IllegalArgumentException("type == null");
        if (!clazz.isInterface()) {
            throw new IllegalArgumentException(" type(" + clazz + ") is not interface!");
        }
        if (!clazz.isAnnotationPresent(SPI.class)) {
            throw new IllegalArgumentException("type(" + clazz + ") is not extension, because WITHOUT @" + SPI.class.getSimpleName() + " Annotation!");
        }
        SPI spi = clazz.getAnnotation(SPI.class);
        String defaultName = spi.dftValue();
        String dynamicConfigKey = spi.key();
        final Set<URLDefinition> urlDefinitions = new HashSet<URLDefinition>();
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        urlDefinitions.addAll(collectExtensionUrls(LTS_DIRECTORY + clazz.getName(), classLoader));
        urlDefinitions.addAll(collectExtensionUrls(LTS_INTERNAL_DIRECTORY + clazz.getName(), classLoader));
        final ConcurrentMap<String, ServiceDefinition> serviceDefinitions = new ConcurrentHashMap<String, ServiceDefinition>();
        for (URLDefinition urlDefinition : urlDefinitions) {
            serviceDefinitions.putAll(parse(urlDefinition));
        }
        if (serviceDefinitions.isEmpty()) {
            throw new IllegalStateException("Service loader could not load " + clazz.getName() + "'s ServiceProvider from '" + LTS_DIRECTORY + "' or '" + LTS_INTERNAL_DIRECTORY + "' It may be empty or does not exist.");
        }
        ServiceProvider serviceProvider = new ServiceProvider(clazz, dynamicConfigKey, defaultName, serviceDefinitions);
        // 先移除
        serviceMap.remove(clazz);
        serviceMap.put(clazz, serviceProvider);
        return serviceDefinitions.keySet();
    }

    private static Map<String, ServiceDefinition> parse(URLDefinition urlDefinition) {
        final Map<String, ServiceDefinition> nameClassMap = new HashMap<String, ServiceDefinition>();
        try {
            BufferedReader r = null;
            try {
                URL url = urlDefinition.uri.toURL();
                r = new BufferedReader(new InputStreamReader(url.openStream(), "UTF-8"));
                while (true) {
                    String line = r.readLine();
                    if (line == null) {
                        break;
                    }
                    int comment = line.indexOf('#');
                    if (comment >= 0) {
                        line = line.substring(0, comment);
                    }
                    line = line.trim();
                    if (line.length() == 0) {
                        continue;
                    }
                    int i = line.indexOf('=');
                    if (i > 0) {
                        String name = line.substring(0, i).trim();
                        String clazz = line.substring(i + 1).trim();
                        nameClassMap.put(name, new ServiceDefinition(name, clazz, urlDefinition.classLoader));
                    }
                }
            } finally {
                if (r != null) {
                    r.close();
                }
            }
        } catch (Exception e) {
            LOGGER.error("parse " + urlDefinition.uri + " error:" + e.getMessage(), e);
        }
        return nameClassMap;
    }

    private static Set<URLDefinition> collectExtensionUrls(String resourceName, ClassLoader classLoader) {
        try {
            final Enumeration<URL> configs;
            if (classLoader != null) {
                configs = classLoader.getResources(resourceName);
            } else {
                configs = ClassLoader.getSystemResources(resourceName);
            }
            Set<URLDefinition> urlDefinitions = new HashSet<URLDefinition>();
            while (configs.hasMoreElements()) {
                URL url = configs.nextElement();
                final URI uri = url.toURI();
                ClassLoader highestClassLoader = findHighestReachableClassLoader(url, classLoader, resourceName);
                urlDefinitions.add(new URLDefinition(uri, highestClassLoader));
            }
            return urlDefinitions;
        } catch (Exception e) {
            LOGGER.error(e.getMessage(), e);
        }
        return Collections.emptySet();
    }

    private static ClassLoader findHighestReachableClassLoader(URL url, ClassLoader classLoader, String resourceName) {
        if (classLoader.getParent() == null) {
            return classLoader;
        }
        ClassLoader highestClassLoader = classLoader;
        ClassLoader current = classLoader;
        while (current.getParent() != null) {
            ClassLoader parent = current.getParent();
            try {
                Enumeration<URL> resources = parent.getResources(resourceName);
                if (resources != null) {
                    while (resources.hasMoreElements()) {
                        URL resourceURL = resources.nextElement();
                        if (url.toURI().equals(resourceURL.toURI())) {
                            highestClassLoader = parent;
                        }
                    }
                }
            } catch (IOException ignore) {
            } catch (URISyntaxException ignore) {
            }
            current = current.getParent();
        }
        return highestClassLoader;
    }

    private static final class URLDefinition {

        private final URI uri;

        private final ClassLoader classLoader;

        private URLDefinition(URI url, ClassLoader classLoader) {
            this.uri = url;
            this.classLoader = classLoader;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) {
                return true;
            }
            if (o == null || getClass() != o.getClass()) {
                return false;
            }
            URLDefinition that = (URLDefinition) o;
            return uri != null ? uri.equals(that.uri) : that.uri == null;
        }

        @Override
        public int hashCode() {
            return uri != null ? uri.hashCode() : 0;
        }
    }

    private static final class ServiceDefinition {

        private final String name;

        private final String clazz;

        private final ClassLoader classLoader;

        private ServiceDefinition(String name, String clazz, ClassLoader classLoader) {
            Assert.notNull(name, "name");
            Assert.notNull(clazz, "clazz");
            Assert.notNull(classLoader, "classLoader");
            this.name = name;
            this.clazz = clazz;
            this.classLoader = classLoader;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o)
                return true;
            if (o == null || getClass() != o.getClass())
                return false;
            ServiceDefinition that = (ServiceDefinition) o;
            if (name != null ? !name.equals(that.name) : that.name != null)
                return false;
            if (clazz != null ? !clazz.equals(that.clazz) : that.clazz != null)
                return false;
            return classLoader != null ? classLoader.equals(that.classLoader) : that.classLoader == null;
        }

        @Override
        public int hashCode() {
            int result = name != null ? name.hashCode() : 0;
            result = 31 * result + (clazz != null ? clazz.hashCode() : 0);
            result = 31 * result + (classLoader != null ? classLoader.hashCode() : 0);
            return result;
        }
    }

    private static class IdentityUniqueKey {

        private String identity;

        private ServiceDefinition definition;

        public IdentityUniqueKey(String identity, ServiceDefinition definition) {
            this.identity = identity;
            this.definition = definition;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o)
                return true;
            if (o == null || getClass() != o.getClass())
                return false;
            IdentityUniqueKey that = (IdentityUniqueKey) o;
            if (identity != null ? !identity.equals(that.identity) : that.identity != null)
                return false;
            return definition != null ? definition.equals(that.definition) : that.definition == null;
        }

        @Override
        public int hashCode() {
            int result = identity != null ? identity.hashCode() : 0;
            result = 31 * result + (definition != null ? definition.hashCode() : 0);
            return result;
        }
    }

    private static final class ServiceProvider {

        private final Class<?> clazz;

        private final String defaultName;

        private final String dynamicConfigKey;

        private final ConcurrentMap<String, ServiceDefinition> nameMaps;

        public ServiceProvider(Class<?> clazz, String dynamicConfigKey, String defaultName, ConcurrentMap<String, ServiceDefinition> nameMaps) {
            this.clazz = clazz;
            this.dynamicConfigKey = dynamicConfigKey;
            this.defaultName = defaultName;
            this.nameMaps = nameMaps;
        }
    }
}


        if (clazz == null)
            throw new IllegalArgumentException("type == null");
        if (!clazz.isInterface()) {
            throw new IllegalArgumentException(" type(" + clazz + ") is not interface!");
        }
        if (!clazz.isAnnotationPresent(SPI.class)) {
            throw new IllegalArgumentException("type(" + clazz + ") is not extension, because WITHOUT @" + SPI.class.getSimpleName() + " Annotation!");
        }
        SPI spi = clazz.getAnnotation(SPI.class);
        String defaultName = spi.dftValue();
        String dynamicConfigKey = spi.key();

package ltd.beihu.core.tools.spi;

import ltd.beihu.core.tools.utils.Assert;
import ltd.beihu.core.tools.utils.IDUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

public class ServiceLoader {

    private static final Logger LOGGER = LoggerFactory.getLogger(ServiceLoader.class);

    private static final String LTS_DIRECTORY = "META-INF/ceres/";

    private static final String LTS_INTERNAL = "internal";

    private static final String LTS_INTERNAL_DIRECTORY = LTS_DIRECTORY + LTS_INTERNAL + "/";

    private static final String DEFAULT_IDENTITY = IDUtils.UUID();

    private static final ConcurrentMap<Class<?>, ServiceProvider> serviceMap = new ConcurrentHashMap<Class<?>, ServiceProvider>();

    private static final ConcurrentMap<IdentityUniqueKey, Object> cachedObjectMap = new ConcurrentHashMap<IdentityUniqueKey, Object>();

    public static <T> T loadDefault(Class<T> clazz) {
        return load(clazz, "");
    }

    public static <T> T load(Class<T> clazz, String name) {
        return load(clazz, name, DEFAULT_IDENTITY);
    }

    @SuppressWarnings("unchecked")
    public static <T> T load(Class<T> clazz, String name, String identity) {
        try {
            ServiceProvider serviceProvider = getServiceProvider(clazz);
            if (StringUtils.isEmpty(name)) {
                // 加载默认的
                name = serviceProvider.defaultName;
            }
            ServiceDefinition definition = serviceProvider.nameMaps.get(name);
            if (definition == null) {
                throw new IllegalStateException("Service loader could not load name:" + name + "  class:" + clazz.getName() + "'s ServiceProvider from '" + LTS_DIRECTORY + "' or '" + LTS_INTERNAL_DIRECTORY + "' It may be empty or does not exist.");
            }
            // 用来保证每个节点都是一个各自的对象
            IdentityUniqueKey uniqueKey = new IdentityUniqueKey(identity, definition);
            Object obj = cachedObjectMap.get(uniqueKey);
            if (obj != null) {
                return (T) obj;
            }
            synchronized (definition) {
                obj = cachedObjectMap.get(uniqueKey);
                if (obj != null) {
                    return (T) obj;
                }
                String className = definition.clazz;
                ClassLoader classLoader = definition.classLoader;
                T srv = clazz.cast(ClassLoaderUtil.newInstance(classLoader, className));
                cachedObjectMap.putIfAbsent(uniqueKey, srv);
                return srv;
            }
        } catch (Exception e) {
            throw new IllegalStateException("Service loader could not load name:" + name + "  class:" + clazz.getName() + "'s ServiceProvider from '" + LTS_DIRECTORY + "' or '" + LTS_INTERNAL_DIRECTORY + "' It may be empty or does not exist.");
        }
    }

    private static ServiceProvider getServiceProvider(Class<?> clazz) {
        ServiceProvider serviceProvider = serviceMap.get(clazz);
        if (serviceProvider == null) {
            getServiceProviders(clazz);
            serviceProvider = serviceMap.get(clazz);
        }
        return serviceProvider;
    }

    public static Set<String> getServiceProviders(final Class<?> clazz) {

        final Set<URLDefinition> urlDefinitions = new HashSet<URLDefinition>();
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        urlDefinitions.addAll(collectExtensionUrls(LTS_DIRECTORY + clazz.getName(), classLoader));
        urlDefinitions.addAll(collectExtensionUrls(LTS_INTERNAL_DIRECTORY + clazz.getName(), classLoader));
        final ConcurrentMap<String, ServiceDefinition> serviceDefinitions = new ConcurrentHashMap<String, ServiceDefinition>();
        for (URLDefinition urlDefinition : urlDefinitions) {
            serviceDefinitions.putAll(parse(urlDefinition));
        }
        if (serviceDefinitions.isEmpty()) {
            throw new IllegalStateException("Service loader could not load " + clazz.getName() + "'s ServiceProvider from '" + LTS_DIRECTORY + "' or '" + LTS_INTERNAL_DIRECTORY + "' It may be empty or does not exist.");
        }
        ServiceProvider serviceProvider = new ServiceProvider(clazz, dynamicConfigKey, defaultName, serviceDefinitions);
        // 先移除
        serviceMap.remove(clazz);
        serviceMap.put(clazz, serviceProvider);
        return serviceDefinitions.keySet();
    }

    private static Map<String, ServiceDefinition> parse(URLDefinition urlDefinition) {
        final Map<String, ServiceDefinition> nameClassMap = new HashMap<String, ServiceDefinition>();
        try {
            BufferedReader r = null;
            try {
                URL url = urlDefinition.uri.toURL();
                r = new BufferedReader(new InputStreamReader(url.openStream(), "UTF-8"));
                while (true) {
                    String line = r.readLine();
                    if (line == null) {
                        break;
                    }
                    int comment = line.indexOf('#');
                    if (comment >= 0) {
                        line = line.substring(0, comment);
                    }
                    line = line.trim();
                    if (line.length() == 0) {
                        continue;
                    }
                    int i = line.indexOf('=');
                    if (i > 0) {
                        String name = line.substring(0, i).trim();
                        String clazz = line.substring(i + 1).trim();
                        nameClassMap.put(name, new ServiceDefinition(name, clazz, urlDefinition.classLoader));
                    }
                }
            } finally {
                if (r != null) {
                    r.close();
                }
            }
        } catch (Exception e) {
            LOGGER.error("parse " + urlDefinition.uri + " error:" + e.getMessage(), e);
        }
        return nameClassMap;
    }

    private static Set<URLDefinition> collectExtensionUrls(String resourceName, ClassLoader classLoader) {
        try {
            final Enumeration<URL> configs;
            if (classLoader != null) {
                configs = classLoader.getResources(resourceName);
            } else {
                configs = ClassLoader.getSystemResources(resourceName);
            }
            Set<URLDefinition> urlDefinitions = new HashSet<URLDefinition>();
            while (configs.hasMoreElements()) {
                URL url = configs.nextElement();
                final URI uri = url.toURI();
                ClassLoader highestClassLoader = findHighestReachableClassLoader(url, classLoader, resourceName);
                urlDefinitions.add(new URLDefinition(uri, highestClassLoader));
            }
            return urlDefinitions;
        } catch (Exception e) {
            LOGGER.error(e.getMessage(), e);
        }
        return Collections.emptySet();
    }

    private static ClassLoader findHighestReachableClassLoader(URL url, ClassLoader classLoader, String resourceName) {
        if (classLoader.getParent() == null) {
            return classLoader;
        }
        ClassLoader highestClassLoader = classLoader;
        ClassLoader current = classLoader;
        while (current.getParent() != null) {
            ClassLoader parent = current.getParent();
            try {
                Enumeration<URL> resources = parent.getResources(resourceName);
                if (resources != null) {
                    while (resources.hasMoreElements()) {
                        URL resourceURL = resources.nextElement();
                        if (url.toURI().equals(resourceURL.toURI())) {
                            highestClassLoader = parent;
                        }
                    }
                }
            } catch (IOException ignore) {
            } catch (URISyntaxException ignore) {
            }
            current = current.getParent();
        }
        return highestClassLoader;
    }

    private static final class URLDefinition {

        private final URI uri;

        private final ClassLoader classLoader;

        private URLDefinition(URI url, ClassLoader classLoader) {
            this.uri = url;
            this.classLoader = classLoader;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) {
                return true;
            }
            if (o == null || getClass() != o.getClass()) {
                return false;
            }
            URLDefinition that = (URLDefinition) o;
            return uri != null ? uri.equals(that.uri) : that.uri == null;
        }

        @Override
        public int hashCode() {
            return uri != null ? uri.hashCode() : 0;
        }
    }

    private static final class ServiceDefinition {

        private final String name;

        private final String clazz;

        private final ClassLoader classLoader;

        private ServiceDefinition(String name, String clazz, ClassLoader classLoader) {
            Assert.notNull(name, "name");
            Assert.notNull(clazz, "clazz");
            Assert.notNull(classLoader, "classLoader");
            this.name = name;
            this.clazz = clazz;
            this.classLoader = classLoader;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o)
                return true;
            if (o == null || getClass() != o.getClass())
                return false;
            ServiceDefinition that = (ServiceDefinition) o;
            if (name != null ? !name.equals(that.name) : that.name != null)
                return false;
            if (clazz != null ? !clazz.equals(that.clazz) : that.clazz != null)
                return false;
            return classLoader != null ? classLoader.equals(that.classLoader) : that.classLoader == null;
        }

        @Override
        public int hashCode() {
            int result = name != null ? name.hashCode() : 0;
            result = 31 * result + (clazz != null ? clazz.hashCode() : 0);
            result = 31 * result + (classLoader != null ? classLoader.hashCode() : 0);
            return result;
        }
    }

    private static class IdentityUniqueKey {

        private String identity;

        private ServiceDefinition definition;

        public IdentityUniqueKey(String identity, ServiceDefinition definition) {
            this.identity = identity;
            this.definition = definition;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o)
                return true;
            if (o == null || getClass() != o.getClass())
                return false;
            IdentityUniqueKey that = (IdentityUniqueKey) o;
            if (identity != null ? !identity.equals(that.identity) : that.identity != null)
                return false;
            return definition != null ? definition.equals(that.definition) : that.definition == null;
        }

        @Override
        public int hashCode() {
            int result = identity != null ? identity.hashCode() : 0;
            result = 31 * result + (definition != null ? definition.hashCode() : 0);
            return result;
        }
    }

    private static final class ServiceProvider {

        private final Class<?> clazz;

        private final String defaultName;

        private final String dynamicConfigKey;

        private final ConcurrentMap<String, ServiceDefinition> nameMaps;

        public ServiceProvider(Class<?> clazz, String dynamicConfigKey, String defaultName, ConcurrentMap<String, ServiceDefinition> nameMaps) {
            this.clazz = clazz;
            this.dynamicConfigKey = dynamicConfigKey;
            this.defaultName = defaultName;
            this.nameMaps = nameMaps;
        }
    }
}


final Set<URLDefinition> urlDefinitions = new HashSet<URLDefinition>();
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        urlDefinitions.addAll(collectExtensionUrls(LTS_DIRECTORY + clazz.getName(), classLoader));
        urlDefinitions.addAll(collectExtensionUrls(LTS_INTERNAL_DIRECTORY + clazz.getName(), classLoader));
        final ConcurrentMap<String, ServiceDefinition> serviceDefinitions = new ConcurrentHashMap<String, ServiceDefinition>();
        for (URLDefinition urlDefinition : urlDefinitions) {
            serviceDefinitions.putAll(parse(urlDefinition));
        }

package ltd.beihu.core.tools.spi;

import ltd.beihu.core.tools.utils.Assert;
import ltd.beihu.core.tools.utils.IDUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

public class ServiceLoader {

    private static final Logger LOGGER = LoggerFactory.getLogger(ServiceLoader.class);

    private static final String LTS_DIRECTORY = "META-INF/ceres/";

    private static final String LTS_INTERNAL = "internal";

    private static final String LTS_INTERNAL_DIRECTORY = LTS_DIRECTORY + LTS_INTERNAL + "/";

    private static final String DEFAULT_IDENTITY = IDUtils.UUID();

    private static final ConcurrentMap<Class<?>, ServiceProvider> serviceMap = new ConcurrentHashMap<Class<?>, ServiceProvider>();

    private static final ConcurrentMap<IdentityUniqueKey, Object> cachedObjectMap = new ConcurrentHashMap<IdentityUniqueKey, Object>();

    public static <T> T loadDefault(Class<T> clazz) {
        return load(clazz, "");
    }

    public static <T> T load(Class<T> clazz, String name) {
        return load(clazz, name, DEFAULT_IDENTITY);
    }

    @SuppressWarnings("unchecked")
    public static <T> T load(Class<T> clazz, String name, String identity) {
        try {
            ServiceProvider serviceProvider = getServiceProvider(clazz);
            if (StringUtils.isEmpty(name)) {
                // 加载默认的
                name = serviceProvider.defaultName;
            }
            ServiceDefinition definition = serviceProvider.nameMaps.get(name);
            if (definition == null) {
                throw new IllegalStateException("Service loader could not load name:" + name + "  class:" + clazz.getName() + "'s ServiceProvider from '" + LTS_DIRECTORY + "' or '" + LTS_INTERNAL_DIRECTORY + "' It may be empty or does not exist.");
            }
            // 用来保证每个节点都是一个各自的对象
            IdentityUniqueKey uniqueKey = new IdentityUniqueKey(identity, definition);
            Object obj = cachedObjectMap.get(uniqueKey);
            if (obj != null) {
                return (T) obj;
            }
            synchronized (definition) {
                obj = cachedObjectMap.get(uniqueKey);
                if (obj != null) {
                    return (T) obj;
                }
                String className = definition.clazz;
                ClassLoader classLoader = definition.classLoader;
                T srv = clazz.cast(ClassLoaderUtil.newInstance(classLoader, className));
                cachedObjectMap.putIfAbsent(uniqueKey, srv);
                return srv;
            }
        } catch (Exception e) {
            throw new IllegalStateException("Service loader could not load name:" + name + "  class:" + clazz.getName() + "'s ServiceProvider from '" + LTS_DIRECTORY + "' or '" + LTS_INTERNAL_DIRECTORY + "' It may be empty or does not exist.");
        }
    }

    private static ServiceProvider getServiceProvider(Class<?> clazz) {
        ServiceProvider serviceProvider = serviceMap.get(clazz);
        if (serviceProvider == null) {
            getServiceProviders(clazz);
            serviceProvider = serviceMap.get(clazz);
        }
        return serviceProvider;
    }

    public static Set<String> getServiceProviders(final Class<?> clazz) {
        if (clazz == null)
            throw new IllegalArgumentException("type == null");
        if (!clazz.isInterface()) {
            throw new IllegalArgumentException(" type(" + clazz + ") is not interface!");
        }
        if (!clazz.isAnnotationPresent(SPI.class)) {
            throw new IllegalArgumentException("type(" + clazz + ") is not extension, because WITHOUT @" + SPI.class.getSimpleName() + " Annotation!");
        }
        SPI spi = clazz.getAnnotation(SPI.class);
        String defaultName = spi.dftValue();
        String dynamicConfigKey = spi.key();

        if (serviceDefinitions.isEmpty()) {
            throw new IllegalStateException("Service loader could not load " + clazz.getName() + "'s ServiceProvider from '" + LTS_DIRECTORY + "' or '" + LTS_INTERNAL_DIRECTORY + "' It may be empty or does not exist.");
        }
        ServiceProvider serviceProvider = new ServiceProvider(clazz, dynamicConfigKey, defaultName, serviceDefinitions);
        // 先移除
        serviceMap.remove(clazz);
        serviceMap.put(clazz, serviceProvider);
        return serviceDefinitions.keySet();
    }

    private static Map<String, ServiceDefinition> parse(URLDefinition urlDefinition) {
        final Map<String, ServiceDefinition> nameClassMap = new HashMap<String, ServiceDefinition>();
        try {
            BufferedReader r = null;
            try {
                URL url = urlDefinition.uri.toURL();
                r = new BufferedReader(new InputStreamReader(url.openStream(), "UTF-8"));
                while (true) {
                    String line = r.readLine();
                    if (line == null) {
                        break;
                    }
                    int comment = line.indexOf('#');
                    if (comment >= 0) {
                        line = line.substring(0, comment);
                    }
                    line = line.trim();
                    if (line.length() == 0) {
                        continue;
                    }
                    int i = line.indexOf('=');
                    if (i > 0) {
                        String name = line.substring(0, i).trim();
                        String clazz = line.substring(i + 1).trim();
                        nameClassMap.put(name, new ServiceDefinition(name, clazz, urlDefinition.classLoader));
                    }
                }
            } finally {
                if (r != null) {
                    r.close();
                }
            }
        } catch (Exception e) {
            LOGGER.error("parse " + urlDefinition.uri + " error:" + e.getMessage(), e);
        }
        return nameClassMap;
    }

    private static Set<URLDefinition> collectExtensionUrls(String resourceName, ClassLoader classLoader) {
        try {
            final Enumeration<URL> configs;
            if (classLoader != null) {
                configs = classLoader.getResources(resourceName);
            } else {
                configs = ClassLoader.getSystemResources(resourceName);
            }
            Set<URLDefinition> urlDefinitions = new HashSet<URLDefinition>();
            while (configs.hasMoreElements()) {
                URL url = configs.nextElement();
                final URI uri = url.toURI();
                ClassLoader highestClassLoader = findHighestReachableClassLoader(url, classLoader, resourceName);
                urlDefinitions.add(new URLDefinition(uri, highestClassLoader));
            }
            return urlDefinitions;
        } catch (Exception e) {
            LOGGER.error(e.getMessage(), e);
        }
        return Collections.emptySet();
    }

    private static ClassLoader findHighestReachableClassLoader(URL url, ClassLoader classLoader, String resourceName) {
        if (classLoader.getParent() == null) {
            return classLoader;
        }
        ClassLoader highestClassLoader = classLoader;
        ClassLoader current = classLoader;
        while (current.getParent() != null) {
            ClassLoader parent = current.getParent();
            try {
                Enumeration<URL> resources = parent.getResources(resourceName);
                if (resources != null) {
                    while (resources.hasMoreElements()) {
                        URL resourceURL = resources.nextElement();
                        if (url.toURI().equals(resourceURL.toURI())) {
                            highestClassLoader = parent;
                        }
                    }
                }
            } catch (IOException ignore) {
            } catch (URISyntaxException ignore) {
            }
            current = current.getParent();
        }
        return highestClassLoader;
    }

    private static final class URLDefinition {

        private final URI uri;

        private final ClassLoader classLoader;

        private URLDefinition(URI url, ClassLoader classLoader) {
            this.uri = url;
            this.classLoader = classLoader;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) {
                return true;
            }
            if (o == null || getClass() != o.getClass()) {
                return false;
            }
            URLDefinition that = (URLDefinition) o;
            return uri != null ? uri.equals(that.uri) : that.uri == null;
        }

        @Override
        public int hashCode() {
            return uri != null ? uri.hashCode() : 0;
        }
    }

    private static final class ServiceDefinition {

        private final String name;

        private final String clazz;

        private final ClassLoader classLoader;

        private ServiceDefinition(String name, String clazz, ClassLoader classLoader) {
            Assert.notNull(name, "name");
            Assert.notNull(clazz, "clazz");
            Assert.notNull(classLoader, "classLoader");
            this.name = name;
            this.clazz = clazz;
            this.classLoader = classLoader;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o)
                return true;
            if (o == null || getClass() != o.getClass())
                return false;
            ServiceDefinition that = (ServiceDefinition) o;
            if (name != null ? !name.equals(that.name) : that.name != null)
                return false;
            if (clazz != null ? !clazz.equals(that.clazz) : that.clazz != null)
                return false;
            return classLoader != null ? classLoader.equals(that.classLoader) : that.classLoader == null;
        }

        @Override
        public int hashCode() {
            int result = name != null ? name.hashCode() : 0;
            result = 31 * result + (clazz != null ? clazz.hashCode() : 0);
            result = 31 * result + (classLoader != null ? classLoader.hashCode() : 0);
            return result;
        }
    }

    private static class IdentityUniqueKey {

        private String identity;

        private ServiceDefinition definition;

        public IdentityUniqueKey(String identity, ServiceDefinition definition) {
            this.identity = identity;
            this.definition = definition;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o)
                return true;
            if (o == null || getClass() != o.getClass())
                return false;
            IdentityUniqueKey that = (IdentityUniqueKey) o;
            if (identity != null ? !identity.equals(that.identity) : that.identity != null)
                return false;
            return definition != null ? definition.equals(that.definition) : that.definition == null;
        }

        @Override
        public int hashCode() {
            int result = identity != null ? identity.hashCode() : 0;
            result = 31 * result + (definition != null ? definition.hashCode() : 0);
            return result;
        }
    }

    private static final class ServiceProvider {

        private final Class<?> clazz;

        private final String defaultName;

        private final String dynamicConfigKey;

        private final ConcurrentMap<String, ServiceDefinition> nameMaps;

        public ServiceProvider(Class<?> clazz, String dynamicConfigKey, String defaultName, ConcurrentMap<String, ServiceDefinition> nameMaps) {
            this.clazz = clazz;
            this.dynamicConfigKey = dynamicConfigKey;
            this.defaultName = defaultName;
            this.nameMaps = nameMaps;
        }
    }
}


if (serviceDefinitions.isEmpty()) {
            throw new IllegalStateException("Service loader could not load " + clazz.getName() + "'s ServiceProvider from '" + LTS_DIRECTORY + "' or '" + LTS_INTERNAL_DIRECTORY + "' It may be empty or does not exist.");
        }
        ServiceProvider serviceProvider = new ServiceProvider(clazz, dynamicConfigKey, defaultName, serviceDefinitions);
        // 先移除
        serviceMap.remove(clazz);
        serviceMap.put(clazz, serviceProvider);
        return serviceDefinitions.keySet();
    }

package ltd.beihu.core.tools.spi;

import ltd.beihu.core.tools.utils.Assert;
import ltd.beihu.core.tools.utils.IDUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

public class ServiceLoader {

    private static final Logger LOGGER = LoggerFactory.getLogger(ServiceLoader.class);

    private static final String LTS_DIRECTORY = "META-INF/ceres/";

    private static final String LTS_INTERNAL = "internal";

    private static final String LTS_INTERNAL_DIRECTORY = LTS_DIRECTORY + LTS_INTERNAL + "/";

    private static final String DEFAULT_IDENTITY = IDUtils.UUID();

    private static final ConcurrentMap<Class<?>, ServiceProvider> serviceMap = new ConcurrentHashMap<Class<?>, ServiceProvider>();

    private static final ConcurrentMap<IdentityUniqueKey, Object> cachedObjectMap = new ConcurrentHashMap<IdentityUniqueKey, Object>();

    public static <T> T loadDefault(Class<T> clazz) {
        return load(clazz, "");
    }

    public static <T> T load(Class<T> clazz, String name) {
        return load(clazz, name, DEFAULT_IDENTITY);
    }

    @SuppressWarnings("unchecked")
    public static <T> T load(Class<T> clazz, String name, String identity) {
        try {
            ServiceProvider serviceProvider = getServiceProvider(clazz);
            if (StringUtils.isEmpty(name)) {
                // 加载默认的
                name = serviceProvider.defaultName;
            }
            ServiceDefinition definition = serviceProvider.nameMaps.get(name);
            if (definition == null) {
                throw new IllegalStateException("Service loader could not load name:" + name + "  class:" + clazz.getName() + "'s ServiceProvider from '" + LTS_DIRECTORY + "' or '" + LTS_INTERNAL_DIRECTORY + "' It may be empty or does not exist.");
            }
            // 用来保证每个节点都是一个各自的对象
            IdentityUniqueKey uniqueKey = new IdentityUniqueKey(identity, definition);
            Object obj = cachedObjectMap.get(uniqueKey);
            if (obj != null) {
                return (T) obj;
            }
            synchronized (definition) {
                obj = cachedObjectMap.get(uniqueKey);
                if (obj != null) {
                    return (T) obj;
                }
                String className = definition.clazz;
                ClassLoader classLoader = definition.classLoader;
                T srv = clazz.cast(ClassLoaderUtil.newInstance(classLoader, className));
                cachedObjectMap.putIfAbsent(uniqueKey, srv);
                return srv;
            }
        } catch (Exception e) {
            throw new IllegalStateException("Service loader could not load name:" + name + "  class:" + clazz.getName() + "'s ServiceProvider from '" + LTS_DIRECTORY + "' or '" + LTS_INTERNAL_DIRECTORY + "' It may be empty or does not exist.");
        }
    }

    private static ServiceProvider getServiceProvider(Class<?> clazz) {
        ServiceProvider serviceProvider = serviceMap.get(clazz);
        if (serviceProvider == null) {
            getServiceProviders(clazz);
            serviceProvider = serviceMap.get(clazz);
        }
        return serviceProvider;
    }

    public static Set<String> getServiceProviders(final Class<?> clazz) {
        if (clazz == null)
            throw new IllegalArgumentException("type == null");
        if (!clazz.isInterface()) {
            throw new IllegalArgumentException(" type(" + clazz + ") is not interface!");
        }
        if (!clazz.isAnnotationPresent(SPI.class)) {
            throw new IllegalArgumentException("type(" + clazz + ") is not extension, because WITHOUT @" + SPI.class.getSimpleName() + " Annotation!");
        }
        SPI spi = clazz.getAnnotation(SPI.class);
        String defaultName = spi.dftValue();
        String dynamicConfigKey = spi.key();
        final Set<URLDefinition> urlDefinitions = new HashSet<URLDefinition>();
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        urlDefinitions.addAll(collectExtensionUrls(LTS_DIRECTORY + clazz.getName(), classLoader));
        urlDefinitions.addAll(collectExtensionUrls(LTS_INTERNAL_DIRECTORY + clazz.getName(), classLoader));
        final ConcurrentMap<String, ServiceDefinition> serviceDefinitions = new ConcurrentHashMap<String, ServiceDefinition>();
        for (URLDefinition urlDefinition : urlDefinitions) {
            serviceDefinitions.putAll(parse(urlDefinition));
        }

    private static Map<String, ServiceDefinition> parse(URLDefinition urlDefinition) {
        final Map<String, ServiceDefinition> nameClassMap = new HashMap<String, ServiceDefinition>();
        try {
            BufferedReader r = null;
            try {
                URL url = urlDefinition.uri.toURL();
                r = new BufferedReader(new InputStreamReader(url.openStream(), "UTF-8"));
                while (true) {
                    String line = r.readLine();
                    if (line == null) {
                        break;
                    }
                    int comment = line.indexOf('#');
                    if (comment >= 0) {
                        line = line.substring(0, comment);
                    }
                    line = line.trim();
                    if (line.length() == 0) {
                        continue;
                    }
                    int i = line.indexOf('=');
                    if (i > 0) {
                        String name = line.substring(0, i).trim();
                        String clazz = line.substring(i + 1).trim();
                        nameClassMap.put(name, new ServiceDefinition(name, clazz, urlDefinition.classLoader));
                    }
                }
            } finally {
                if (r != null) {
                    r.close();
                }
            }
        } catch (Exception e) {
            LOGGER.error("parse " + urlDefinition.uri + " error:" + e.getMessage(), e);
        }
        return nameClassMap;
    }

    private static Set<URLDefinition> collectExtensionUrls(String resourceName, ClassLoader classLoader) {
        try {
            final Enumeration<URL> configs;
            if (classLoader != null) {
                configs = classLoader.getResources(resourceName);
            } else {
                configs = ClassLoader.getSystemResources(resourceName);
            }
            Set<URLDefinition> urlDefinitions = new HashSet<URLDefinition>();
            while (configs.hasMoreElements()) {
                URL url = configs.nextElement();
                final URI uri = url.toURI();
                ClassLoader highestClassLoader = findHighestReachableClassLoader(url, classLoader, resourceName);
                urlDefinitions.add(new URLDefinition(uri, highestClassLoader));
            }
            return urlDefinitions;
        } catch (Exception e) {
            LOGGER.error(e.getMessage(), e);
        }
        return Collections.emptySet();
    }

    private static ClassLoader findHighestReachableClassLoader(URL url, ClassLoader classLoader, String resourceName) {
        if (classLoader.getParent() == null) {
            return classLoader;
        }
        ClassLoader highestClassLoader = classLoader;
        ClassLoader current = classLoader;
        while (current.getParent() != null) {
            ClassLoader parent = current.getParent();
            try {
                Enumeration<URL> resources = parent.getResources(resourceName);
                if (resources != null) {
                    while (resources.hasMoreElements()) {
                        URL resourceURL = resources.nextElement();
                        if (url.toURI().equals(resourceURL.toURI())) {
                            highestClassLoader = parent;
                        }
                    }
                }
            } catch (IOException ignore) {
            } catch (URISyntaxException ignore) {
            }
            current = current.getParent();
        }
        return highestClassLoader;
    }

    private static final class URLDefinition {

        private final URI uri;

        private final ClassLoader classLoader;

        private URLDefinition(URI url, ClassLoader classLoader) {
            this.uri = url;
            this.classLoader = classLoader;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) {
                return true;
            }
            if (o == null || getClass() != o.getClass()) {
                return false;
            }
            URLDefinition that = (URLDefinition) o;
            return uri != null ? uri.equals(that.uri) : that.uri == null;
        }

        @Override
        public int hashCode() {
            return uri != null ? uri.hashCode() : 0;
        }
    }

    private static final class ServiceDefinition {

        private final String name;

        private final String clazz;

        private final ClassLoader classLoader;

        private ServiceDefinition(String name, String clazz, ClassLoader classLoader) {
            Assert.notNull(name, "name");
            Assert.notNull(clazz, "clazz");
            Assert.notNull(classLoader, "classLoader");
            this.name = name;
            this.clazz = clazz;
            this.classLoader = classLoader;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o)
                return true;
            if (o == null || getClass() != o.getClass())
                return false;
            ServiceDefinition that = (ServiceDefinition) o;
            if (name != null ? !name.equals(that.name) : that.name != null)
                return false;
            if (clazz != null ? !clazz.equals(that.clazz) : that.clazz != null)
                return false;
            return classLoader != null ? classLoader.equals(that.classLoader) : that.classLoader == null;
        }

        @Override
        public int hashCode() {
            int result = name != null ? name.hashCode() : 0;
            result = 31 * result + (clazz != null ? clazz.hashCode() : 0);
            result = 31 * result + (classLoader != null ? classLoader.hashCode() : 0);
            return result;
        }
    }

    private static class IdentityUniqueKey {

        private String identity;

        private ServiceDefinition definition;

        public IdentityUniqueKey(String identity, ServiceDefinition definition) {
            this.identity = identity;
            this.definition = definition;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o)
                return true;
            if (o == null || getClass() != o.getClass())
                return false;
            IdentityUniqueKey that = (IdentityUniqueKey) o;
            if (identity != null ? !identity.equals(that.identity) : that.identity != null)
                return false;
            return definition != null ? definition.equals(that.definition) : that.definition == null;
        }

        @Override
        public int hashCode() {
            int result = identity != null ? identity.hashCode() : 0;
            result = 31 * result + (definition != null ? definition.hashCode() : 0);
            return result;
        }
    }

    private static final class ServiceProvider {

        private final Class<?> clazz;

        private final String defaultName;

        private final String dynamicConfigKey;

        private final ConcurrentMap<String, ServiceDefinition> nameMaps;

        public ServiceProvider(Class<?> clazz, String dynamicConfigKey, String defaultName, ConcurrentMap<String, ServiceDefinition> nameMaps) {
            this.clazz = clazz;
            this.dynamicConfigKey = dynamicConfigKey;
            this.defaultName = defaultName;
            this.nameMaps = nameMaps;
        }
    }
}