com.devexperts.util.IndexedSet

Here are the examples of the java api com.devexperts.util.IndexedSet taken from open source projects. By voting up you can indicate which examples are most useful and appropriate.

57 Examples 7

19 Source : JMXStats.java
with Mozilla Public License 2.0
from devexperts

public clreplaced JMXStats extends QDStats implements DynamicMBean, MBeanRegistration {

    private static final String LONG_CLreplaced_NAME = long.clreplaced.getName();

    private static final String LONG_ARRAY_CLreplaced_NAME = long[].clreplaced.getName();

    private static final String STRING_CLreplaced_NAME = String.clreplaced.getName();

    private static final MBeanAttributeInfo CHILDREN = createJXMAttr("+Children", ObjectName[].clreplaced.getName());

    private static final MBeanAttributeInfo PARENT = createJXMAttr("+Parent", ObjectName.clreplaced.getName());

    // CHILDREN & PARENT
    private static final int BASIC_ATTRIBUTE_COUNT = 2;

    private static final String ARRAY_ATTR_SUFFIX = "Array*";

    private static final String TOP_ATTR_SUFFIX = "Top*";

    private static final int TOP_COUNT = 5;

    private static final Map<String, MBeanAttributeInfo> attr_map = new HashMap<String, MBeanAttributeInfo>();

    // indexed by flags
    private static final boolean[] SHOULD_REGISTER = new boolean[FLAG_COUNT];

    private static final MBeanInfo[] MBEAN_INFO = new MBeanInfo[FLAG_COUNT];

    private static final Comparator<ObjectName> NAMES_COMPARATOR = new Comparator<ObjectName>() {

        public int compare(ObjectName o1, ObjectName o2) {
            int i = o1.getDomain().compareTo(o2.getDomain());
            if (i != 0)
                return i;
            return o1.getKeyPropertyListString().compareTo(o2.getKeyPropertyListString());
        }
    };

    private static MBeanAttributeInfo createJXMAttr(String name, String clreplaced_name) {
        return new MBeanAttributeInfo(name, clreplaced_name, name, true, false, false);
    }

    static {
        // init attributes lists
        ArrayList<MBeanAttributeInfo>[] al = (ArrayList<MBeanAttributeInfo>[]) new ArrayList[FLAG_COUNT];
        for (int f = 0; f < FLAG_COUNT; f++) {
            al[f] = new ArrayList<MBeanAttributeInfo>();
            al[f].add(CHILDREN);
            al[f].add(PARENT);
        }
        for (int i = 0; i < SValue.getValueCount(); i++) {
            SValue value = SValue.getValue(i);
            MBeanAttributeInfo attr = createJXMAttr(value.getName(), LONG_CLreplaced_NAME);
            for (int f = 0; f < FLAG_COUNT; f++) if (value.supportsFlag(f)) {
                al[f].add(attr);
            }
        }
        for (int i = 0; i < SValue.getValueCount(); i++) {
            SValue value = SValue.getValue(i);
            if (value.isRid()) {
                MBeanAttributeInfo array_attr = createJXMAttr(value.getName() + ARRAY_ATTR_SUFFIX, LONG_ARRAY_CLreplaced_NAME);
                MBeanAttributeInfo top_attr = createJXMAttr(value.getName() + TOP_ATTR_SUFFIX, STRING_CLreplaced_NAME);
                for (int f = 0; f < FLAG_COUNT; f++) if ((f & FLAG_RID) != 0 && value.supportsFlag(f)) {
                    al[f].add(array_attr);
                    al[f].add(top_attr);
                }
            }
        }
        ArrayList<MBeanAttributeInfo> all_attributes = al[FLAG_COUNT - 1];
        for (MBeanAttributeInfo attr : all_attributes) attr_map.put(attr.getName(), attr);
        // init MBeanInfo
        for (int f = 0; f < FLAG_COUNT; f++) {
            SHOULD_REGISTER[f] = al[f].size() > BASIC_ATTRIBUTE_COUNT;
            MBEAN_INFO[f] = new MBeanInfo(JMXStats.clreplaced.getName(), "JMXStats", al[f].toArray(new MBeanAttributeInfo[al[f].size()]), null, new MBeanOperationInfo[] { new MBeanOperationInfo("reportCounters", "Reports performance counters", new MBeanParameterInfo[] { new MBeanParameterInfo("format", "java.lang.String", "html (default) or csv"), new MBeanParameterInfo("topSize", "java.lang.Integer", "max size of TOP tables, " + TOP_COUNT + " by default") }, "java.lang.String", MBeanOperationInfo.INFO) }, null);
        }
    }

    public static RootRegistration createRoot(String name, DataScheme scheme) {
        for (int i = 0; ; i++) {
            String s = i == 0 ? name : name + "#" + i;
            QDStats rootStats = new JMXStats("name=" + s);
            rootStats.initRoot(SType.ANY, scheme);
            Management.Registration registration = Management.registerMBean(rootStats, null, getRootStatsObjectName(s));
            if (!registration.hasExisted())
                return new RootRegistration(rootStats, registration);
            i++;
        }
    }

    private static String getRootStatsObjectName(String name) {
        return "com.devexperts.qd.stats:name=" + name + ",type=Any";
    }

    public static clreplaced RootRegistration {

        private final QDStats rootStats;

        private final Management.Registration registration;

        public RootRegistration(QDStats rootStats, Management.Registration registration) {
            this.rootStats = rootStats;
            this.registration = registration;
        }

        public QDStats getRootStats() {
            return rootStats;
        }

        public void unregister() {
            registration.unregister();
        }
    }

    // ------------------------- instance -------------------------
    private JmxInfo jmx;

    private int index;

    private IndexedSet<String, ChildIndex> childIndexerByCollectorType;

    private volatile Map<String, AddedMBeanEntry> addedMBeans;

    public JMXStats() {
    }

    public JMXStats(String key_properties) {
        super(key_properties);
    }

    @Override
    protected QDStats newInstance(SType type, boolean unmanaged) {
        // anonymous AGENT and DISTRIBUTOR children are regular QDStats (see QD-445)
        if (unmanaged && (type == SType.AGENT || type == SType.DISTRIBUTOR))
            return new QDStats();
        return new JMXStats();
    }

    @Override
    protected JMXStats getParent() {
        return (JMXStats) super.getParent();
    }

    @Override
    protected // SYNC: lock
    void initChild(QDStats child, SType type, String key_properties, int rid_count, DataScheme scheme) {
        super.initChild(child, type, key_properties, rid_count, scheme);
        if (!(child instanceof JMXStats))
            // anonymous children are regular QDStats (see QD-445)
            return;
        JMXStats jmxChild = (JMXStats) child;
        // compute index for this bean
        String childCollectorType = jmxChild.getCollectorType();
        if (childIndexerByCollectorType == null)
            childIndexerByCollectorType = IndexedSet.create(ChildIndex::getCollectorType);
        ChildIndex childIndex = childIndexerByCollectorType.getByKey(childCollectorType);
        if (childIndex == null)
            childIndexerByCollectorType.add(childIndex = new ChildIndex(childCollectorType));
        jmxChild.index = childIndex.index++;
        // register if needed
        if (jmxChild.shouldRegister())
            registerChildBean(jmxChild);
    }

    private boolean shouldRegister() {
        return SHOULD_REGISTER[getType().getFlag()];
    }

    private JmxInfo getJmxInfoFromAncestors() {
        JMXStats stats = this;
        while (stats != null) {
            if (stats.jmx != null)
                return stats.jmx;
            stats = stats.getParent();
        }
        return null;
    }

    private void registerChildBean(JMXStats jmxChild) {
        JmxInfo jmx = getJmxInfoFromAncestors();
        if (jmx != null)
            try {
                // it will construct its name in preRegister
                jmx.server.registerMBean(jmxChild, jmx.name);
            } catch (InstanceAlreadyExistsException e) {
                QDLog.log.warn("Already registered JMX bean " + e.getMessage());
            } catch (Exception e) {
                QDLog.log.error("Unexpected exception while registering JMX children bean of " + jmx.name, e);
            }
    }

    @Override
    protected void closeImpl() {
        if (jmx != null)
            unregisterMBean();
        else
            unregisterChildrenRec();
    }

    protected void registerAddedMBeans() {
        if (jmx != null && addedMBeans != null)
            for (Map.Entry<String, AddedMBeanEntry> entry : addedMBeans.entrySet()) registerAddedMBean(entry.getKey(), entry.getValue());
    }

    protected void registerAddedMBean(String type, AddedMBeanEntry mbe) {
        if (jmx != null && mbe.name == null) {
            String name = constructName(jmx.name.getDomain(), type, jmx.name.getKeyPropertyListString());
            try {
                mbe.name = jmx.server.registerMBean(mbe.mbean, new ObjectName(name)).getObjectName();
            } catch (Exception e) {
                QDLog.log.error("Unexpected exception registering JMX bean " + name, e);
            }
        }
    }

    protected void unregisterAddedMBeans() {
        if (jmx != null && addedMBeans != null)
            for (Map.Entry<String, AddedMBeanEntry> entry : addedMBeans.entrySet()) unregisterAddedMBean(entry.getValue());
    }

    protected void unregisterAddedMBean(AddedMBeanEntry mbe) {
        if (jmx != null && mbe.name != null)
            try {
                if (jmx.server.isRegistered(mbe.name))
                    jmx.server.unregisterMBean(mbe.name);
                mbe.name = null;
            } catch (Exception e) {
                QDLog.log.error("Unexpected exception unregistering JMX bean " + mbe.name, e);
            }
    }

    @Override
    public void addMBean(String type, Object mbean) {
        if (addedMBeans == null)
            synchronized (this) {
                if (addedMBeans == null)
                    // because we want synchronized access just in case...
                    addedMBeans = new ConcurrentHashMap<String, AddedMBeanEntry>();
            }
        AddedMBeanEntry mbe = addedMBeans.get(type);
        if (mbe != null)
            unregisterAddedMBean(mbe);
        mbe = new AddedMBeanEntry(Management.wrapMBean(mbean, null));
        registerAddedMBean(type, mbe);
        addedMBeans.put(type, mbe);
    }

    private static clreplaced AddedMBeanEntry {

        final DynamicMBean mbean;

        ObjectName name;

        AddedMBeanEntry(DynamicMBean mbean) {
            this.mbean = mbean;
        }
    }

    // ========== DynamicMBean Implementation ==========
    public Object getAttribute(String attribute) throws AttributeNotFoundException {
        try {
            MBeanAttributeInfo attr = attr_map.get(attribute);
            if (attr == null) {
                throw new AttributeNotFoundException(attribute);
            }
            if (attr == CHILDREN) {
                List<ObjectName> names = new ArrayList<ObjectName>(getChildren().length);
                addChildrenNamesRec(names);
                QuickSort.sort(names, NAMES_COMPARATOR);
                return names.toArray(new ObjectName[names.size()]);
            }
            if (attr == PARENT) {
                return getParentNameRec();
            }
            if (attr.getType().equals(LONG_CLreplaced_NAME)) {
                return getValue(SValue.valueOf(attr.getName()), false);
            }
            if (attr.getType().equals(LONG_ARRAY_CLreplaced_NAME) && attr.getName().endsWith(ARRAY_ATTR_SUFFIX)) {
                long[] v = new long[getRidCount()];
                addValues(SValue.valueOf(attr.getName().substring(0, attr.getName().length() - ARRAY_ATTR_SUFFIX.length())), false, v);
                return v;
            }
            if (attr.getType().equals(STRING_CLreplaced_NAME) && attr.getName().endsWith(TOP_ATTR_SUFFIX)) {
                long[] v = new long[getRidCount()];
                addValues(SValue.valueOf(attr.getName().substring(0, attr.getName().length() - TOP_ATTR_SUFFIX.length())), false, v);
                return findTop(v);
            }
            throw new AttributeNotFoundException(attribute);
        } catch (RuntimeException e) {
            QDLog.log.error("Unexpected JMX exception", e);
            throw e;
        }
    }

    private void addChildrenNamesRec(List<ObjectName> names) {
        // Atomic read.
        QDStats[] children = getChildren();
        for (QDStats child : children) {
            if (!(child instanceof JMXStats))
                // anonymous children are regular QDStats (see QD-445)
                continue;
            JMXStats jmxChild = (JMXStats) child;
            if (jmxChild != null) {
                if (jmxChild.jmx != null)
                    names.add(jmxChild.jmx.name);
                else
                    jmxChild.addChildrenNamesRec(names);
            }
        }
    }

    public ObjectName getParentNameRec() {
        JMXStats stats = getParent();
        while (stats != null) {
            if (stats.jmx != null)
                return stats.jmx.name;
            stats = stats.getParent();
        }
        return null;
    }

    private String reportCounters(String format, int topSize) {
        Map<String, AtomicLongArray> counters = new LinkedHashMap<>();
        for (MBeanAttributeInfo attr : getMBeanInfo().getAttributes()) {
            if (attr.getType().equals(STRING_CLreplaced_NAME) && attr.getName().endsWith(TOP_ATTR_SUFFIX)) {
                long[] v = new long[getRidCount()];
                addValues(SValue.valueOf(attr.getName().substring(0, attr.getName().length() - TOP_ATTR_SUFFIX.length())), false, v);
                counters.put(attr.getName(), new AtomicLongArray(v));
            }
        }
        return CollectorCountersImpl.reportCounters(getScheme(), counters, format, topSize);
    }

    private String findTop(long[] v) {
        if (v.length == 0)
            return "";
        PriorityQueue<IndexedValue> pq = new PriorityQueue<IndexedValue>(v.length);
        for (int i = 0; i < v.length; i++) pq.add(new IndexedValue(i, v[i]));
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < TOP_COUNT; i++) {
            if (pq.isEmpty())
                break;
            IndexedValue iv = pq.remove();
            if (iv.value == 0)
                break;
            if (sb.length() > 0)
                sb.append(", ");
            sb.append(getScheme() != null && iv.index < getScheme().getRecordCount() ? getScheme().getRecord(iv.index).getName() : "[" + iv.index + "]");
            sb.append('=').append(iv.value);
        }
        return sb.toString();
    }

    private static clreplaced IndexedValue implements Comparable<IndexedValue> {

        final int index;

        final long value;

        IndexedValue(int index, long value) {
            this.index = index;
            this.value = value;
        }

        public int compareTo(IndexedValue o) {
            return o.value > value ? 1 : o.value < value ? -1 : 0;
        }
    }

    public void setAttribute(Attribute attribute) throws AttributeNotFoundException {
        throw new AttributeNotFoundException(attribute.getName());
    }

    public AttributeList getAttributes(String[] attributes) {
        AttributeList al = new AttributeList();
        for (String attribute : attributes) try {
            al.add(new Attribute(attribute, getAttribute(attribute)));
        } catch (Exception e) {
            QDLog.log.error("Unexpected JMX exception", e);
        }
        return al;
    }

    public AttributeList setAttributes(AttributeList attributes) {
        AttributeList al = new AttributeList();
        for (Object attribute : attributes) try {
            Attribute a = (Attribute) attribute;
            al.add(new Attribute(a.getName(), getAttribute(a.getName())));
        } catch (Exception e) {
            QDLog.log.error("Unexpected JMX exception", e);
        }
        return al;
    }

    public Object invoke(String action, Object[] params, String[] signature) throws ReflectionException {
        if (action.equalsIgnoreCase("reportCounters")) {
            String format = (String) params[0];
            Integer topSize = (Integer) params[1];
            int top = topSize == null ? TOP_COUNT : topSize;
            return reportCounters(format, top);
        }
        throw new ReflectionException(new NoSuchMethodException(action));
    }

    public MBeanInfo getMBeanInfo() {
        return MBEAN_INFO[getType().getFlag()];
    }

    // ========== MBeanRegistration Implementation ==========
    // props may be empty or null
    protected ObjectName constructName(String domain, String key_properties) throws MalformedObjectNameException {
        return new ObjectName(constructName(domain, getType().getName() + "Stats", key_properties));
    }

    protected String constructName(String domain, String type, String key_properties) {
        JMXStatsNameBuilder nb = new JMXStatsNameBuilder(domain);
        // append full key properties first (always)
        nb.appendKeyProperties(getFullKeyProperties());
        for (QDStats child = this, parent; (parent = child.getParent()) != null; child = parent) {
            if (child.isSumMode())
                nb.insertSumModeFlag();
            else
                nb.insertId(((JMXStats) child).index);
        }
        nb.append("c", getCollectorFromAncestors());
        nb.appendType(type);
        nb.doneId();
        // inherited key properties (at the end)
        nb.appendKeyProperties(key_properties);
        return nb.toString();
    }

    private String getCollector() {
        if (getType() == SType.TICKER)
            return "Ticker";
        if (getType() == SType.STREAM)
            return "Stream";
        if (getType() == SType.HISTORY)
            return "History";
        return null;
    }

    private String getCollectorFromAncestors() {
        String collector = getCollector();
        for (QDStats stats = getParent(); collector == null && stats != null; stats = stats.getParent()) collector = ((JMXStats) stats).getCollector();
        return collector == null ? "Any" : collector;
    }

    private String getCollectorType() {
        return getCollectorFromAncestors() + "-" + getType();
    }

    public ObjectName preRegister(MBeanServer server, ObjectName name) throws Exception {
        if (jmx != null)
            unregisterMBean();
        jmx = new JmxInfo(server, name == null ? constructName(server.getDefaultDomain(), null) : constructName(name.getDomain(), name.getKeyPropertyListString()));
        registerChildrenRec();
        registerAddedMBeans();
        return jmx.name;
    }

    private void registerChildrenRec() {
        // Atomic read.
        QDStats[] children = getChildren();
        for (QDStats child : children) {
            if (!(child instanceof JMXStats))
                // anonymous children are regular QDStats (see QD-445)
                continue;
            JMXStats jmxChild = (JMXStats) child;
            if (jmxChild != null && jmxChild.getParent() == this) {
                // register only proper children
                if (jmxChild.shouldRegister())
                    registerChildBean(jmxChild);
                else
                    jmxChild.registerChildrenRec();
            }
        }
    }

    public void postRegister(Boolean registration_done) {
        if (registration_done == null || !registration_done)
            jmx = null;
    }

    public void preDeregister() throws Exception {
        unregisterAddedMBeans();
        unregisterChildrenRec();
    }

    private void unregisterChildrenRec() {
        // Atomic read.
        QDStats[] children = getChildren();
        for (QDStats child : children) {
            if (!(child instanceof JMXStats))
                // anonymous children are regular QDStats (see QD-445)
                continue;
            JMXStats jmxChild = (JMXStats) child;
            if (jmxChild != null && jmxChild.getParent() == this) {
                if (jmxChild.jmx != null)
                    jmxChild.unregisterMBean();
                else
                    jmxChild.unregisterChildrenRec();
            }
        }
    }

    public void postDeregister() {
        jmx = null;
    }

    private void unregisterMBean() {
        try {
            jmx.server.unregisterMBean(jmx.name);
        } catch (Exception e) {
            QDLog.log.error("Unexpected exception while unregistering JMX bean " + jmx.name, e);
        }
    }

    public String toString() {
        return jmx == null ? super.toString() : jmx.name.toString();
    }

    private static clreplaced JmxInfo {

        final MBeanServer server;

        final ObjectName name;

        JmxInfo(MBeanServer server, ObjectName name) {
            this.server = server;
            this.name = name;
        }
    }

    private static clreplaced ChildIndex {

        final String collectorType;

        int index;

        ChildIndex(String collectorType) {
            this.collectorType = collectorType;
        }

        String getCollectorType() {
            return collectorType;
        }
    }
}

19 Source : DebugDumpReader.java
with Mozilla Public License 2.0
from devexperts

public clreplaced DebugDumpReader {

    private final IndexedSet<Integer, ObjectReader> clreplacedMap = IndexedSet.createInt(reader -> reader.clreplacedId);

    private final IndexedSet<Integer, ObjectRef> objectMap = IndexedSet.createInt(ref -> ref.id);

    private DebugDumpReader() {
        clreplacedMap.put(new ObjectReader(UNKNOWN, null));
        clreplacedMap.put(new ObjectReader(STRING, String.clreplaced));
        clreplacedMap.put(new ObjectReader(BOOLEAN, boolean[].clreplaced));
        clreplacedMap.put(new ObjectReader(BYTE, byte[].clreplaced));
        clreplacedMap.put(new ObjectReader(SHORT, short[].clreplaced));
        clreplacedMap.put(new ObjectReader(CHAR, char[].clreplaced));
        clreplacedMap.put(new ObjectReader(INT, int[].clreplaced));
        clreplacedMap.put(new ObjectReader(LONG, long[].clreplaced));
        clreplacedMap.put(new ObjectReader(FLOAT, float[].clreplaced));
        clreplacedMap.put(new ObjectReader(DOUBLE, double[].clreplaced));
        objectMap.put(new ObjectRef(0));
    }

    private void read(String fileName) {
        System.out.println("Reading " + fileName);
        try {
            try (StreamInput in = new StreamInput(new InflaterInputStream(new TrackingInput(new RandomAccessFile(fileName, "r"))))) {
                while (parse(in)) /* just loop */
                ;
            }
        } catch (IOException e) {
            System.out.println("Exception while reading " + fileName);
            e.printStackTrace(System.out);
        }
    }

    private void resolve() {
        System.out.println("Resolving " + objectMap.size() + " objects from " + clreplacedMap.size() + " clreplacedes");
        objectMap.forEach(ObjectRef::resolveEnums);
        objectMap.forEach(ObjectRef::resolveFields);
        System.out.println("Snapshot reconstructed");
    }

    private boolean parse(StreamInput in) throws IOException {
        int id;
        try {
            id = in.readCompactInt();
        } catch (EOFException e) {
            return false;
        }
        try {
            if (id < 0)
                readClreplacedDesc(in, id);
            else if (id > 0)
                readObjectDesc(in, id);
            else
                throw new IOException("Invalid");
        } catch (IOException e) {
            throw new IOException("Failed to parse id=" + id, e);
        }
        return true;
    }

    private void readClreplacedDesc(StreamInput in, int clreplacedId) throws IOException {
        if (clreplacedMap.containsKey(clreplacedId))
            throw new IOException("Repeated clreplacedId=" + clreplacedId);
        String clreplacedName = in.readUTFString();
        int parentClreplacedId = in.readCompactInt();
        if (!clreplacedMap.containsKey(parentClreplacedId))
            throw new IOException("Parent clreplaced " + parentClreplacedId + " is not found");
        ObjectReader parent = clreplacedMap.getByKey(parentClreplacedId);
        if (clreplacedName == null) {
            ArrayDesc desc = new ArrayDesc(clreplacedId, parent.clazz);
            clreplacedMap.put(desc);
            return;
        }
        Clreplaced<?> clazz;
        try {
            clazz = Clreplaced.forName(clreplacedName);
        } catch (ClreplacedNotFoundException e) {
            System.out.println("Cannot find clreplaced " + clreplacedName);
            clazz = null;
        }
        int fieldCount = in.readCompactInt();
        ClreplacedDesc desc = new ClreplacedDesc(clreplacedId, parent, clazz, fieldCount);
        clreplacedMap.put(desc);
        for (int i = 0; i < fieldCount; i++) {
            String fieldName = in.readUTFString();
            int fieldType = in.readCompactInt();
            Field field = null;
            try {
                field = clazz.getDeclaredField(fieldName);
                field.setAccessible(true);
            } catch (Exception e) {
                System.out.println("Cannot find field " + fieldName + " of " + clreplacedName);
            }
            desc.fields[i] = new FieldDesc(field, fieldType);
            if (fieldType == REFERENCE)
                desc.refCount++;
        }
    }

    private void readObjectDesc(StreamInput in, int objectId) throws IOException {
        if (objectMap.containsKey(objectId))
            throw new IOException("Repeated objectId=" + objectId);
        int clreplacedId = in.readCompactInt();
        ObjectReader reader = clreplacedMap.getByKey(clreplacedId);
        if (reader == null)
            throw new IOException("Invalid clreplacedId=" + clreplacedId);
        objectMap.put(reader.readObject(in, objectId));
    }

    public void dumpInfo() {
        Object owner = getOwner();
        Object version = getInstanceOrNull(VERSION);
        Object systemProperties = getInstanceOrNull(SYSTEM_PROPERTIES);
        Object exception = getInstanceOrNull(EXCEPTION);
        System.out.println("--- Dump information ---");
        System.out.println("Owner = " + owner);
        System.out.println("QDS   = " + version);
        System.out.println("JVM   = " + ((systemProperties instanceof Map) ? ((Map<?, ?>) systemProperties).get("java.version") : null));
        if (exception instanceof Throwable) {
            System.out.println("Error = " + exception);
            ((Throwable) exception).printStackTrace(System.out);
        }
    }

    public Object getOwner() {
        return getInstanceOrNull(OWNER);
    }

    public CollectorDebug.RehashCrashInfo getRehashCrashInfo() {
        final CollectorDebug.RehashCrashInfo rci = new CollectorDebug.RehashCrashInfo();
        Object exception = getInstanceOrNull(EXCEPTION);
        if (exception instanceof FatalError) {
            String message = ((FatalError) exception).getMessage();
            Matcher matcher = Pattern.compile("^Counter underflow for key=(\\d+),.*").matcher(message);
            if (matcher.matches()) {
                // support only total subscription rehash crash
                rci.agent = 1;
                rci.key = Integer.parseInt(matcher.group(1));
                System.out.println("Rehash crash detected at key " + rci.key);
            }
        }
        return rci;
    }

    public void visit(Object owner, CollectorVisitor visitor) {
        if (owner instanceof Collection) {
            for (Object o : (Collection<?>) owner) visit(o, visitor);
        } else if (owner instanceof Collector)
            visitor.visit((Collector) owner);
    }

    private Object getInstanceOrNull(int objectId) {
        ObjectRef ref = objectMap.getByKey(objectId);
        return ref == null ? null : ref.instance;
    }

    private Object getInstanceOrClreplaced(int objectId) {
        if (objectId >= 0) {
            ObjectRef ref = objectMap.getByKey(objectId);
            if (ref == null) {
                System.out.println("Cannot find object instance #" + objectId);
                return null;
            }
            return ref.instance;
        } else {
            ObjectReader reader = clreplacedMap.getByKey(objectId);
            if (reader == null) {
                System.out.println("Cannot find clreplaced #" + objectId);
                return null;
            }
            return reader.clazz;
        }
    }

    public static void main(String[] args) throws IOException {
        if (args.length == 0) {
            System.err.println("Usage: DebugDumpReader <file> [<command> [<args>]] [+ <command> ...]");
            return;
        }
        DebugDumpReader reader = new DebugDumpReader();
        reader.read(args[0]);
        reader.resolve();
        DebugDumpCLI cli = new DebugDumpCLI(reader);
        if (args.length == 1)
            cli.interactive();
        else
            cli.execute(Arrays.copyOfRange(args, 1, args.length));
    }

    static clreplaced ObjectRef {

        final int id;

        Object instance;

        ObjectRef(int id) {
            this.id = id;
        }

        ObjectRef(int id, Object instance) {
            this.id = id;
            this.instance = instance;
        }

        void resolveEnums() {
        }

        void resolveFields() {
        }
    }

    clreplaced ArrayRef extends ObjectRef {

        int[] refs;

        ArrayRef(int id, Clreplaced<?> componentType, int length) {
            super(id);
            instance = Array.newInstance(componentType, length);
            refs = new int[length];
        }

        @Override
        void resolveFields() {
            Object[] a = (Object[]) instance;
            for (int i = 0; i < refs.length; i++) {
                int ref = refs[i];
                Object value = getInstanceOrClreplaced(ref);
                try {
                    a[i] = value;
                } catch (ArrayStoreException e) {
                    System.out.println("Cannot store element " + i + " of array #" + id + " " + a.getClreplaced().getComponentType().getName() + "[]" + " to reference #" + ref + " " + value.getClreplaced().getName());
                }
            }
        }
    }

    clreplaced InstanceRef extends ObjectRef {

        int[] refs;

        final ClreplacedDesc clreplacedDesc;

        InstanceRef(int id, Object instance, ClreplacedDesc clreplacedDesc) {
            super(id, instance);
            refs = new int[clreplacedDesc.refCount];
            this.clreplacedDesc = clreplacedDesc;
        }

        void resolveEnums() {
            if (instance instanceof Enum<?>) {
                resolveFields();
                instance = resolveEnum((Enum<?>) instance);
            }
        }

        @Override
        void resolveFields() {
            int i = 0;
            ClreplacedDesc cur = clreplacedDesc;
            do {
                for (FieldDesc fd : cur.fields) if (fd.type == REFERENCE && fd.field != null) {
                    int ref = refs[i++];
                    Object value = getInstanceOrClreplaced(ref);
                    try {
                        if (fd.field.getType().isInstance(value))
                            fd.field.set(instance, value);
                    } catch (Exception e) {
                        System.out.println("Cannot set field " + fd.field.getName() + " on object #" + id + " " + fd.field.getDeclaringClreplaced().getName() + " to reference #" + ref + " " + value.getClreplaced().getName());
                    }
                }
                cur = cur.parent;
            } while (cur != null);
        }

        private Object resolveEnum(Enum<?> instance) {
            try {
                return Enum.valueOf(instance.getClreplaced(), instance.name());
            } catch (IllegalArgumentException e) {
                System.out.println("Cannot resolveFields enum " + instance.getClreplaced().getName() + " with name " + instance.name());
                return instance;
            }
        }
    }

    static clreplaced ObjectReader {

        final int clreplacedId;

        final Clreplaced<?> clazz;

        ObjectReader(int clreplacedId, Clreplaced<?> clazz) {
            this.clreplacedId = clreplacedId;
            this.clazz = clazz;
        }

        ObjectRef readObject(BufferedInput in, int objectId) throws IOException {
            if (clreplacedId == UNKNOWN) {
                // skip
                in.readUTFString();
                return new ObjectRef(objectId);
            }
            if (clreplacedId == STRING)
                return new ObjectRef(objectId, in.readUTFString());
            int length = in.readCompactInt();
            switch(clreplacedId) {
                case BOOLEAN:
                    boolean[] booleanArr = new boolean[length];
                    for (int i = 0; i < length; i++) booleanArr[i] = in.readByte() != 0;
                    return new ObjectRef(objectId, booleanArr);
                case BYTE:
                    byte[] byteArr = new byte[length];
                    for (int i = 0; i < length; i++) byteArr[i] = in.readByte();
                    return new ObjectRef(objectId, byteArr);
                case SHORT:
                    short[] shortArr = new short[length];
                    for (int i = 0; i < length; i++) shortArr[i] = (short) in.readCompactInt();
                    return new ObjectRef(objectId, shortArr);
                case CHAR:
                    char[] charArr = new char[length];
                    for (int i = 0; i < length; i++) charArr[i] = (char) in.readUTFChar();
                    return new ObjectRef(objectId, charArr);
                case INT:
                    int[] intArr = new int[length];
                    for (int i = 0; i < length; i++) intArr[i] = in.readCompactInt();
                    return new ObjectRef(objectId, intArr);
                case LONG:
                    long[] longArr = new long[length];
                    for (int i = 0; i < length; i++) longArr[i] = in.readCompactLong();
                    return new ObjectRef(objectId, longArr);
                case FLOAT:
                    float[] floatArr = new float[length];
                    for (int i = 0; i < length; i++) floatArr[i] = in.readFloat();
                    return new ObjectRef(objectId, floatArr);
                case DOUBLE:
                    double[] doubleArr = new double[length];
                    for (int i = 0; i < length; i++) doubleArr[i] = in.readFloat();
                    return new ObjectRef(objectId, doubleArr);
                default:
                    throw new IllegalArgumentException("clreplacedId=" + clreplacedId);
            }
        }
    }

    clreplaced ArrayDesc extends ObjectReader {

        ArrayDesc(int clreplacedId, Clreplaced<?> parent) {
            super(clreplacedId, parent == null ? Object[].clreplaced : Array.newInstance(parent, 0).getClreplaced());
        }

        @Override
        ObjectRef readObject(BufferedInput in, int objectId) throws IOException {
            int length = in.readCompactInt();
            ArrayRef result = new ArrayRef(objectId, clazz.getComponentType(), length);
            for (int i = 0; i < length; i++) result.refs[i] = in.readCompactInt();
            return result;
        }
    }

    static clreplaced FieldDesc {

        final Field field;

        final int type;

        FieldDesc(Field field, int type) {
            this.field = field;
            this.type = type;
        }
    }

    clreplaced ClreplacedDesc extends ObjectReader {

        final ClreplacedDesc parent;

        final FieldDesc[] fields;

        int refCount;

        ClreplacedDesc(int clreplacedId, ObjectReader parent, Clreplaced<?> clazz, int fieldCount) {
            super(clreplacedId, clazz);
            this.parent = parent instanceof ClreplacedDesc ? (ClreplacedDesc) parent : null;
            fields = new FieldDesc[fieldCount];
            refCount = this.parent == null ? 0 : this.parent.refCount;
        }

        @Override
        ObjectRef readObject(BufferedInput in, int objectId) throws IOException {
            if (clazz == null || Modifier.isAbstract(clazz.getModifiers())) {
                skipInstanceFields(in);
                return new ObjectRef(objectId);
            }
            Object instance;
            try {
                instance = UnsafeHolder.UNSAFE.allocateInstance(clazz);
            } catch (InstantiationException e) {
                throw new IOException("Cannot allocate instance " + objectId + " of " + clazz.getName(), e);
            }
            InstanceRef result = readInstanceFields(in, new InstanceRef(objectId, instance, this));
            return result;
        }

        private InstanceRef readInstanceFields(BufferedInput in, InstanceRef result) throws IOException {
            ClreplacedDesc cur = this;
            Object instance = result.instance;
            int i = 0;
            do {
                for (FieldDesc fd : cur.fields) try {
                    switch(fd.type) {
                        case REFERENCE:
                            result.refs[i++] = in.readCompactInt();
                            break;
                        case BOOLEAN:
                            boolean booleanValue = in.readByte() != 0;
                            if (fd.field != null)
                                fd.field.setBoolean(instance, booleanValue);
                            break;
                        case BYTE:
                            byte byteValue = in.readByte();
                            if (fd.field != null)
                                fd.field.setByte(instance, byteValue);
                            break;
                        case SHORT:
                            short shortValue = (short) in.readCompactInt();
                            if (fd.field != null)
                                fd.field.setShort(instance, shortValue);
                            break;
                        case CHAR:
                            char charValue = (char) in.readUTFChar();
                            if (fd.field != null)
                                fd.field.setChar(instance, charValue);
                            break;
                        case INT:
                            int intValue = in.readCompactInt();
                            if (fd.field != null)
                                fd.field.setInt(instance, intValue);
                            break;
                        case LONG:
                            long longValue = in.readCompactLong();
                            if (fd.field != null)
                                fd.field.setLong(instance, longValue);
                            break;
                        case FLOAT:
                            float floatValue = in.readFloat();
                            if (fd.field != null)
                                fd.field.setFloat(instance, floatValue);
                            break;
                        case DOUBLE:
                            double doubleValue = in.readDouble();
                            if (fd.field != null)
                                fd.field.setDouble(instance, doubleValue);
                            break;
                        default:
                            throw new IOException("Invalid field type " + fd.type);
                    }
                } catch (IOException e) {
                    throw e;
                } catch (Exception e) {
                    System.out.println("Cannot set field " + fd.field.getName() + " on " + fd.field.getDeclaringClreplaced().getName() + ": " + e);
                }
                cur = cur.parent;
            } while (cur != null);
            return result;
        }

        private void skipInstanceFields(BufferedInput in) throws IOException {
            ClreplacedDesc cur = this;
            do {
                for (FieldDesc fd : cur.fields) switch(fd.type) {
                    case REFERENCE:
                        in.readCompactInt();
                        break;
                    case BOOLEAN:
                        in.readByte();
                        break;
                    case BYTE:
                        in.readByte();
                        break;
                    case SHORT:
                        in.readCompactInt();
                        break;
                    case CHAR:
                        in.readUTFChar();
                        break;
                    case INT:
                        in.readCompactInt();
                        break;
                    case LONG:
                        in.readCompactLong();
                        break;
                    case FLOAT:
                        in.readFloat();
                        break;
                    case DOUBLE:
                        in.readDouble();
                        break;
                    default:
                        throw new IOException("Invalid field type " + fd.type);
                }
                cur = cur.parent;
            } while (cur != null);
        }
    }
}

19 Source : TDP.java
with Mozilla Public License 2.0
from devexperts

/**
 * Parses full thread dumps and organizes result data into performance profile.
 * The results are written to a file - by default 'profile.txt'.
 */
@ToolSummary(info = "Parses full thread dumps and writes performance profile.", argString = "<files>", arguments = { "<files> -- list of files to parse" })
@ServiceProvider
public clreplaced TDP extends AbstractTool {

    private final Option locks = new Option('l', "locks", "Use full original stacktraces when concurrent lock in contended state.");

    private final OptionInteger topdepth = new OptionInteger('t', "topdepth", "<number>", "Depth of reverse stack trace in tops section, by default 50.", 0, 1000, 50);

    private final OptionInteger methoddepth = new OptionInteger('m', "methoddepth", "<number>", "Depth of stack trace in methods section, by default 50.", 0, 1000, 50);

    private final OptionDouble threshold = new OptionDouble('h', "threshold", "<number>", "Threshold for contribution to expand, by default 5 (%).", 0, 100, 5);

    private final OptionString state = new OptionString('s', "state", "<id>", "Limit report to a specific state, all states are reported by default");

    private final OptionString output = new OptionString('o', "output", "<file>", "Output file, by default 'profile.txt'.");

    @Override
    protected Option[] getOptions() {
        return new Option[] { locks, topdepth, methoddepth, state, output };
    }

    private static final String HEADER = "Profiling information at ";

    // -1 for all
    private int onlyThreadState = -1;

    @Override
    protected void executeImpl(String[] args) {
        if (args.length == 0)
            noArguments();
        if (state.isSet()) {
            for (int i = 0; i < THREAD_STATES.length; i++) {
                ThreadState threadState = THREAD_STATES[i];
                if (threadState.abbreviation.equalsIgnoreCase(state.getValue())) {
                    onlyThreadState = i;
                    break;
                }
            }
            if (onlyThreadState < 0)
                throw new OptionParseException("State abbreviation is not found: " + state.getValue());
        }
        try {
            for (String file : args) processFile(file);
            if (dumpsCount > 0)
                dumpToFile(output.isSet() ? output.getValue() : "profile.txt");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void processFile(String file) throws IOException {
        try (BufferedReader in = new BufferedReader(new FileReader(file), 100000)) {
            line = in.readLine();
            if (line != null && line.startsWith(HEADER)) {
                try (PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(file + "~"), 100000))) {
                    boolean top = false;
                    while (line != null) {
                        if (line.startsWith("-----"))
                            top = false;
                        else if (line.startsWith("Top "))
                            top = true;
                        int indent = 0;
                        while (indent < line.length() && (line.charAt(indent) == ' ' || line.charAt(indent) == '|')) indent++;
                        if (indent <= (top ? topdepth : methoddepth).getValue() * 2)
                            out.println(line);
                        line = in.readLine();
                    }
                }
            } else
                parse(in);
        }
    }

    // ========== Full Thread Dump metadata ==========
    private static final String DUMP_START = "Full thread dump";

    private static final String JAVA_LANG_THREAD_STATE = "java.lang.Thread.State:";

    private static final String STACK_LOCATION = "\tat ";

    // annotation for lock status (locked, waiting, eliminated, parking, etc)
    private static final String STACK_ANNOTATION = "\t- ";

    // method that is considered to call every thread
    private static final String METHOD_ROOT = "<root>";

    // special method name to profile CPU consumed by the method itself
    private static final String METHOD_THIS = "<this>";

    private static clreplaced ThreadState {

        public final String abbreviation;

        public final String state;

        public final String[] methods;

        ThreadState(String abbreviation, String state, String... methods) {
            this.abbreviation = abbreviation;
            this.state = state;
            this.methods = methods;
        }
    }

    private static final ThreadState[] THREAD_STATES = { // Actual thread states. Use 1 character names.
    new ThreadState("U", "unknown"), new ThreadState("R", "runnable"), new ThreadState("R", Thread.State.RUNNABLE.name()), new ThreadState("E", "waiting for monitor entry"), new ThreadState("E", Thread.State.BLOCKED.name()), new ThreadState("e", "waiting for lock entry"), // native code for Object.wait() in Java 1.4
    new ThreadState("W", "in Object.wait()", "java.lang.Object.wait"), new ThreadState("W", Thread.State.WAITING.name()), new ThreadState("w", "waiting on monitor"), new ThreadState("P", "parking", "sun.misc.Unsafe.park"), // Zing VM, also non-Unsafe parking
    new ThreadState("P", "waiting on VM lock", "java.util.concurrent.locks.LockSupport.park", "java.util.concurrent.locks.LockSupport.parkNanos", "java.util.concurrent.locks.LockSupport.parkUntil"), // going to sleeping or awakening in Java 1.5
    new ThreadState("S", "sleeping", "java.lang.Thread.sleep"), new ThreadState("S", Thread.State.TIMED_WAITING.name()), // sleeping in Java 1.4
    new ThreadState("s", "waiting on condition"), new ThreadState("d", "suspended"), new ThreadState("N", Thread.State.NEW.name()), new ThreadState("T", Thread.State.TERMINATED.name()), // Terminating IO methods which block as 'runnable'. Use additional states with 2+ character names.
    new ThreadState("FR", "File read", "java.io.FileInputStream.read", "java.io.FileInputStream.readBytes", "java.io.RandomAccessFile.read", "java.io.RandomAccessFile.readBytes"), new ThreadState("FW", "File write", "java.io.FileOutputStream.write", "java.io.FileOutputStream.writeBytes", "java.io.RandomAccessFile.write", "java.io.RandomAccessFile.writeBytes"), new ThreadState("SA", "Socket accept", "java.net.PlainSocketImpl.socketAccept"), new ThreadState("SC", "Socket connect", "java.net.PlainSocketImpl.socketConnect"), new ThreadState("SR", "Socket read", "java.net.SocketInputStream.socketRead", "java.net.SocketInputStream.socketRead0"), new ThreadState("SW", "Socket write", "java.net.SocketOutputStream.socketWrite", "java.net.SocketOutputStream.socketWrite0"), new ThreadState("NA", "NIO accept", "sun.nio.ch.ServerSocketChannelImpl.accept0"), new ThreadState("NS", "NIO select", "sun.nio.ch.PollArrayWrapper.poll0", "sun.nio.ch.DevPollArrayWrapper.poll0", "sun.nio.ch.EPollArrayWrapper.epollWait"), new ThreadState("DP", "Datagram peek", "java.net.PlainDatagramSocketImpl.peek", "java.net.PlainDatagramSocketImpl.peekData"), new ThreadState("DR", "Datagram receive", "java.net.PlainDatagramSocketImpl.receive", "java.net.PlainDatagramSocketImpl.receive0", "java.net.TwoStacksPlainDatagramSocketImpl.receive0", "java.net.DualStackPlainDatagramSocketImpl.socketReceiveOrPeekData"), new ThreadState("DS", "Datagram send", "java.net.PlainDatagramSocketImpl.send", "java.net.TwoStacksPlainDatagramSocketImpl.send", "java.net.DualStackPlainDatagramSocketImpl.socketSend") };

    private static final int WAITING_FOR_LOCK_ENTRY = findThreadState("waiting for lock entry", "", "");

    private static int findThreadState(String threadLine, String stateLine, String method) {
        for (int threadState = 0; threadState < THREAD_STATES.length; threadState++) for (String m : THREAD_STATES[threadState].methods) if (method.equals(m))
            return threadState;
        int k = threadLine.indexOf('"', 1);
        for (int threadState = 0; threadState < THREAD_STATES.length; threadState++) if (threadLine.indexOf(THREAD_STATES[threadState].state, k + 1) >= 0)
            return threadState;
        if (stateLine.length() != 0) {
            k = stateLine.indexOf(JAVA_LANG_THREAD_STATE) + JAVA_LANG_THREAD_STATE.length();
            for (int threadState = 0; threadState < THREAD_STATES.length; threadState++) if (stateLine.indexOf(THREAD_STATES[threadState].state, k + 1) >= 0)
                return threadState;
        }
        return 0;
    }

    private static clreplaced MethodStats {

        final String name;

        int totalCount;

        final int[] counts = new int[THREAD_STATES.length];

        // created on first need
        IndexedSet<String, MethodStats> children;

        MethodStats(String name) {
            this.name = name;
        }

        String getName() {
            return name;
        }

        IndexedSet<String, MethodStats> getChildren() {
            if (children == null)
                children = IndexedSet.create(MethodStats::getName);
            return children;
        }

        boolean contributes(int threadState, MethodStats outer, double thresholdPercent) {
            return counts[threadState] * 100.0 > outer.counts[threadState] * thresholdPercent;
        }

        boolean contributes(MethodStats outer, double thresholdPercent) {
            for (int i = 0; i < counts.length; i++) if (contributes(i, outer, thresholdPercent))
                return true;
            return false;
        }
    }

    private static final Comparator<MethodStats> METHOD_STATS_BY_TOTAL_COUNT = new Comparator<MethodStats>() {

        @Override
        public int compare(MethodStats o1, MethodStats o2) {
            return Long.compare(o2.totalCount, o1.totalCount);
        }
    };

    private static clreplaced Top implements Comparable<Top> {

        final MethodStats ms;

        final int count;

        Top(MethodStats ms, int count) {
            this.ms = ms;
            this.count = count;
        }

        @Override
        public int compareTo(Top other) {
            return other.count != count ? other.count - count : ms.name.compareTo(other.ms.name);
        }
    }

    private static final int TOP_METHODS = 30;

    // ========== Stats ==========
    private final IndexedSet<String, MethodStats> forwardStats = IndexedSet.create(MethodStats::getName);

    private final IndexedSet<String, MethodStats> reverseStats = IndexedSet.create(MethodStats::getName);

    private int dumpsCount;

    // ========== Parser internal state ==========
    // current line
    private String line;

    // current stack track
    private final List<String> methods = new ArrayList<>();

    // method names cache
    private final IndexedSet<String, String> cache = new IndexedSet<>();

    // seen method (rec) in current stack trace
    private final IndexedSet<String, String> seen = new IndexedSet<>();

    // current thread state
    private int threadState;

    private void parse(BufferedReader in) throws IOException {
        while (line != null) {
            if (!line.startsWith(DUMP_START)) {
                line = in.readLine();
                continue;
            }
            // Full thread dump has started.
            dumpsCount++;
            line = in.readLine();
            while (line != null) {
                // Loop for all threads.
                // Skip all empty lines.
                while (line != null && line.isEmpty()) line = in.readLine();
                // Break if line is not describing the next thread.
                if (line == null || line.charAt(0) != '"')
                    break;
                String threadLine = line;
                line = in.readLine();
                String stateLine = line != null && line.trim().startsWith(JAVA_LANG_THREAD_STATE) ? line : "";
                if (// Java 1.6 thread state info.
                stateLine.length() != 0)
                    line = in.readLine();
                readStackTrace(in);
                threadState = findThreadState(threadLine, stateLine, methods.get(0));
                // Collapse new "concurrent locks" stacktrace when lock in contended state (i.e. blocking wait).
                if (methods.get(0).equals("sun.misc.Unsafe.park") && !locks.isSet()) {
                    int i = 1;
                    while (i < methods.size() && methods.get(i).startsWith("java.util.concurrent.locks.")) i++;
                    String m = methods.get(i - 1);
                    if (m.startsWith("java.util.concurrent.locks.") && (m.endsWith(".lock") || m.endsWith(".lockInterruptibly") || m.endsWith(".tryLock")) || m.startsWith("java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire") || m.startsWith("java.util.concurrent.locks.AbstractQueuedSynchronizer.tryAcquire")) {
                        while (i < methods.size() && (methods.get(i).endsWith(".lock") || methods.get(i).endsWith(".lockInterruptibly") || methods.get(i).endsWith(".tryLock") || methods.get(i).endsWith(".acquire") || methods.get(i).endsWith(".tryAcquire"))) {
                            i++;
                        }
                        threadState = WAITING_FOR_LOCK_ENTRY;
                        methods.subList(0, i).clear();
                    }
                }
                // Check is were are collecting a single state only
                if (onlyThreadState < 0 || threadState == onlyThreadState)
                    updateStats();
            }
        }
    }

    private void readStackTrace(BufferedReader in) throws IOException {
        methods.clear();
        while (true) {
            while (line != null && (line.startsWith(STACK_ANNOTATION))) line = in.readLine();
            if (line == null || !line.startsWith(STACK_LOCATION) || line.indexOf('(') < 0)
                break;
            String methodName = line.substring(STACK_LOCATION.length(), line.indexOf('('));
            String cachedName = cache.getByKey(methodName);
            if (cachedName == null) {
                cache.add(methodName);
                cachedName = methodName;
            }
            methods.add(cachedName);
            line = in.readLine();
        }
        methods.add(METHOD_ROOT);
    }

    private void updateStats() {
        // collect reverse stack-trace stats from <this> (method at the top)
        updateStatsAt(reverseStats, 0);
        // reverse list to get forward stats
        Collections.reverse(methods);
        // add this to the end of forward stats
        methods.add(METHOD_THIS);
        // collect forward stats from each method, but this, keep track of recursion
        seen.clear();
        for (int i = methods.size() - 2; i >= 0; i--) if (seen.add(methods.get(i)))
            updateStatsAt(forwardStats, i);
    }

    private void updateStatsAt(IndexedSet<String, MethodStats> set, int i) {
        while (true) {
            MethodStats ms = inc(set, methods.get(i));
            if (++i >= methods.size())
                break;
            set = ms.getChildren();
        }
    }

    private MethodStats inc(IndexedSet<String, MethodStats> set, String name) {
        MethodStats ms = set.getByKey(name);
        if (ms == null)
            set.add(ms = new MethodStats(name));
        ms.totalCount++;
        ms.counts[threadState]++;
        return ms;
    }

    private void dumpToFile(String file) throws IOException {
        try (PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(file), 100000))) {
            out.println(HEADER + new Date());
            out.println(forwardStats.size() + " method(s) have been collected in " + dumpsCount + " dump(s)");
            dumpReverseTops(out);
            dumpForwardMethods(out);
        }
    }

    private void dumpReverseTops(PrintWriter out) {
        for (int threadState = 0; threadState < THREAD_STATES.length; threadState++) dumpReverseTopsForState(out, threadState);
    }

    private void dumpReverseTopsForState(PrintWriter out, int threadState) {
        PriorityQueue<Top> tops = new PriorityQueue<>();
        for (MethodStats ms : reverseStats) {
            int count = ms.counts[threadState];
            if (count != 0)
                tops.add(new Top(ms, count));
        }
        if (tops.isEmpty())
            return;
        out.println();
        out.println("----------------------------------------------------------------");
        out.println("Top " + TOP_METHODS + " methods with " + METHOD_THIS + " in '" + THREAD_STATES[threadState].state + "' state and reverse call stacks");
        int n = Math.min(tops.size(), TOP_METHODS);
        for (int i = 0; i < n; i++) {
            Top top = tops.poll();
            dumpTop(out, "", top, threadState);
            dumpTopDepth(out, "  ", top.ms, threadState, top.ms);
        }
    }

    private void dumpTopDepth(PrintWriter out, String indent, MethodStats method, int threadState, MethodStats outer) {
        if ((indent.length() >> 1) > topdepth.getValue())
            return;
        if (method.children == null)
            return;
        PriorityQueue<Top> tops = new PriorityQueue<>();
        int skipped = 0;
        for (MethodStats ms : method.children) if (ms.counts[threadState] != 0) {
            // Filter by contribution
            if (ms.contributes(threadState, outer, threshold.getValue()))
                tops.add(new Top(ms, ms.counts[threadState]));
            else
                skipped++;
        }
        int n = tops.size();
        for (int i = 0; i < n; i++) {
            Top top = tops.poll();
            dumpTop(out, indent, top, threadState);
            MethodStats ms = method.children.getByKey(top.ms.name);
            dumpTopDepth(out, indent + (i < n - 1 || skipped > 0 ? "| " : "  "), ms, threadState, outer);
        }
        if (skipped > 0)
            dumpSkipped(out, indent, skipped);
    }

    private void dumpTop(PrintWriter out, String indent, Top top, int threadState) {
        out.print(indent);
        out.print(THREAD_STATES[threadState].abbreviation);
        out.print(":");
        dumpCount(out, top.count);
        out.print(" - ");
        out.print(top.ms.name);
        out.println();
    }

    private void dumpForwardMethods(PrintWriter out) {
        out.println();
        out.println("================================================================");
        out.println("All methods with submethods and forward call stacks");
        MethodStats[] sorted = sort(forwardStats, METHOD_STATS_BY_TOTAL_COUNT);
        for (MethodStats ms : sorted) {
            out.println();
            dumpMethod(out, "", "+ ", ms);
            dumpMethodDepth(out, "  ", ms, ms);
        }
    }

    private void dumpMethodDepth(PrintWriter out, String indent, MethodStats method, MethodStats outer) {
        if ((indent.length() >> 1) > methoddepth.getValue())
            return;
        if (method.children == null)
            return;
        MethodStats[] sorted = sort(method.children, METHOD_STATS_BY_TOTAL_COUNT);
        // Filter by contribution
        int skipped = 0;
        int n = 0;
        for (int i = 0; i < sorted.length; i++) {
            if (sorted[i].contributes(outer, threshold.getValue()))
                sorted[n++] = sorted[i];
            else
                skipped++;
        }
        for (int i = 0; i < n; i++) {
            MethodStats ms = sorted[i];
            dumpMethod(out, indent, "- ", ms);
            dumpMethodDepth(out, indent + (i < n - 1 || skipped > 0 ? "| " : "  "), ms, outer);
        }
        if (skipped > 0)
            dumpSkipped(out, indent, skipped);
    }

    private void dumpMethod(PrintWriter out, String indent, String prefix, MethodStats method) {
        out.print(indent);
        out.print(prefix);
        out.print(method.name);
        int states = 0;
        for (int count : method.counts) if (count != 0)
            states++;
        if (states > 1) {
            out.print(" - ");
            dumpCount(out, method.totalCount);
        }
        out.print(" =");
        for (int threadState = 0; threadState < THREAD_STATES.length; threadState++) if (method.counts[threadState] != 0) {
            out.print(" ");
            out.print(THREAD_STATES[threadState].abbreviation);
            out.print(":");
            dumpCount(out, method.counts[threadState]);
        }
        out.println();
    }

    private void dumpSkipped(PrintWriter out, String indent, int skipped) {
        out.print(indent);
        out.print("... ");
        out.print(skipped);
        out.print(" more below threshold");
        out.println();
    }

    private void dumpCount(PrintWriter out, int count) {
        out.printf(Locale.US, "%d (%.2f%%)", count, 100.0 * count / dumpsCount);
    }

    @SuppressWarnings("unchecked")
    private static MethodStats[] sort(Collection<MethodStats> c, Comparator<MethodStats> comparator) {
        MethodStats[] a = c.toArray(new MethodStats[c.size()]);
        Arrays.sort(a, comparator);
        return a;
    }

    public static void main(String[] args) {
        Tools.executeSingleTool(TDP.clreplaced, args);
    }
}

19 Source : ConsistentLoadBalancer.java
with Mozilla Public License 2.0
from devexperts

/**
 * This strategy implements consistent hashing algorithm.
 */
public clreplaced ConsistentLoadBalancer implements RMILoadBalancer {

    private static final int DEFAULT_CAPACITY = SystemProperties.getIntProperty(ConsistentLoadBalancer.clreplaced, "defaultCapacity", 100);

    private static final int DEFAULT_PRIORITY = SystemProperties.getIntProperty(ConsistentLoadBalancer.clreplaced, "defaultPriority", 10);

    private static final int MAGIC = 0x9E3779B9;

    private final IndexedSet<RMIServiceId, RMIServiceDescriptor> descriptors = IndexedSet.create(RMIServiceDescriptor.INDEXER_BY_SERVICE_ID);

    private final NavigableMap<Integer, List<RMIServiceDescriptor>> ring = new TreeMap<>();

    @Nonnull
    @Override
    public synchronized Promise<BalanceResult> balance(@Nonnull RMIRequestMessage<?> request) {
        if (descriptors.size() == 0 || request.getTarget() != null)
            return Promise.completed(BalanceResult.route(request.getTarget()));
        if (descriptors.size() == 1)
            return Promise.completed(BalanceResult.route(descriptors.iterator().next().getServiceId()));
        if (ring.isEmpty())
            descriptors.forEach(this::addInRing);
        int requestKey = getRequestKey(request);
        Map.Entry<Integer, List<RMIServiceDescriptor>> ceiling = ring.ceilingEntry(requestKey);
        Map.Entry<Integer, List<RMIServiceDescriptor>> next = ceiling != null ? ceiling : ring.firstEntry();
        return Promise.completed(BalanceResult.route(next.getValue().get(0).getServiceId()));
    }

    @Override
    public synchronized void updateServiceDescriptor(@Nonnull RMIServiceDescriptor descriptor) {
        if (descriptor.isAvailable())
            processAvailableDescriptor(descriptor);
        else
            processUnavailableDescriptor(descriptor);
    }

    @Override
    public void close() {
    // No resource to release
    }

    @GuardedBy("this")
    private void processAvailableDescriptor(@Nonnull RMIServiceDescriptor descriptor) {
        RMIServiceDescriptor existing = descriptors.put(descriptor);
        if (existing == null) {
            if (!ring.isEmpty())
                addInRing(descriptor);
            return;
        }
        // Descriptor already exists, rebuild the ring if properties changed
        if (getCapacity(descriptor) != getCapacity(existing) || getPriority(descriptor) != getPriority(existing) || !Objects.equals(getShardName(descriptor), getShardName(existing))) {
            ring.clear();
        }
    }

    @GuardedBy("this")
    private void processUnavailableDescriptor(@Nonnull RMIServiceDescriptor descriptor) {
        if (!descriptors.remove(descriptor))
            return;
        ring.clear();
    }

    // can be overridden
    /**
     * Returns the capacity for the specified descriptor if it is set or returns the capacity of the default.
     * @param descriptor the specified service descriptor
     * @return the capacity for the specified descriptor if it is set or returns the capacity of the default
     */
    public int getCapacity(RMIServiceDescriptor descriptor) {
        return getIntProperty(descriptor, RMIServiceDescriptor.SERVICE_CAPACITY_PROPERTY, DEFAULT_CAPACITY);
    }

    // can be overridden
    /**
     * Returns the shard name for the specified descriptor if it is set or null.
     * @param descriptor the specified service descriptor
     * @return the shard name for the specified descriptor if it is set or null
     */
    public String getShardName(RMIServiceDescriptor descriptor) {
        return descriptor.getProperty(RMIServiceDescriptor.SERVICE_SHARD_PROPERTY);
    }

    // can be overridden
    /**
     * Returns the service seed for the specified descriptor if it is set or seed of the default.
     * @param descriptor the specified service descriptor
     * @return the service seed for the specified descriptor if it is set or seed of the default
     */
    public byte[] getServiceSeed(RMIServiceDescriptor descriptor) {
        String shard = getShardName(descriptor);
        return shard != null ? shard.getBytes(StandardCharsets.UTF_8) : descriptor.getServiceId().getBytes();
    }

    // can be overridden
    /**
     * Returns the service priority for the specified descriptor if it is set or priority of the default.
     * @param descriptor the specified service descriptor
     * @return the service priority for the specified descriptor if it is set or priority of the default
     */
    public int getPriority(RMIServiceDescriptor descriptor) {
        return getIntProperty(descriptor, RMIServiceDescriptor.SERVICE_PRIORITY_PROPERTY, DEFAULT_PRIORITY);
    }

    private int getIntProperty(RMIServiceDescriptor descriptor, String key, int def) {
        try {
            String value = descriptor.getProperty(key);
            return value == null || value.isEmpty() ? def : Integer.valueOf(value);
        } catch (NumberFormatException e) {
            return def;
        }
    }

    // can be overridden
    /**
     * Compares the shard names these service descriptors.
     * @param descriptor1 the first service descriptor to be compared
     * @param descriptor2 the second service descriptor to be compared
     * @return a negative integer, zero, or a positive integer as the first argument is less than, equal to,
     * or greater than the second.
     */
    public int compareInShard(RMIServiceDescriptor descriptor1, RMIServiceDescriptor descriptor2) {
        int pr1 = getPriority(descriptor1);
        int pr2 = getPriority(descriptor2);
        return pr1 < pr2 ? -1 : pr1 > pr2 ? 1 : descriptor1.getServiceId().compareTo(descriptor2.getServiceId());
    }

    private final Comparator<RMIServiceDescriptor> inShardComparator = this::compareInShard;

    // can be overridden
    /**
     * Returns key for the specified request.
     * @param request the request message
     * @return  key for the specified request
     */
    public int getRequestKey(RMIRequestMessage<?> request) {
        RMIRoute route = request.getRoute();
        if (route.isEmpty())
            return 0;
        return route.getFirst().hashCode() * MAGIC;
    }

    private void addInRing(RMIServiceDescriptor descriptor) {
        SecureRandom random;
        try {
            random = SecureRandom.getInstance("SHA1PRNG");
        } catch (NoSuchAlgorithmException e) {
            throw new replacedertionError(e);
        }
        random.setSeed(getServiceSeed(descriptor));
        int pos;
        for (int i = 0; i < getCapacity(descriptor); i++) {
            pos = random.nextInt();
            List<RMIServiceDescriptor> ids = ring.get(pos);
            if (ids == null) {
                // we'll almost never need more than 1 item (very low probability)
                ids = new ArrayList<>(1);
                ids.add(descriptor);
                ring.put(pos, ids);
            } else {
                int index = Collections.binarySearch(ids, descriptor, inShardComparator);
                if (index < 0) {
                    ids.add(-index - 1, descriptor);
                } else {
                    ids.add(index + 1, descriptor);
                }
            }
        }
    }
}

19 Source : ServerDescriptorsManager.java
with Mozilla Public License 2.0
from devexperts

@ThreadSafe
clreplaced ServerDescriptorsManager {

    private final RMIConnection connection;

    private volatile ServiceFilter services;

    private final IndexedSet<RMIServiceId, RMIServiceDescriptor> descriptors = IndexedSet.create(RMIServiceDescriptor.INDEXER_BY_SERVICE_ID);

    ServerDescriptorsManager(RMIConnection connection) {
        this.connection = connection;
        services = ServiceFilter.NOTHING;
    }

    void setServicesOnDescribeProtocolAndSendAllDescriptors(ServiceFilter services) {
        synchronized (this) {
            this.services = services;
        }
        if (connection.side.hreplacederver())
            connection.endpoint.getServer().sendAllDescriptorsToConnection(connection);
    }

    synchronized void addServiceDescriptors(List<RMIServiceDescriptor> descriptors) {
        if (!connection.adFilter.isSendAdvertisement()) {
            return;
        }
        boolean changed = false;
        for (RMIServiceDescriptor descriptor : descriptors) {
            if (!services.accept(descriptor.getServiceName()))
                continue;
            if (descriptor.getIntermediateNodes().contains(connection.getRemoteEndpointId())) {
                this.descriptors.add(RMIServiceDescriptor.createUnavailableDescriptor(descriptor.getServiceId(), descriptor.getProperties()));
                changed = true;
                continue;
            }
            this.descriptors.add(descriptor);
            changed = true;
        }
        if (changed)
            connection.messageAdapter.rmiMessageAvailable(RMIQueueType.ADVERTISE);
    }

    synchronized List<RMIServiceDescriptor> pollServiceDescriptors() {
        if (descriptors.isEmpty())
            return null;
        List<RMIServiceDescriptor> result = new ArrayList<>(descriptors.size());
        result.addAll(descriptors);
        descriptors.clear();
        return result;
    }

    synchronized int descriptorsSize() {
        return descriptors.size();
    }
}

19 Source : SentRequests.java
with Mozilla Public License 2.0
from devexperts

@ThreadSafe
clreplaced SentRequests {

    private final IndexedSet<Long, RMIRequestImpl<?>> channelRequests = IndexedSet.createLong((IndexerFunction.LongKey<RMIRequestImpl<?>>) RMIRequestImpl::getId);

    private final Map<Long, IndexedSet<Long, RMIRequestImpl<?>>> clientNestedRequests = new HashMap<>();

    private final Map<Long, IndexedSet<Long, RMIRequestImpl<?>>> serverNestedRequests = new HashMap<>();

    synchronized void addSentRequest(RMIRequestImpl<?> request) {
        if (!request.isNestedRequest()) {
            channelRequests.add(request);
            return;
        }
        Map<Long, IndexedSet<Long, RMIRequestImpl<?>>> map = request.getKind().hasClient() ? clientNestedRequests : serverNestedRequests;
        IndexedSet<Long, RMIRequestImpl<?>> set = map.get(request.getChannelId());
        if (set == null) {
            set = IndexedSet.createLong((IndexerFunction.LongKey<RMIRequestImpl<?>>) RMIRequestImpl::getId);
            map.put(request.getChannelId(), set);
        }
        set.add(request);
    }

    // if channelId = 0 => top-level request
    RMIRequestImpl<?> removeSentRequest(long channelId, long curRequestId, RMIMessageKind kind) {
        RMIRequestImpl<?> headRequest;
        RMIRequestImpl<?> result;
        IndexedSet<Long, RMIRequestImpl<?>> set;
        // Limit synchronized range to honor lock hierarchy with requestLock
        synchronized (this) {
            if (channelId != 0) {
                IndexedSet<Long, RMIRequestImpl<?>> requests = kind.hasClient() ? clientNestedRequests.get(channelId) : serverNestedRequests.get(channelId);
                result = requests != null ? requests.removeKey(curRequestId) : null;
                return result;
            }
            headRequest = channelRequests.removeKey(curRequestId);
            if (headRequest == null)
                return null;
            set = clientNestedRequests.remove(((RMIChannelImpl) headRequest.getChannel()).getChannelId());
        }
        if (set != null && !set.isEmpty()) {
            for (RMIRequestImpl<?> request : set) request.setFailedState(RMIExceptionType.CHANNEL_CLOSED, null);
        }
        return headRequest;
    }

    synchronized RMIRequestImpl<?>[] getSentRequests(RMIRequestImpl<?>[] requests) {
        return channelRequests.toArray(requests);
    }

    void close() {
        List<RMIRequestImpl<?>> allRequests = new ArrayList<>();
        // Limit synchronized range to honor lock hierarchy with requestLock
        synchronized (this) {
            for (IndexedSet<Long, RMIRequestImpl<?>> requests : clientNestedRequests.values()) allRequests.addAll(requests);
            clientNestedRequests.clear();
            for (IndexedSet<Long, RMIRequestImpl<?>> requests : serverNestedRequests.values()) allRequests.addAll(requests);
            serverNestedRequests.clear();
            allRequests.addAll(channelRequests);
            channelRequests.clear();
        }
        for (RMIRequestImpl<?> request : allRequests) {
            request.setFailedState(RMIExceptionType.DISCONNECTION, null);
        }
    }
}

19 Source : SentRequests.java
with Mozilla Public License 2.0
from devexperts

// if channelId = 0 => top-level request
RMIRequestImpl<?> removeSentRequest(long channelId, long curRequestId, RMIMessageKind kind) {
    RMIRequestImpl<?> headRequest;
    RMIRequestImpl<?> result;
    IndexedSet<Long, RMIRequestImpl<?>> set;
    // Limit synchronized range to honor lock hierarchy with requestLock
    synchronized (this) {
        if (channelId != 0) {
            IndexedSet<Long, RMIRequestImpl<?>> requests = kind.hasClient() ? clientNestedRequests.get(channelId) : serverNestedRequests.get(channelId);
            result = requests != null ? requests.removeKey(curRequestId) : null;
            return result;
        }
        headRequest = channelRequests.removeKey(curRequestId);
        if (headRequest == null)
            return null;
        set = clientNestedRequests.remove(((RMIChannelImpl) headRequest.getChannel()).getChannelId());
    }
    if (set != null && !set.isEmpty()) {
        for (RMIRequestImpl<?> request : set) request.setFailedState(RMIExceptionType.CHANNEL_CLOSED, null);
    }
    return headRequest;
}

19 Source : RunningTask.java
with Mozilla Public License 2.0
from devexperts

@ThreadSafe
clreplaced RunningTask {

    private final IndexedSet<Long, RMITaskImpl<?>> serverChannelTasks = IndexedSet.createLong(RMITaskImpl.TASK_INDEXER_BY_ID);

    private final EnumMap<RMIChannelType, Map<Long, IndexedSet<Long, RMITaskImpl<?>>>> mapNestedTask = new EnumMap<>(RMIChannelType.clreplaced);

    synchronized void add(RMITaskImpl<?> task) {
        if (!task.isNestedTask()) {
            serverChannelTasks.add(task);
            return;
        }
        IndexedSet<Long, RMITaskImpl<?>> set = getMap(task.getChannel().getType()).get(task.getChannelId());
        if (set == null) {
            set = IndexedSet.createLong(RMITaskImpl.TASK_INDEXER_BY_ID);
            getMap(task.getChannel().getType()).put(task.getChannelId(), set);
        }
        set.add(task);
    }

    // for inner task
    synchronized void remove(RMITaskImpl<?> task) {
        replacedert task.isNestedTask();
        IndexedSet<Long, RMITaskImpl<?>> set = getMap(task.getChannel().getType()).get(task.getChannelId());
        if (set == null)
            return;
        set.remove(task);
        if (set.isEmpty())
            getMap(task.getChannel().getType()).remove(task.getChannelId());
    }

    // for top-level tasks
    synchronized void remove(RMIChannelOwner owner, long channelId) {
        IndexedSet<Long, RMITaskImpl<?>> set = getMap(owner.getChannelType()).get(channelId);
        if (set != null && !set.isEmpty()) {
            for (RMITaskImpl<?> runTask : set) runTask.completeExceptionally(RMIExceptionType.CHANNEL_CLOSED, null);
        }
        if (owner.getChannelType() == RMIChannelType.SERVER_CHANNEL) {
            // noinspection SuspiciousMethodCalls
            serverChannelTasks.remove(owner);
        }
    }

    synchronized RMITaskImpl<?> removeById(long requestId, long channelId, RMIChannelType type) {
        if (channelId == 0)
            return serverChannelTasks.removeKey(requestId);
        IndexedSet<Long, RMITaskImpl<?>> set = getMap(type).get(channelId);
        if (set == null || set.isEmpty())
            return null;
        return set.removeKey(requestId);
    }

    synchronized Set<RMITaskImpl<?>> removeAllById(long channelId, RMIChannelType type) {
        IndexedSet<Long, RMITaskImpl<?>> set = getMap(type).get(channelId);
        if (set == null || set.isEmpty())
            return null;
        Set<RMITaskImpl<?>> result = new HashSet<>((getMap(type).get(channelId)));
        set.clear();
        return result;
    }

    @SuppressWarnings("unchecked")
    synchronized void close() {
        // task.cancel invokes runningTask.remove(task), so we need to avoid ConcurrentModificationException
        for (RMIChannelType type : RMIChannelType.values()) {
            IndexedSet<Long, RMITaskImpl<?>>[] mapNestedTasksArray = getMap(type).values().toArray(new IndexedSet[getMap(type).size()]);
            for (IndexedSet<Long, RMITaskImpl<?>> nestedTasks : mapNestedTasksArray) {
                RMITaskImpl<?>[] nestedTasksArray = nestedTasks.toArray(new RMITaskImpl[nestedTasks.size()]);
                for (RMITaskImpl<?> task : nestedTasksArray) task.cancel(RMIExceptionType.DISCONNECTION);
            }
        }
        RMITaskImpl<?>[] channelTasksArray = serverChannelTasks.toArray(new RMITaskImpl[serverChannelTasks.size()]);
        for (RMITaskImpl<?> task : channelTasksArray) task.cancel(RMIExceptionType.DISCONNECTION);
    }

    boolean hreplacederverChannelTask() {
        // volatile read
        return !serverChannelTasks.isEmpty();
    }

    private Map<Long, IndexedSet<Long, RMITaskImpl<?>>> getMap(RMIChannelType type) {
        return mapNestedTask.computeIfAbsent(type, k -> new HashMap<>());
    }
}

19 Source : RMIClientService.java
with Mozilla Public License 2.0
from devexperts

clreplaced RMIClientService extends ForwardService {

    @SuppressWarnings("rawtypes")
    private static final TypedKey<RMIRequest> REQUEST = new TypedKey<>();

    private final IndexedSet<RMIServiceId, RMIServiceDescriptor> descriptors = IndexedSet.create(RMIServiceDescriptor.INDEXER_BY_SERVICE_ID);

    private final List<RMIServiceDescriptorsListener> listeners = new CopyOnWriteArrayList<>();

    private final ServiceFilter filter;

    private final RMIClient client;

    RMIClientService(String serviceName, RMIClientImpl client) {
        super(serviceName, client.getPort(null));
        this.filter = ServiceFilter.valueOf(serviceName);
        this.client = client;
    }

    @Override
    public synchronized void addServiceDescriptorsListener(RMIServiceDescriptorsListener listener) {
        super.addServiceDescriptorsListener(listener);
        listeners.add(listener);
    }

    @Override
    public synchronized void removeServiceDescriptorsListener(RMIServiceDescriptorsListener listener) {
        super.removeServiceDescriptorsListener(listener);
        listeners.remove(listener);
    }

    @Override
    public void openChannel(RMITask<Object> task) {
        RMIRequest<?> request = client.getPort(task.getSubject()).createRequest(task.getRequestMessage());
        request.getChannel().addChannelHandler(new ForwardService("*", task.getChannel()));
        task.getChannel().addChannelHandler(new ForwardService("*", request.getChannel()));
        task.getTaskVariables().set(REQUEST, request);
    }

    @Override
    RMIRequest<?> createRequest(RMITask<?> task) {
        if (task.getRequestMessage().getRequestType() == RMIRequestType.ONE_WAY)
            return client.getPort(task.getSubject()).createRequest(task.getRequestMessage());
        return task.getTaskVariables().get(REQUEST);
    }

    // Must only use descriptors accepted by this service filter
    synchronized void updateDescriptors(List<RMIServiceDescriptor> descriptors) {
        for (RMIServiceDescriptor descriptor : descriptors) {
            if (descriptor.isAvailable()) {
                // replace the descriptor
                this.descriptors.add(descriptor);
            } else {
                this.descriptors.remove(descriptor);
            }
        }
        for (RMIServiceDescriptorsListener listener : listeners) try {
            listener.descriptorsUpdated(Collections.unmodifiableList(descriptors));
        } catch (Throwable t) {
            Logging.getLogging(RMIClientService.clreplaced).error("Failed to update service descriptors", t);
        }
    }

    @Nonnull
    @Override
    public List<RMIServiceDescriptor> getDescriptors() {
        // Note: we must use concurrent iterator to get a snapshot of descriptors (that are being modified),
        // so we use toArray that gives a concurrent snapshot of IndexedSet
        return Arrays.asList((RMIServiceDescriptor[]) descriptors.toArray(new RMIServiceDescriptor[descriptors.size()]));
    }

    @Override
    public boolean isAvailable() {
        return !descriptors.isEmpty();
    }

    ServiceFilter getFilter() {
        return filter;
    }
}

19 Source : PendingRequests.java
with Mozilla Public License 2.0
from devexperts

/**
 * Holds requests that cannot be sent/processed yet. There are two reasons for this: either load balancing is in
 * progress or load balancing completed but there is no route to the selected target.
 * <p>
 * This clreplaced is used both on client- and server-side. On the server side we don't have
 * {@link RMIRequest RMI request objects} and we keep {@link ServerRequestInfo} objects.
 * <p>
 * This clreplaced is thread-safe, however iteration operations are not atomic. See Javadoc for the methods for details.
 */
@ThreadSafe
clreplaced PendingRequests {

    private final IndexedSet<Long, PendingRequest> requests = SynchronizedIndexedSet.createLong(PendingRequest::getId);

    /**
     * Adds a new pending RMI request. The request is balanced - its tentative target is already determined by
     * the {@link RMILoadBalancer load balancer}. The request is kept until {@link #dropPendingRequest(long)} or
     * {@link #removeAllBalanced()} method is called.
     * @param pendingRequest RMI request
     */
    void addPendingRequest(@Nonnull RMIRequestImpl<?> pendingRequest) {
        requests.add(PendingRequest.fromRMIRequest(pendingRequest));
    }

    /**
     * Adds an incomplete request balancing promise for an {@link RMIRequest}.
     * The promise is kept until it is completed or cancelled. After the promise completes, a given action is
     * invoked (with no locks held).
     * @param rmiRequest RMI request that is being balanced
     * @param balancePromise a balancing promise
     * @param promiseCompletionAction action to be invoked when balancing completes. The action is not invoked
     *                                if the request has been dropped with {@link #dropPendingRequest(long)}
     */
    void addBalancePromise(@Nonnull RMIRequestImpl<?> rmiRequest, @Nonnull Promise<BalanceResult> balancePromise, @Nonnull BiConsumer<RMIRequestImpl<?>, Promise<BalanceResult>> promiseCompletionAction) {
        requests.add(PendingRequest.fromBalancePromise(rmiRequest, balancePromise));
        // We should not capture the request in whenDone handler so that even if a load balancer
        // leaks its promises we don't leak the replacedociated requests and they can be GCed when they are aborted
        long reqId = rmiRequest.getId();
        balancePromise.whenDone(result -> {
            // Any balancing promise completion requires removing the request from the 'being balanced' map
            PendingRequest pendingRequest = requests.removeKey(reqId);
            if (// someone already dropped the request
            pendingRequest == null)
                return;
            RMILog.logBalancingCompletion(pendingRequest.rmiRequest, balancePromise);
            promiseCompletionAction.accept(pendingRequest.rmiRequest, balancePromise);
        });
    }

    /**
     * Adds an incomplete request balancing promise for an {@link ServerRequestInfo}.
     * The promise is kept until it is completed or cancelled. After the promise completes, a given action is
     * invoked (with no locks held).
     * @param requestInfo request that is being balanced
     * @param balancePromise a balancing promise
     * @param promiseCompletionAction action to be invoked when balancing completes. The action is not invoked
     *                                if the request has been dropped with {@link #dropPendingRequest(long)}
     */
    void addBalancePromise(@Nonnull ServerRequestInfo requestInfo, @Nonnull Promise<BalanceResult> balancePromise, @Nonnull BiConsumer<ServerRequestInfo, Promise<BalanceResult>> promiseCompletionAction) {
        requests.add(PendingRequest.fromBalancePromise(requestInfo, balancePromise));
        long reqId = requestInfo.reqId;
        balancePromise.whenDone(result -> {
            // Any balancing promise completion requires removing the request from the 'being balanced' map
            PendingRequest pendingRequest = requests.removeKey(reqId);
            if (// someone already dropped the request
            pendingRequest == null)
                return;
            RMILog.logBalancingCompletion(requestInfo, balancePromise);
            promiseCompletionAction.accept(pendingRequest.serverRequestInfo, balancePromise);
        });
    }

    /**
     * Drops a request with the given id. If there is a pending balance request promise, it is cancelled.
     * This method is to support manual/timeout request cancellation.
     * @param requestId id of the request per {@link RMIRequestImpl#getId()} or {@link ServerRequestInfo#reqId}.
     * @return true if the request has been present, false otherwise
     */
    boolean dropPendingRequest(long requestId) {
        PendingRequest pendingRequest = requests.removeKey(requestId);
        if (pendingRequest != null && pendingRequest.balancePromise != null)
            pendingRequest.balancePromise.cancel();
        return pendingRequest != null;
    }

    /**
     * @return number of pending requests and requests being balanced
     */
    int size() {
        return requests.size();
    }

    /**
     * Preplacedes each pending balanced {@link RMIRequestImpl RMI request} to the given consumer. Skips requests
     * that are being balanced.
     * Note that this operation is not atomic: if requests are {@link #addPendingRequest(RMIRequestImpl) added}
     * concurrently they might be missed by the consumer. Apply external locking and prevent concurrent request
     * addition if you need atomicity.
     * @param consumer consumer
     */
    void forEachRMIRequest(@Nonnull Consumer<RMIRequestImpl<?>> consumer) {
        for (Iterator<PendingRequest> it = requests.concurrenreplacederator(); it.hasNext(); ) {
            PendingRequest pendingRequest = it.next();
            if (pendingRequest.rmiRequest != null)
                consumer.accept(pendingRequest.rmiRequest);
        }
    }

    /**
     * Preplacedes each load balance promise to the given consumer. Skips pending requests that are already balanced.
     * Note that this operation is not atomic: if promises are
     * {@link #addBalancePromise(RMIRequestImpl, Promise, BiConsumer)} added} concurrently they might be missed
     * by the consumer. Apply external locking and prevent concurrent promises addition if you need atomicity.
     * @param consumer consumer
     */
    private void forEachBalancePromise(@Nonnull Consumer<Promise<BalanceResult>> consumer) {
        for (Iterator<PendingRequest> it = requests.concurrenreplacederator(); it.hasNext(); ) {
            PendingRequest pendingRequest = it.next();
            if (pendingRequest.balancePromise != null)
                consumer.accept(pendingRequest.balancePromise);
        }
    }

    /**
     * Removes all pending {@link RMIRequestImpl requests} that are already balanced. Note that this operation is not
     * atomic: if requests are {@link #addPendingRequest(RMIRequestImpl) added} concurrently they might
     * remain after this method completes. Apply external synchronization and prevent concurrent request addition
     * if you need atomicity.
     * @return list of all balanced pending requests or empty list
     */
    List<RMIRequestImpl<?>> removeAllBalanced() {
        List<RMIRequestImpl<?>> result = new ArrayList<>();
        for (Iterator<PendingRequest> it = requests.concurrenreplacederator(); it.hasNext(); ) {
            PendingRequest pendingRequest = it.next();
            if (pendingRequest.balancePromise == null) {
                it.remove();
                result.add(pendingRequest.rmiRequest);
            }
        }
        return result;
    }

    /**
     * Clears all pending requests cancelling balance promises
     */
    void clear() {
        forEachBalancePromise(Promise::cancel);
        requests.clear();
    }

    private static clreplaced PendingRequest {

        final Promise<BalanceResult> balancePromise;

        final RMIRequestImpl<?> rmiRequest;

        final ServerRequestInfo serverRequestInfo;

        PendingRequest(Promise<BalanceResult> balancePromise, RMIRequestImpl<?> rmiRequest, ServerRequestInfo serverRequestInfo) {
            replacedert rmiRequest != null || serverRequestInfo != null;
            this.balancePromise = balancePromise;
            this.rmiRequest = rmiRequest;
            this.serverRequestInfo = serverRequestInfo;
        }

        static PendingRequest fromBalancePromise(RMIRequestImpl<?> request, Promise<BalanceResult> balancePromise) {
            return new PendingRequest(balancePromise, request, null);
        }

        static PendingRequest fromBalancePromise(ServerRequestInfo request, Promise<BalanceResult> balancePromise) {
            return new PendingRequest(balancePromise, null, request);
        }

        static PendingRequest fromRMIRequest(RMIRequestImpl<?> request) {
            return new PendingRequest(null, request, null);
        }

        long getId() {
            return rmiRequest != null ? rmiRequest.getId() : serverRequestInfo.reqId;
        }
    }
}

19 Source : ClientDescriptorsManager.java
with Mozilla Public License 2.0
from devexperts

public clreplaced ClientDescriptorsManager {

    @GuardedBy("RMIClientImpl.services")
    private final IndexedSet<RMIServiceId, RMIServiceDescriptor> serviceDescriptors = IndexedSet.create(RMIServiceDescriptor.INDEXER_BY_SERVICE_ID);

    @GuardedBy("RMIClientImpl.services")
    void updateDescriptors(List<RMIServiceDescriptor> descriptors) {
        for (RMIServiceDescriptor descriptor : descriptors) {
            if (descriptor.isAvailable())
                serviceDescriptors.add(descriptor);
            else
                serviceDescriptors.removeValue(descriptor);
        }
    }

    @GuardedBy("RMIClientImpl.services")
    List<RMIServiceDescriptor> clearDescriptors() {
        List<RMIServiceDescriptor> result = new ArrayList<>(serviceDescriptors);
        serviceDescriptors.clear();
        return result;
    }
}

19 Source : AbstractServiceDescriptorsProcessor.java
with Mozilla Public License 2.0
from devexperts

abstract clreplaced AbstractServiceDescriptorsProcessor implements RMIServiceDescriptorsListener, Runnable {

    private final Logging log = Logging.getLogging(getClreplaced());

    private final Executor executor;

    @GuardedBy("this")
    private final IndexedSet<RMIServiceId, RMIServiceDescriptor> descriptors = IndexedSet.create(RMIServiceDescriptor.INDEXER_BY_SERVICE_ID);

    private final AtomicInteger scheduled = new AtomicInteger(0);

    AbstractServiceDescriptorsProcessor(Executor executor) {
        this.executor = executor;
    }

    @Override
    public void descriptorsUpdated(List<RMIServiceDescriptor> descriptors) {
        synchronized (this) {
            this.descriptors.addAll(descriptors);
        }
        // do it outside of lock
        boolean schedule = scheduled.getAndIncrement() == 0;
        if (RMIEndpointImpl.RMI_TRACE_LOG)
            log.trace("Update descriptors by " + this + ", descriptors=" + descriptors + ", schedule=" + schedule);
        if (schedule)
            executor.execute(this);
    }

    @Override
    public void run() {
        int oldScheduled;
        do {
            oldScheduled = scheduled.get();
            List<RMIServiceDescriptor> descriptors = takeDescriptors();
            if (RMIEndpointImpl.RMI_TRACE_LOG)
                log.trace("Process descriptors by " + this + ", descriptors=" + descriptors);
            process(descriptors);
        } while (!scheduled.compareAndSet(oldScheduled, 0));
    }

    protected abstract void process(List<RMIServiceDescriptor> descriptors);

    private synchronized List<RMIServiceDescriptor> takeDescriptors() {
        List<RMIServiceDescriptor> result = new ArrayList<>(this.descriptors);
        this.descriptors.clear();
        return result;
    }
}

19 Source : ConnectOrder.java
with Mozilla Public License 2.0
from devexperts

/**
 * ConnectOrder specifies a strategy of considering specified addresses in {@link ClientSocketConnector} during
 * connect/reconnect.
 */
public clreplaced ConnectOrder {

    private static final IndexedSet<String, ConnectOrder> VALUES = IndexedSet.create(ConnectOrder::getName);

    public static final ConnectOrder SHUFFLE = register("shuffle", true, false);

    public static final ConnectOrder RANDOM = register("random", true, true);

    public static final ConnectOrder ORDERED = register("ordered", false, false);

    public static final ConnectOrder PRIORITY = register("priority", false, true);

    private final String name;

    private final boolean randomized;

    private final boolean resetOnConnect;

    private ConnectOrder(String name, boolean randomized, boolean resetOnConnect) {
        this.name = Objects.requireNonNull(name);
        this.randomized = randomized;
        this.resetOnConnect = resetOnConnect;
    }

    @Override
    public int hashCode() {
        return name.hashCode();
    }

    @Override
    public boolean equals(Object o) {
        if (this == o)
            return true;
        if (o == null || getClreplaced() != o.getClreplaced())
            return false;
        ConnectOrder that = (ConnectOrder) o;
        return name.equals(that.name);
    }

    private static ConnectOrder register(String name, boolean randomized, boolean resetOnConnect) {
        ConnectOrder order = new ConnectOrder(name, randomized, resetOnConnect);
        if (VALUES.put(order) != null)
            throw new IllegalArgumentException("Duplicate ConnectOrder name '" + name + "'");
        return order;
    }

    public static ConnectOrder valueOf(String name) {
        if (name == null || name.isEmpty())
            return null;
        ConnectOrder connectOrder = VALUES.getByKey(name);
        if (connectOrder == null)
            throw new InvalidFormatException("Unknown ConnectOrder '" + name + "'");
        return connectOrder;
    }

    public String getName() {
        return name;
    }

    @Override
    public String toString() {
        return name;
    }

    boolean isRandomized() {
        return randomized;
    }

    boolean isResetOnConnect() {
        return resetOnConnect;
    }
}

19 Source : ThreadNameFormatter.java
with Mozilla Public License 2.0
from devexperts

clreplaced ThreadNameFormatter implements Comparable<ThreadNameFormatter> {

    /**
     * Configuration as a set of pairs (pattern, replacement).
     */
    private static final Map<Pattern, String> PATTERNS = new LinkedHashMap<>();

    private static final int MAX_NAME_CONVERSIONS_CACHE_SIZE = 1000;

    /**
     * Thread name replacement cache: (thread name, replacement string).
     */
    private static final IndexedSet<String, ThreadNameFormatter> NAME_CONVERSIONS = SynchronizedIndexedSet.create(ThreadNameFormatter::getThreadName);

    static {
        loadPatterns();
    }

    private static void loadPatterns() {
        InputStream config_input_stream = null;
        try {
            String config_path = DefaultLogging.getProperty(LogFormatter.CONFIG_FILE_PROPERTY, null);
            if (config_path != null)
                try {
                    config_input_stream = new URLInputStream(config_path);
                } catch (IOException e) {
                    System.err.println("Cannot find log formatter configuration file: '" + config_path + "'");
                    System.err.println("No thread name conversion will be performed.");
                    return;
                }
            else
                config_input_stream = LogFormatter.clreplaced.getResourcereplacedtream(LogFormatter.DEFAULT_CONFIG_FILE);
            if (config_input_stream == null)
                return;
            BufferedReader reader = new BufferedReader(new InputStreamReader(config_input_stream, Charset.forName("ISO-8859-1")));
            Pattern config_line_pattern = Pattern.compile("((?:[^=]|(?:\\\\=))*[^\\\\=])=(.*)");
            Pattern whitespace_line_pattern = Pattern.compile("\\s*");
            Pattern comment_line_pattern = Pattern.compile("#.*|!.*");
            String line;
            Set<String> patterns_set = new HashSet<String>();
            while ((line = reader.readLine()) != null) {
                Matcher config_line_matcher = config_line_pattern.matcher(line);
                // If it is whitespace or comment line
                if (whitespace_line_pattern.matcher(line).matches() || comment_line_pattern.matcher(line).matches())
                    continue;
                if (!config_line_matcher.matches()) {
                    System.err.println("The following line cannot be parsed in log formatter configuration file: '" + line + "'");
                    continue;
                }
                String config_pattern = config_line_matcher.group(1);
                String config_replacement = config_line_matcher.group(2);
                if (!patterns_set.add(config_pattern)) {
                    System.err.println("Duplicate pattern found in log formatter configuration file: '" + config_pattern + "'");
                    continue;
                }
                try {
                    PATTERNS.put(Pattern.compile(config_pattern), config_replacement);
                } catch (PatternSyntaxException e) {
                    System.err.println("Cannot parse config pattern in log formatter configuration file: '" + config_pattern + "'");
                }
            }
        } catch (IOException e) {
            // Do not wish to log using logger until initialization has completed.
            System.err.println("Cannot read log formatter configuration file");
            e.printStackTrace(System.err);
        } finally {
            try {
                if (config_input_stream != null) {
                    config_input_stream.close();
                }
            } catch (IOException e) {
                // Do not wish to log using logger until initialization has completed.
                System.err.println("Cannot close log formatter configuration file");
                e.printStackTrace(System.err);
            }
        }
    }

    /**
     * Formats thread name according to thread name conversion rules.
     *
     * @return Formatted thread name
     */
    static String formatThreadName(long time, String thread_name) {
        ThreadNameFormatter entry = NAME_CONVERSIONS.getByKey(thread_name);
        if (entry == null) {
            cleanupNameConversionsIfNeeded();
            entry = new ThreadNameFormatter(thread_name, calculateThreadNameReplacement(thread_name));
            NAME_CONVERSIONS.put(entry);
        }
        entry.last_time = time;
        return entry.replacement_name;
    }

    private static void cleanupNameConversionsIfNeeded() {
        if (NAME_CONVERSIONS.size() <= MAX_NAME_CONVERSIONS_CACHE_SIZE)
            // everything is Ok
            return;
        synchronized (NAME_CONVERSIONS) {
            if (NAME_CONVERSIONS.size() <= MAX_NAME_CONVERSIONS_CACHE_SIZE)
                // everything is Ok
                return;
            ThreadNameFormatter[] entries = NAME_CONVERSIONS.toArray(new ThreadNameFormatter[NAME_CONVERSIONS.size()]);
            QuickSort.sort(entries);
            for (int i = 0; i < entries.length - MAX_NAME_CONVERSIONS_CACHE_SIZE / 2; i++) NAME_CONVERSIONS.removeKey(entries[i].thread_name);
        }
    }

    private static String calculateThreadNameReplacement(String thread_name) {
        for (Map.Entry<Pattern, String> entry : PATTERNS.entrySet()) {
            Matcher matcher = entry.getKey().matcher(thread_name);
            if (matcher.matches()) {
                String config_replacement = entry.getValue();
                try {
                    return matcher.replaceAll(config_replacement);
                } catch (IndexOutOfBoundsException e) {
                    // The replacement string refers to a capturing group that does not exist in the pattern.
                    // To prevent cycling log it as is.
                    // Incorrect replacement. Just use thread name.
                    System.err.println("Cannot parse replacement string in log formatter configuration file: '" + config_replacement + "'");
                }
            }
        }
        return thread_name;
    }

    final String thread_name;

    final String replacement_name;

    // Atomicity, visibility and consistency of this field are unimportant.
    long last_time;

    ThreadNameFormatter(String thread_name, String replacement_name) {
        this.thread_name = thread_name;
        this.replacement_name = replacement_name;
    }

    private String getThreadName() {
        return thread_name;
    }

    public int compareTo(ThreadNameFormatter o) {
        return last_time < o.last_time ? -1 : last_time > o.last_time ? 1 : 0;
    }
}

19 Source : SSEConnection.java
with Mozilla Public License 2.0
from devexperts

/**
 * Basic connection clreplaced for Server-Sent Events.
 * It maintains a list of connections and static {@link #checkAndHeartbeatAll} method.
 */
public abstract clreplaced SSEConnection implements AsyncListener, Serializable {

    protected static final Logging log = Logging.getLogging(SSEConnection.clreplaced);

    private static final long serialVersionUID = 0;

    public static final long HEARTBEAT_PERIOD = TimePeriod.valueOf(SystemProperties.getProperty(EventsServlet.clreplaced, "heartbeatPeriod", "10s")).getTime();

    private static final IndexedSet<SSEConnection, SSEConnection> CONNECTIONS = new SynchronizedIndexedSet<>();

    private static final AtomicLong CONNECTION_ID = new AtomicLong();

    public static final String CONTENT_TYPE = "text/event-stream";

    private static final byte[] LINE_START = "data: ".getBytes(StandardCharsets.UTF_8);

    private static final byte LINE_END = 0x0a;

    private static final byte MESSAGE_END = 0x0a;

    // ========================== instance fields ==========================
    protected final long id;

    @GuardedBy("this")
    protected transient volatile boolean active;

    @GuardedBy("this")
    protected transient Output out;

    @GuardedBy("this")
    protected transient AsyncContext async;

    protected transient volatile long lastMessageTime;

    protected SSEConnection() {
        this.id = CONNECTION_ID.incrementAndGet();
    }

    public boolean isActive() {
        return active;
    }

    protected synchronized boolean start(AsyncContext async) throws IOException {
        boolean wasActive = active;
        stopSync();
        active = true;
        ServletResponse resp = async.getResponse();
        resp.setContentType(CONTENT_TYPE);
        // immediately commit headers
        resp.flushBuffer();
        this.async = async;
        this.out = new Output(resp.getOutputStream());
        CONNECTIONS.add(this);
        // no timeout
        async.setTimeout(0);
        async.addListener(this);
        startImpl();
        return wasActive;
    }

    protected boolean stop() {
        if (!active)
            return false;
        return stopSync();
    }

    private synchronized boolean stopSync() {
        if (!active)
            return false;
        active = false;
        CONNECTIONS.remove(this);
        stopImpl();
        async.complete();
        async = null;
        out = null;
        return true;
    }

    public void heartbeat() {
        if (System.currentTimeMillis() >= lastMessageTime + HEARTBEAT_PERIOD)
            heartbeatImpl();
    }

    @GuardedBy("this")
    protected abstract void startImpl();

    @GuardedBy("this")
    protected abstract void stopImpl();

    protected abstract void heartbeatImpl();

    @Override
    public void onComplete(AsyncEvent event) throws IOException {
        if (stop())
            log.info("Stopped, because of onComplete " + this);
    }

    @Override
    public void onTimeout(AsyncEvent event) throws IOException {
        if (stop())
            log.info("Stopped, because of onTimeout " + this);
    }

    @Override
    public void onError(AsyncEvent event) throws IOException {
        if (stop())
            log.info("Stopped, because of onError " + this);
    }

    @Override
    public void onStartAsync(AsyncEvent event) throws IOException {
    }

    public static void checkAndHeartbeatAll() {
        for (Iterator<SSEConnection> it = CONNECTIONS.concurrenreplacederator(); it.hasNext(); ) {
            it.next().heartbeat();
        }
    }

    protected clreplaced Output extends FilterOutputStream {

        boolean inLine;

        boolean crSeen;

        Output(OutputStream out) {
            super(out);
        }

        private void startLine() throws IOException {
            if (!inLine) {
                inLine = true;
                super.write(LINE_START);
            }
        }

        private void endLine() throws IOException {
            if (inLine) {
                inLine = false;
                super.write(LINE_END);
            }
        }

        @Override
        public void write(int b) throws IOException {
            switch(b) {
                case 0x0d:
                    crSeen = true;
                    startLine();
                    endLine();
                    break;
                case 0x0a:
                    if (crSeen)
                        crSeen = false;
                    else {
                        startLine();
                        endLine();
                    }
                    break;
                default:
                    crSeen = false;
                    startLine();
                    super.write(b);
            }
        }

        @Override
        public void flush() throws IOException {
        // will flush only on endMessage
        }

        @Override
        public void close() throws IOException {
        // is not closeable directly
        }

        public void endMessage() throws IOException {
            endLine();
            super.write(MESSAGE_END);
            super.flush();
            lastMessageTime = System.currentTimeMillis();
        }
    }
}

19 Source : QuoteBoardTableModel.java
with Mozilla Public License 2.0
from devexperts

clreplaced QuoteBoardTableModel implements TableModel {

    private final ArrayList<QuoteBoardTableRow> rows = new ArrayList<QuoteBoardTableRow>();

    private final IndexedSet<String, QuoteBoardTableRow> rowsBySymbol = IndexedSet.create((IndexerFunction<String, QuoteBoardTableRow>) row -> row.symbol);

    private final Set<TableModelListener> modelListeners = new HashSet<TableModelListener>();

    private final SubscriptionChangeListener subscriptionChangeListener;

    private TimeZone timeZone = TimeZone.getDefault();

    private boolean frozen = false;

    QuoteBoardTableModel(SubscriptionChangeListener subscriptionChangeListener) {
        this.subscriptionChangeListener = subscriptionChangeListener;
    }

    public void setSymbols(String symbolsStr) {
        clearInternal();
        if (symbolsStr == null || symbolsStr.isEmpty())
            return;
        for (String symbol : symbolsStr.split(",")) addRowInternal(rows.size(), symbol);
        fireTableChanged(new TableModelEvent(this));
    }

    public String getSymbols() {
        if (rows.size() == 0)
            return "";
        StringBuilder result = new StringBuilder();
        for (QuoteBoardTableRow row : rows) result.append(row.symbol).append(',');
        result.setLength(result.length() - 1);
        return result.toString();
    }

    public void clear() {
        clearInternal();
        fireTableChanged(new TableModelEvent(this));
    }

    public void addRow(int rowIndex) {
        addRowInternal(rowIndex, "");
        fireTableChanged(new TableModelEvent(this, rowIndex, rowIndex, TableModelEvent.ALL_COLUMNS, TableModelEvent.INSERT));
    }

    public QuoteBoardTableRow getRowAt(int rowIndex) {
        return rows.get(rowIndex);
    }

    public void removeRows(int[] rowIndexes) {
        if (rowIndexes.length == 0)
            return;
        Arrays.sort(rowIndexes);
        for (int i = rowIndexes.length - 1; i >= 0; i--) removeRowInternal(rowIndexes[i]);
        fireTableChanged(new TableModelEvent(this, rowIndexes[0], rowIndexes[rowIndexes.length - 1], TableModelEvent.ALL_COLUMNS, TableModelEvent.DELETE));
    }

    public void swapRows(int row1, int row2) {
        if (row1 == row2)
            return;
        QuoteBoardTableRow row = rows.get(row1);
        rows.set(row1, rows.get(row2));
        rows.set(row2, row);
        updateRowIndexes();
        fireTableChanged(new TableModelEvent(this, Math.min(row1, row2), Math.max(row1, row2)));
    }

    private void clearInternal() {
        while (!rows.isEmpty()) removeRowInternal(rows.size() - 1);
    }

    private void addRowInternal(int rowIndex, String symbol) {
        QuoteBoardTableRow row = rowsBySymbol.getByKey(symbol);
        if (row == null) {
            row = new QuoteBoardTableRow(symbol);
            rowsBySymbol.add(row);
            if (symbol.length() > 0)
                subscriptionChangeListener.addSymbol(symbol);
        }
        rows.add(rowIndex, row);
        updateRowIndexes();
    }

    private void removeRowInternal(int rowIndex) {
        QuoteBoardTableRow row = rows.remove(rowIndex);
        if (row.indexes.size() < 2) {
            rowsBySymbol.removeValue(row);
            if (row.symbol.length() > 0)
                subscriptionChangeListener.removeSymbol(row.symbol);
        }
        updateRowIndexes();
    }

    private void updateRowIndexes() {
        for (QuoteBoardTableRow row : rowsBySymbol) row.indexes.clear();
        for (int i = 0; i < rows.size(); i++) rows.get(i).indexes.add(i);
    }

    private void fireTableChanged(TableModelEvent event) {
        for (TableModelListener listener : modelListeners) listener.tableChanged(event);
    }

    // -------------------- Events processing --------------------
    private int prevMinIndex = Integer.MAX_VALUE;

    private int prevMaxIndex = Integer.MIN_VALUE;

    public void eventsReceived(List<? extends MarketEvent> events) {
        if (frozen)
            return;
        long curTime = System.currentTimeMillis();
        int minRowIndex = Integer.MAX_VALUE;
        int maxRowIndex = Integer.MIN_VALUE;
        for (MarketEvent event : events) {
            QuoteBoardTableRow row;
            row = rowsBySymbol.getByKey(event.getEventSymbol());
            if (row == null)
                continue;
            Clreplaced<? extends MarketEvent> eventClreplaced = event.getClreplaced();
            if (eventClreplaced == Quote.clreplaced)
                row.updateQuote((Quote) event, curTime);
            else if (eventClreplaced == Trade.clreplaced)
                row.updateTrade((Trade) event, curTime);
            else if (eventClreplaced == Summary.clreplaced)
                row.updateSummary((Summary) event, curTime);
            else if (eventClreplaced == Profile.clreplaced)
                row.updateProfile((Profile) event);
            for (int index : row.indexes) {
                minRowIndex = Math.min(minRowIndex, index);
                maxRowIndex = Math.max(maxRowIndex, index);
            }
        }
        int oldMinIndex = prevMinIndex;
        int oldMaxIndex = prevMaxIndex;
        prevMinIndex = minRowIndex;
        prevMaxIndex = maxRowIndex;
        minRowIndex = Math.min(minRowIndex, oldMinIndex);
        maxRowIndex = Math.max(maxRowIndex, oldMaxIndex);
        if (minRowIndex != Integer.MAX_VALUE)
            fireTableChanged(new TableModelEvent(this, minRowIndex, maxRowIndex));
    }

    // -------------------- TableModel implementation --------------------
    public int getRowCount() {
        return rows.size();
    }

    public int getColumnCount() {
        return QuoteBoardTableColumn.values().length;
    }

    public String getColumnName(int columnIndex) {
        return QuoteBoardTableColumn.values()[columnIndex].caption;
    }

    public Clreplaced<?> getColumnClreplaced(int columnIndex) {
        return Object.clreplaced;
    }

    public boolean isCellEditable(int rowIndex, int columnIndex) {
        return columnIndex == QuoteBoardTableColumn.SYMBOL.ordinal();
    }

    public Object getValueAt(int rowIndex, int columnIndex) {
        return QuoteBoardTableColumn.values()[columnIndex].getValue(rows.get(rowIndex), timeZone);
    }

    public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
        if (columnIndex != QuoteBoardTableColumn.SYMBOL.ordinal())
            return;
        String symbol = (String) aValue;
        if (symbol.equals(symbol.toLowerCase()))
            symbol = symbol.toUpperCase();
        removeRowInternal(rowIndex);
        addRowInternal(rowIndex, symbol);
        fireTableChanged(new TableModelEvent(this, rowIndex));
    }

    public void addTableModelListener(TableModelListener l) {
        modelListeners.add(l);
    }

    public void removeTableModelListener(TableModelListener l) {
        modelListeners.remove(l);
    }

    boolean isFrozen() {
        return frozen;
    }

    void setFrozen(boolean frozen) {
        this.frozen = frozen;
    }

    TimeZone getTimeZone() {
        return timeZone;
    }

    void setTimeZone(TimeZone timeZone) {
        this.timeZone = timeZone;
        fireTableChanged(new TableModelEvent(this));
    }
}

19 Source : Current.java
with Mozilla Public License 2.0
from devexperts

clreplaced Current {

    final IndexedSet<Key, Key> subscription = IndexedSet.create();

    final IndexedSet<Key, CurrentSegment> segments = IndexedSet.create(CurrentSegment.KEY_INDEXER);

    // last Cache.version it was rebuild for
    long version;

    long time;

    long startTime;

    long endTime;

    long size;

    double replaySpeed = 1;

    boolean isCurrentInterval(long time) {
        return time >= startTime && time < endTime;
    }

    void resetInterval() {
        startTime = Long.MIN_VALUE;
        endTime = Long.MAX_VALUE;
    }
}

19 Source : Cache.java
with Mozilla Public License 2.0
from devexperts

clreplaced Cache implements Runnable {

    private static final String FILE_CACHE_NAME = "mdrcache";

    private static final String FILE_CACHE_NAME_SUFFIX = ".tmp";

    // "MDRCache"
    private static final long FILE_HEADER = 0x4D44524361636865L;

    private static final long MIN_CACHE_INTERVAL = 60 * TimeUtil.SECOND;

    private static final double CACHE_LIMIT_FACTOR = 0.8;

    private static final AtomicLongFieldUpdater<Cache> USAGE_UPDATER = AtomicLongFieldUpdater.newUpdater(Cache.clreplaced, "usage");

    // ------------------------ static collection of instances ------------------------
    private static final IndexedSet<CacheConfig, Cache> INSTANCES = SynchronizedIndexedSet.create(Cache::getConfig);

    public static Cache acquireInstance(CacheConfig config) {
        config = config.clone();
        Cache cache;
        synchronized (INSTANCES) {
            cache = INSTANCES.getByKey(config);
            if (cache == null)
                INSTANCES.add(cache = new Cache(config));
        }
        if (cache.acquire())
            cache.startFileCacheWriter();
        return cache;
    }

    // ------------------------ instance ------------------------
    private final CacheConfig config;

    private int acquireCounter;

    private Thread cacheWriter;

    // incs on each addData
    private volatile long version;

    // incs on any operation
    private volatile long usage;

    private long writtenUsage;

    private long cacheSize;

    private final IndexedSet<Segment, Segment> segments = new IndexedSet<Segment, Segment>();

    Cache(CacheConfig config) {
        this.config = config;
    }

    public CacheConfig getConfig() {
        return config;
    }

    private synchronized boolean acquire() {
        return acquireCounter++ == 0;
    }

    public Thread release() {
        Thread cacheWriter;
        synchronized (this) {
            if (--acquireCounter > 0)
                return null;
            cacheWriter = this.cacheWriter;
            triggerFileCacheWriting();
            this.cacheWriter = null;
        }
        INSTANCES.remove(this);
        return cacheWriter;
    }

    private void startFileCacheWriter() {
        try {
            readCache();
        } catch (Throwable t) {
            Log.log.error("Unexpected error", t);
        }
        try {
            cacheWriter = new Thread(this, "MarketDataReplay-CacheWriter");
            cacheWriter.setDaemon(true);
            cacheWriter.start();
        } catch (Throwable ignored) {
        }
    }

    private synchronized void triggerFileCacheWriting() {
        if (cacheWriter != null)
            LockSupport.unpark(cacheWriter);
    }

    public long nextUsage() {
        long cur;
        long upd;
        do {
            cur = usage;
            upd = cur + 1;
        } while (!USAGE_UPDATER.compareAndSet(this, cur, upd));
        return upd;
    }

    public synchronized void checkRequestKeys(Current current, long requestTime, IndexedSet<Key, Key> presentKeys, IndexedSet<Key, Key> expiredKeys) {
        long millis = System.currentTimeMillis();
        for (Segment segment : segments) if (current.subscription.containsKey(segment.block) && segment.block.containsTime(requestTime)) {
            if (Math.abs(segment.downloadTime - millis) < config.timeToLive)
                presentKeys.put(segment.block);
            else
                expiredKeys.put(segment.block);
        }
    }

    public long getVersion() {
        return version;
    }

    public synchronized void addData(Collection<Segment> newSegments) {
        if (newSegments.isEmpty())
            return;
        long newSize = 0;
        HashMap<Key, ArrayList<Segment>> map = new HashMap<Key, ArrayList<Segment>>();
        long usage = nextUsage();
        for (Segment segment : newSegments) {
            newSize += segment.size();
            segment.usage = usage;
            ArrayList<Segment> segs = map.get(segment.block);
            if (segs == null)
                map.put(segment.block, segs = new ArrayList<Segment>());
            segs.add(segment);
        }
        int multiples = filterReceivedIntersections(map);
        long replacedSize = 0;
        int replacedSegments = 0;
        long identicalSize = 0;
        int identicalSegments = 0;
        RecordBuffer buffer = new RecordBuffer();
        for (Iterator<Segment> it = segments.iterator(); it.hasNext(); ) {
            Segment segment = it.next();
            ArrayList<Segment> segs = map.get(segment.block);
            if (segs == null)
                continue;
            boolean replace = false;
            for (Segment seg : segs) if (segment.intersects(seg)) {
                if (segment.block.isIdentical(seg.block)) {
                    identicalSize += segment.size();
                    identicalSegments++;
                    if (Math.abs(segment.downloadTime - seg.downloadTime) < config.timeToLive)
                        Log.log.warn("Identical segment " + seg + " at " + TimeFormat.DEFAULT.withMillis().format(seg.downloadTime) + " replaces " + segment + " at " + TimeFormat.DEFAULT.withMillis().format(segment.downloadTime));
                } else
                    Log.log.warn("Segment intersection: " + seg + " replaces " + segment);
                replace = true;
            }
            if (replace) {
                replacedSize += segment.size();
                replacedSegments++;
                it.remove();
                cacheSize -= segment.size();
            }
        }
        for (ArrayList<Segment> segs : map.values()) for (Segment seg : segs) {
            segments.put(seg);
            cacheSize += seg.size();
        }
        Log.log.info("addData: " + Log.mb(newSize) + " in " + newSegments.size() + " segments (" + multiples + " multiples)" + ", replaced " + Log.mb(replacedSize) + " in " + replacedSegments + " segments" + ", identical " + Log.mb(identicalSize) + " in " + identicalSegments + " segments");
        // will force rebuild of current segments by reading MDRs
        version++;
        triggerFileCacheWriting();
    }

    private int filterReceivedIntersections(HashMap<Key, ArrayList<Segment>> map) {
        int multiples = 0;
        for (ArrayList<Segment> segs : map.values()) if (segs.size() > 1) {
            multiples++;
            Collections.sort(segs, new Comparator<Segment>() {

                public int compare(Segment segment1, Segment segment2) {
                    // Place most valuable segments first to delete least valuable.
                    Block b1 = segment1.block;
                    Block b2 = segment2.block;
                    if (b1.getEndTime() != b2.getEndTime())
                        return b1.getEndTime() > b2.getEndTime() ? -1 : 1;
                    long d1 = b1.getEndTime() - b1.getStartTime();
                    long d2 = b2.getEndTime() - b2.getStartTime();
                    return d1 > d2 ? -1 : d1 < d2 ? 1 : Block.COMPARATOR.compare(b1, b2);
                }
            });
            for (int i = 0; i < segs.size(); i++) for (// Traverse backward to simplify deletion code.
            int j = segs.size(); // Traverse backward to simplify deletion code.
            --j > i; ) if (segs.get(i).intersects(segs.get(j))) {
                Log.log.warn("Received intersection: " + segs.get(i) + " replaces " + segs.get(j));
                segs.remove(j);
            }
        }
        return multiples;
    }

    public void rebuildCurrentSegmentsIfNeeded(Current current) {
        if (version > current.version)
            rebuildCurrentSegments(current);
    }

    public synchronized void rebuildCurrentSegments(Current current) {
        long usage = nextUsage();
        current.resetInterval();
        // add/replace new current segments
        for (Segment segment : segments) {
            if (isCurrentSegment(segment, current, current.time)) {
                segment.usage = usage;
                current.size += segment.size();
                current.startTime = Math.max(current.startTime, segment.block.getStartTime());
                current.endTime = Math.min(current.endTime, segment.block.getEndTime());
                CurrentSegment old = current.segments.getByKey(segment.block);
                if (old != null) {
                    // replace existing current segment
                    current.size -= old.segment.size();
                    old.replaceSegment(segment, current.time, usage);
                } else {
                    // add fresh segment
                    current.segments.add(new CurrentSegment(segment));
                }
            }
        }
        // release segments that are no longer current
        for (Iterator<CurrentSegment> it = current.segments.iterator(); it.hasNext(); ) {
            CurrentSegment cur = it.next();
            if (!isCurrentSegment(cur.segment, current, current.time)) {
                current.size -= cur.segment.size();
                cur.release();
                it.remove();
            }
        }
        // finish
        if (current.segments.isEmpty()) {
            current.startTime = current.time / MIN_CACHE_INTERVAL * MIN_CACHE_INTERVAL;
            current.endTime = current.startTime + MIN_CACHE_INTERVAL;
        }
        // only log something interesting
        if (current.size > 0 || !current.segments.isEmpty())
            Log.log.info("rebuildCurrent at " + TimeFormat.DEFAULT.withMillis().format(current.time) + ": " + Log.mb(current.size) + " in " + current.segments.size() + " segments" + " from " + TimeFormat.DEFAULT.withMillis().format(current.startTime) + " to " + TimeFormat.DEFAULT.withMillis().format(current.endTime) + ", replay speed " + current.replaySpeed);
        // update version and cleanup cache if needed
        current.version = version;
        checkCacheLimit(current);
    }

    public synchronized void releaseSegments(Current current) {
        for (CurrentSegment currentSegment : current.segments) currentSegment.release();
        current.segments.clear();
        current.size = 0;
        current.resetInterval();
    }

    public synchronized double getAvailableData(Current current, long time) {
        if (current.subscription.isEmpty())
            return 1;
        rebuildCurrentSegmentsIfNeeded(current);
        int available;
        if (current.isCurrentInterval(time)) {
            available = current.segments.size();
        } else {
            available = 0;
            for (Segment segment : segments) if (isCurrentSegment(segment, current, time))
                available++;
        }
        return (double) available / current.subscription.size();
    }

    private boolean isCurrentSegment(Segment segment, Current current, long time) {
        return segment.block.containsTime(time) && current.subscription.containsKey(segment.block);
    }

    // SYNC(this)
    private void checkCacheLimit(Current current) {
        long oldCacheSize = cacheSize;
        int oldSegments = segments.size();
        if (cacheSize > config.cacheLimit && current.size < cacheSize * CACHE_LIMIT_FACTOR) {
            Segment[] sorted = segments.toArray(new Segment[segments.size()]);
            Arrays.sort(sorted, Segment.USAGE_COMPARATOR);
            for (Segment segment : sorted) if (segment.currentCounter == 0) {
                segments.remove(segment);
                cacheSize -= segment.size();
                if (cacheSize <= config.cacheLimit * CACHE_LIMIT_FACTOR)
                    break;
            }
        }
        // only log something interesting
        if (cacheSize > 0 || current.size > 0 || oldCacheSize > 0 || !segments.isEmpty() || !current.segments.isEmpty() || oldSegments > 0)
            Log.log.info("Cache: limit " + Log.mb(config.cacheLimit) + ", used " + Log.mb(cacheSize) + " in " + segments.size() + " segments" + ", current " + Log.mb(current.size) + " in " + current.segments.size() + " segments" + ", removed " + Log.mb(oldCacheSize - cacheSize) + " in " + (oldSegments - segments.size()) + " segments");
    }

    private synchronized boolean needsWriteCache() {
        return writtenUsage != usage;
    }

    private synchronized void updateWrittenUsage(long writeUsage) {
        writtenUsage = writeUsage;
    }

    // returns written usage
    private long writeCache() {
        long millis = System.currentTimeMillis();
        long writeUsage;
        Segment[] sorted;
        synchronized (this) {
            writeUsage = usage;
            sorted = segments.toArray(new Segment[segments.size()]);
        }
        Arrays.sort(sorted, Segment.USAGE_COMPARATOR);
        FileOutputStream fos = null;
        try {
            File tmp = File.createTempFile(FILE_CACHE_NAME, FILE_CACHE_NAME_SUFFIX, getCacheFileParent());
            if (tmp.getParentFile() != null)
                tmp.getParentFile().mkdirs();
            fos = new FileOutputStream(tmp);
            BufferedOutputStream bos = new BufferedOutputStream(fos, 1000000);
            ByteArrayOutput bao = new ByteArrayOutput(100000);
            long writeSize = 0;
            long size = 0;
            int count = 0;
            bao.writeLong(FILE_HEADER);
            bao.writeCompactLong(writeUsage);
            bao.writeCompactLong(millis);
            for (int i = sorted.length; --i >= 0; ) {
                // Write most recent first
                Segment segment = sorted[i];
                size += segment.size();
                count++;
                segment.block.writeBlock(bao);
                bao.writeCompactLong(segment.downloadTime);
                bao.writeCompactLong(segment.usage);
                bos.write(bao.getBuffer(), 0, bao.getPosition());
                writeSize += bao.getPosition();
                bao.setPosition(0);
                if (writeSize > config.fileCacheLimit)
                    break;
            }
            bos.write(bao.getBuffer(), 0, bao.getPosition());
            writeSize += bao.getPosition();
            bos.close();
            File file = new File(getCacheFileParent(), FILE_CACHE_NAME);
            file.delete();
            tmp.renameTo(file);
            Log.log.info("writeCache: written " + Log.mb(writeSize) + " as " + Log.mb(size) + " in " + count + " segments" + " out of " + Log.mb(cacheSize) + " in " + segments.size() + " segments at " + TimeFormat.DEFAULT.withMillis().format(millis) + " in " + (System.currentTimeMillis() - millis) / 1000.0 + " seconds");
        } catch (Throwable t) {
            Log.log.error("Unexpected error writing cache", t);
        } finally {
            if (fos != null)
                try {
                    fos.close();
                } catch (IOException ignored) {
                }
        }
        return writeUsage;
    }

    private void readCache() {
        if (!segments.isEmpty())
            return;
        File f = new File(getCacheFileParent(), FILE_CACHE_NAME);
        if (!f.isFile() || f.length() < 16)
            return;
        long millis = System.currentTimeMillis();
        FileInputStream fis = null;
        try {
            fis = new FileInputStream(f);
            final FileInputStream fileInput = fis;
            final long[] readSize = new long[1];
            BufferedInput in = new StreamInput(fis);
            if (in.readLong() != FILE_HEADER)
                throw new IOException("Unknown file header");
            long readUsage = in.readCompactLong();
            long readMillis = in.readCompactLong();
            ArrayList<Segment> newSegments = new ArrayList<Segment>();
            long size = 0;
            try {
                while (size < config.cacheLimit) {
                    Block block = new Block();
                    block.readBlock(in);
                    block.decompress();
                    Segment segment = new Segment(block, in.readCompactLong());
                    segment.usage = in.readCompactLong();
                    size += segment.size();
                    newSegments.add(segment);
                }
            } catch (EOFException ignored) {
            } catch (Throwable t) {
                Log.log.error("Unexpected error reading cache at " + (readSize[0] - in.available()), t);
            }
            Log.log.info("readCache: read " + Log.mb(readSize[0]) + " ouf of " + Log.mb(f.length()) + ", " + Log.mb(size) + " in " + newSegments.size() + " segments at " + TimeFormat.DEFAULT.withMillis().format(readMillis) + " in " + (System.currentTimeMillis() - millis) / 1000.0 + " seconds");
            synchronized (this) {
                for (Segment segment : newSegments) {
                    segments.put(segment);
                    cacheSize += segment.size();
                }
                usage = Math.max(usage, readUsage);
                // we've just read cache, so no need to write it again
                writtenUsage = usage;
            }
        } catch (Throwable t) {
            Log.log.error("Unexpected error reading cache", t);
        } finally {
            if (fis != null)
                try {
                    fis.close();
                } catch (IOException ignored) {
                }
        }
    }

    private File getCacheFileParent() {
        String fileCachePath = config.fileCachePath;
        return fileCachePath.length() == 0 ? null : new File(fileCachePath);
    }

    // Cache writer thread main method
    public void run() {
        do {
            try {
                LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(config.fileCacheDumpPeriod));
                if (needsWriteCache())
                    updateWrittenUsage(writeCache());
            } catch (Throwable t) {
                Log.log.error("Unexpected error", t);
            }
        } while (Thread.currentThread() == cacheWriter);
    }
}

19 Source : IPFRegistry.java
with Mozilla Public License 2.0
from devexperts

/**
 * Registry for dynamic {@link IPFSymbolFilter} to make sure we have one instance of such filter for
 * any given {@link Key}.
 */
clreplaced IPFRegistry implements IPFRegistryMXBean {

    // do not create
    private IPFRegistry() {
    }

    private static final ReferenceQueue<IPFSymbolFilter> QUEUE = new ReferenceQueue<IPFSymbolFilter>();

    private static final IndexedSet<Key, FilterReference> REFS = IndexedSet.create((IndexerFunction<Key, FilterReference>) ref -> ref.key);

    private static Management.Registration registration;

    public static synchronized IPFSymbolFilter registerShared(IPFSymbolFilter filter) {
        FilterReference ref = REFS.getByKey(new Key(filter));
        if (ref != null) {
            IPFSymbolFilter registered = ref.get();
            if (registered != null)
                return registered;
        }
        return registerUpdate(filter);
    }

    public static synchronized IPFSymbolFilter registerUpdate(IPFSymbolFilter filter) {
        REFS.add(new FilterReference(filter));
        cleanupQueue();
        if (registration == null)
            registration = Management.registerMBean(new IPFRegistry(), IPFRegistryMXBean.clreplaced, Management.getMBeanNameForClreplaced(IPFRegistry.clreplaced));
        return filter;
    }

    private static void cleanupQueue() {
        Reference<? extends IPFSymbolFilter> ref;
        while ((ref = QUEUE.poll()) != null) REFS.remove(ref);
        if (REFS.isEmpty() && registration != null) {
            registration.unregister();
            registration = null;
        }
    }

    private static clreplaced FilterReference extends WeakReference<IPFSymbolFilter> {

        final Key key;

        FilterReference(IPFSymbolFilter filter) {
            super(filter);
            this.key = new Key(filter);
        }
    }

    static clreplaced Key {

        private final DataScheme scheme;

        private final String spec;

        Key(IPFSymbolFilter filter) {
            scheme = filter.getScheme();
            spec = filter.toString();
        }

        @Override
        public boolean equals(Object o) {
            if (this == o)
                return true;
            if (!(o instanceof Key))
                return false;
            Key that = (Key) o;
            return scheme.equals(that.scheme) && spec.equals(that.spec);
        }

        @Override
        public int hashCode() {
            return 31 * scheme.hashCode() + spec.hashCode();
        }
    }

    // --- Management ---
    public synchronized int getRegisteredFiltersCount() {
        return REFS.size();
    }

    public synchronized String[] getRegisteredFilters() {
        cleanupQueue();
        Set<String> result = new HashSet<String>();
        for (FilterReference ref : REFS) result.add(ref.key.spec);
        return result.toArray(new String[result.size()]);
    }

    public synchronized String reportStats() {
        cleanupQueue();
        List<String[]> data = new ArrayList<String[]>();
        data.add(new String[] { "Filter", "Symbols", "Modified", "Loaded", "Checked" });
        for (FilterReference ref : REFS) {
            String[] a = { ref.key.spec, "N/A", "N/A", "N/A", "N/A" };
            IPFSymbolFilter filter = ref.get();
            if (filter != null) {
                a[1] = Integer.toString(filter.getNumberOfSymbols());
                a[2] = TimeFormat.DEFAULT.format(filter.getLastModified());
                a[3] = TimeFormat.DEFAULT.format(filter.getLastLoaded());
                a[4] = TimeFormat.DEFAULT.format(filter.getLastChecked());
            }
            data.add(a);
        }
        StringBuilder html = new StringBuilder();
        html.append("Total IPF filters: ").append(REFS.size()).append("<br>");
        html.append("<table border=\"1\"><tr>");
        for (String s : data.get(0)) html.append("<th>").append(s).append("</th>");
        html.append("</tr>");
        for (int i = 1; i < data.size(); i++) {
            html.append("<tr>");
            for (String s : data.get(i)) html.append("<td>").append(s).append("</td>");
            html.append("</tr>");
        }
        html.append("</table>");
        String filler = "                                                                                                    ";
        for (int i = 0; i < data.get(0).length; i++) {
            int n = 0;
            for (int j = 0; j < data.size(); j++) n = Math.max(n, data.get(j)[i].length());
            for (int j = 0; j < data.size(); j++) data.get(j)[i] = data.get(j)[i] + filler.substring(Math.max(0, filler.length() - (n - data.get(j)[i].length())));
        }
        StringBuilder text = new StringBuilder();
        text.append("Total IPF filters: ").append(REFS.size());
        for (String[] a : data) {
            text.append("\n\t");
            for (String s : a) text.append(s).append("    ");
        }
        Logging.getLogging(getClreplaced()).info(text.toString());
        return html.toString();
    }

    public String forceUpdate(String filterSpec) {
        cleanupQueue();
        int cnt = 0;
        for (FilterReference ref : REFS) if (ref.key.spec.equals(filterSpec)) {
            IPFSymbolFilter filter = ref.get();
            if (filter != null) {
                filter.forceUpdate();
                cnt++;
            }
        }
        return "Forced update on " + cnt + " instances";
    }
}

19 Source : OrderBaseDelegateSet.java
with Mozilla Public License 2.0
from devexperts

clreplaced OrderBaseDelegateSet<T extends OrderBase, D extends OrderBaseDelegateImpl<T>> extends MarketEventDelegateSet<T, D> {

    private static final IndexerFunction.LongKey<List<? extends OrderBaseDelegateImpl<?>>> DELEGATE_LIST_BY_SOURCE_ID = value -> value.get(0).getSource().id();

    private final IndexedSet<Long, List<D>> subDelegatesBySource = IndexedSet.createLong(DELEGATE_LIST_BY_SOURCE_ID);

    private final IndexedSet<Long, List<D>> pubDelegatesBySource = IndexedSet.createLong(DELEGATE_LIST_BY_SOURCE_ID);

    OrderBaseDelegateSet(Clreplaced<T> eventType) {
        super(eventType);
    }

    @Override
    public void add(D delegate) {
        if (delegate.isSub())
            addToSet(subDelegatesBySource, delegate);
        if (delegate.isPub())
            addToSet(pubDelegatesBySource, delegate);
        super.add(delegate);
    }

    @Override
    protected List<D> getRegularSubDelegatesBySubscriptionSymbol(Object symbol, int sourceId) {
        return sourceId >= 0 ? getFromSet(subDelegatesBySource, sourceId) : super.getRegularSubDelegatesBySubscriptionSymbol(symbol, sourceId);
    }

    @Override
    public List<D> getPubDelegatesByEvent(T event) {
        return getFromSet(pubDelegatesBySource, event.getSource().id());
    }

    private static <T extends OrderBase, D extends OrderBaseDelegateImpl<T>> void addToSet(IndexedSet<Long, List<D>> set, D delegate) {
        List<D> list = set.getByKey(delegate.getSource().id());
        if (list == null) {
            list = new ArrayList<>(1);
            list.add(delegate);
            set.add(list);
        } else
            list.add(delegate);
    }

    private static <T extends OrderBase, D extends OrderBaseDelegateImpl<T>> List<D> getFromSet(IndexedSet<Long, List<D>> set, int sourceId) {
        List<D> list = set.getByKey(sourceId);
        return list == null ? Collections.<D>emptyList() : list;
    }
}

19 Source : DXPublisherImpl.java
with Mozilla Public License 2.0
from devexperts

public clreplaced DXPublisherImpl extends DXPublisher {

    private final DXEndpointImpl endpoint;

    private final List<QDDistributor> publishDistributors = new ArrayList<>();

    private final IndexedSet<Clreplaced<?>, Subscription<?>> subscriptionsByClreplaced = IndexedSet.create((IndexerFunction<Clreplaced<?>, Subscription<?>>) sub -> sub.eventType);

    DXPublisherImpl(DXEndpointImpl endpoint) {
        this.endpoint = endpoint;
        for (QDContract contract : endpoint.getContracts()) {
            int ordinal = contract.ordinal();
            while (publishDistributors.size() <= ordinal) publishDistributors.add(null);
            publishDistributors.set(ordinal, endpoint.getCollector(contract).distributorBuilder().build());
        }
    }

    public void closeImpl() {
        for (Subscription<?> subscription : subscriptionsByClreplaced) {
            subscription.close();
        }
    }

    @Override
    @SuppressWarnings({ "unchecked", "rawtypes" })
    public void publishEvents(Collection<?> events) {
        if (endpoint.isClosed() || events.isEmpty())
            return;
        int nextContract = 0;
        int thisContract;
        int doneContracts = 0;
        do {
            thisContract = nextContract;
            nextContract = 0;
            RecordMode mode = RecordMode.FLAGGED_DATA;
            if (endpoint.getQDEndpoint().hasEventTimeSequence())
                mode = mode.withEventTimeSequence();
            RecordBuffer buf = RecordBuffer.getInstance(mode);
            for (Object event : events) {
                EventDelegateSet delegateSet = endpoint.getDelegateSetByEventType(event.getClreplaced());
                List<EventDelegate<?>> delegates = delegateSet.getPubDelegatesByEvent((EventType<?>) event);
                for (int i = 0, delegatesSize = delegates.size(); i < delegatesSize; i++) {
                    EventDelegate delegate = delegates.get(i);
                    int curContract = 1 << delegate.getContract().ordinal();
                    if (curContract != thisContract && thisContract != 0) {
                        if (nextContract == 0 && (doneContracts & curContract) == 0)
                            nextContract = curContract;
                        continue;
                    }
                    RecordCursor cursor = delegate.putEvent((EventType<?>) event, buf);
                    if (cursor != null)
                        thisContract = curContract;
                }
            }
            if (// replacedert thisContract != 0
            !buf.isEmpty())
                getOrCreateDistributor(Integer.numberOfTrailingZeros(thisContract)).process(buf);
            buf.release();
            doneContracts |= thisContract;
        } while (nextContract != 0);
    }

    private synchronized QDDistributor getOrCreateDistributor(int contractOrdinal) {
        QDDistributor distributor = publishDistributors.get(contractOrdinal);
        if (distributor == null) {
            distributor = endpoint.getCollector(QDContract.values()[contractOrdinal]).distributorBuilder().build();
            publishDistributors.set(contractOrdinal, distributor);
        }
        return distributor;
    }

    @Override
    @SuppressWarnings("unchecked")
    public <T> DXPublisherObservableSubscriptionImpl<T> getSubscription(Clreplaced<T> eventType) {
        Subscription<T> subscription = (Subscription<T>) subscriptionsByClreplaced.getByKey(eventType);
        if (subscription == null) {
            subscription = createSubscriptionImpl(eventType);
            if (endpoint.isClosed())
                subscription.close();
        }
        return subscription.observableSubscription;
    }

    @SuppressWarnings("unchecked")
    private synchronized <T> Subscription<T> createSubscriptionImpl(Clreplaced<T> eventType) {
        Subscription<T> subscription = (Subscription<T>) subscriptionsByClreplaced.getByKey(eventType);
        if (subscription == null) {
            subscriptionsByClreplaced.add(subscription = new Subscription<>(eventType));
            subscription.start();
        }
        return subscription;
    }

    private clreplaced Subscription<T> {

        final Clreplaced<T> eventType;

        // set of records subscribed to by "*" via QDStream
        final Set<DataRecord> wildcardRecords;

        final InnerSubscription<T> innerSubscription;

        final DXPublisherObservableSubscriptionImpl<T> observableSubscription;

        final List<Processor<T>> processors = new ArrayList<>();

        @SuppressWarnings("unchecked")
        Subscription(Clreplaced<T> eventType) {
            this.eventType = eventType;
            wildcardRecords = new IndexedSet<DataRecord, DataRecord>();
            innerSubscription = new InnerSubscription(eventType);
            observableSubscription = new DXPublisherObservableSubscriptionImpl<>(innerSubscription);
            EnumMap<QDContract, Set<DataRecord>> records = new EnumMap<>(QDContract.clreplaced);
            EventDelegateSet<?, ?> delegateSet = endpoint.getDelegateSetByEventType(eventType);
            if (delegateSet != null)
                for (EventDelegate<?> eventDelegate : delegateSet.getAllPubDelegates()) {
                    QDContract contract = eventDelegate.getContract();
                    Set<DataRecord> set = records.get(contract);
                    if (set == null)
                        records.put(contract, set = new HashSet<>());
                    set.add(eventDelegate.getRecord());
                }
            for (Map.Entry<QDContract, Set<DataRecord>> entry : records.entrySet()) {
                QDContract contract = entry.getKey();
                Set<DataRecord> set = entry.getValue();
                QDDistributor distributor = endpoint.getCollector(contract).distributorBuilder().withFilter(CompositeFilters.forRecords(set)).build();
                processors.add(new Processor<>(endpoint.getOrCreateExecutor(), contract, distributor, this));
            }
        }

        void start() {
            for (Processor<T> processor : processors) {
                processor.start();
            }
        }

        void close() {
            innerSubscription.close();
            for (Processor<T> processor : processors) {
                processor.close();
            }
        }
    }

    @SuppressWarnings("serial")
    private static clreplaced InnerSubscription<T> extends DXFeedSubscription<T> {

        InnerSubscription(Clreplaced<T> eventType) {
            super(eventType);
        }

        @Override
        protected boolean shallNotifyOnSymbolUpdate(@Nonnull Object symbol, @Nullable Object oldSymbol) {
            return true;
        }
    }

    private clreplaced Processor<T> extends SubscriptionProcessor {

        private final QDContract contract;

        private final QDDistributor distributor;

        private final Subscription<T> subscription;

        // reusable object (sub processing is single-threaded)
        private final Set<Object> symbols = new IndexedSet<>();

        private Processor(Executor executor, QDContract contract, QDDistributor distributor, Subscription<T> subscription) {
            super(executor, contract);
            this.contract = contract;
            this.distributor = distributor;
            this.subscription = subscription;
        }

        public void start() {
            startProcessing(distributor);
        }

        public void close() {
            stopProcessing();
            distributor.close();
        }

        @Override
        protected void processAddedSubscription(RecordSource source) {
            processSub(source, true);
        }

        @Override
        protected void processRemovedSubscription(RecordSource source) {
            processSub(source, false);
        }

        @SuppressWarnings("unchecked")
        private void processSub(RecordSource source, boolean add) {
            symbols.clear();
            Collection<DataRecord> wildcards = null;
            RecordCursor cursor;
            while ((cursor = source.next()) != null) {
                DataRecord record = cursor.getRecord();
                List<EventDelegate<?>> delegates = endpoint.getDelegateListByContractAndRecord(contract, record);
                if (delegates == null)
                    continue;
                for (EventDelegate<?> delegate : delegates) {
                    if (!delegate.isPub() || delegate.getEventType() != subscription.eventType)
                        continue;
                    String qdSymbol = endpoint.decode(cursor.getCipher(), cursor.getSymbol());
                    if (qdSymbol.equals(WildcardSymbol.RESERVED_PREFIX) && contract == QDContract.STREAM) {
                        if (wildcards == null)
                            wildcards = new ArrayList<>();
                        wildcards.add(record);
                    } else
                        symbols.add(delegate.getSubscriptionSymbolByQDSymbolAndTime(qdSymbol, cursor.getTime()));
                }
            }
            if (add) {
                if (wildcards != null) {
                    if (subscription.wildcardRecords.isEmpty())
                        symbols.add(WildcardSymbol.ALL);
                    subscription.wildcardRecords.addAll(wildcards);
                }
                subscription.innerSubscription.addSymbols(symbols);
            } else {
                if (wildcards != null) {
                    subscription.wildcardRecords.removeAll(wildcards);
                    if (subscription.wildcardRecords.isEmpty())
                        symbols.add(WildcardSymbol.ALL);
                }
                subscription.innerSubscription.removeSymbols(symbols);
            }
        }
    }
}

19 Source : DXEndpointImpl.java
with Mozilla Public License 2.0
from devexperts

public clreplaced DXEndpointImpl extends ExtensibleDXEndpoint implements MessageConnectorListener, RMISupportingDXEndpoint {

    private static final boolean TRACE_LOG = DXEndpointImpl.clreplaced.desiredreplacedertionStatus();

    private static final ExecutorProvider DEFAULT_EXECUTOR_PROVIDER = new ExecutorProvider("DXEndpoint-DXExecutorThread", QDLog.log);

    private final Role role;

    private final QDEndpoint qdEndpoint;

    private final Properties props;

    private final DataScheme scheme;

    private final SymbolCodec codec;

    private final IndexedSet<Clreplaced<?>, EventDelegateSet<?, ?>> delegateSetsByEventType = IndexedSet.create((IndexerFunction<Clreplaced<?>, EventDelegateSet<?, ?>>) value -> value.eventType());

    private final EnumMap<QDContract, IndexedSet<DataRecord, List<EventDelegate<?>>>> delegateListsByContractAndRecord = new EnumMap<>(QDContract.clreplaced);

    // unmodifiable after construction
    private Set<Clreplaced<? extends EventType<?>>> eventTypes = new IndexedSet<Clreplaced<? extends EventType<?>>, Clreplaced<? extends EventType<?>>>();

    private final List<PropertyChangeListener> stateChangeListeners = new CopyOnWriteArrayList<>();

    // this lock is taken from qdEndpoint and protects overall state
    private final Object lock;

    // separate lock to protect writes to state only. "updateState" method uses only this lock
    private final StateHolder stateHolder = new StateHolder();

    private RMIEndpointImpl rmiEndpoint;

    // SYNC(lock) on create
    private volatile DXFeedImpl feed;

    // SYNC(lock) on create
    private volatile DXPublisherImpl publisher;

    // SYNC(lock), != null when started (connect was called)
    private String address;

    public static MessageAdapter.AbstractFactory getMessageAdapterFactory(QDEndpoint qdEndpoint, Role role) {
        switch(role) {
            case FEED:
            case ON_DEMAND_FEED:
            case STREAM_FEED:
                return new DistributorAdapter.Factory(qdEndpoint, null);
            case PUBLISHER:
                return new AgentAdapter.Factory(qdEndpoint, null);
            case STREAM_PUBLISHER:
                return new AgentAdapter.Factory(qdEndpoint, null) {

                    @Override
                    public MessageAdapter createAdapter(QDStats stats) {
                        // TODO here we lost capability to configure channels via connector spec, if it ever worked here
                        return new AgentAdapter(endpoint, ticker, stream, history, getFilter(), stats) {

                            @Override
                            protected QDAgent createAgent(QDCollector collector, SubscriptionFilter filter, String keyProperties) {
                                QDAgent agent = super.createAgent(collector, filter, keyProperties);
                                agent.setBufferOverflowStrategy(QDAgent.BufferOverflowStrategy.BLOCK);
                                return agent;
                            }
                        };
                    }
                };
            default:
                throw new UnsupportedOperationException("Connection is not supported in " + role + " role");
        }
    }

    private final ExecutorProvider executorProvider;

    private final ExecutorProvider.Reference executorReference;

    // Must call initConnectivity after this constructor
    protected DXEndpointImpl(Role role, QDEndpoint qdEndpoint, Properties props) {
        this.role = role;
        this.qdEndpoint = qdEndpoint;
        this.lock = qdEndpoint.getLock();
        this.props = props;
        this.scheme = qdEndpoint.getScheme();
        this.codec = scheme.getCodec();
        // Configures executor provide (either common built-in or custom)
        if (hasProperty(DXFEED_THREAD_POOL_SIZE_PROPERTY)) {
            executorProvider = new ExecutorProvider(Integer.decode(getProperty(DXFEED_THREAD_POOL_SIZE_PROPERTY)), "DXEndpoint-" + qdEndpoint.getName() + "-DXExecutorThread", QDLog.log);
        } else
            executorProvider = DEFAULT_EXECUTOR_PROVIDER;
        executorReference = executorProvider.newReference();
        // turns on stream wildcard support if enabled in properties
        if (Boolean.parseBoolean(props.getProperty(DXFEED_WILDCARD_ENABLE_PROPERTY, "false")))
            qdEndpoint.getStream().setEnableWildcards(true);
        // create delegates
        for (QDCollector collector : qdEndpoint.getCollectors()) {
            QDContract contract = collector.getContract();
            delegateListsByContractAndRecord.put(contract, IndexedSet.create((IndexerFunction<DataRecord, List<EventDelegate<?>>>) value -> value.get(0).getRecord()));
        }
        for (EventDelegateFactory factory : Services.createServices(EventDelegateFactory.clreplaced, null)) for (int i = 0; i < scheme.getRecordCount(); i++) createDelegates(factory, scheme.getRecord(i));
        for (EventDelegateSet<?, ?> delegateSet : delegateSetsByEventType) {
            delegateSet.completeConstruction();
            eventTypes.add(delegateSet.eventType());
        }
        eventTypes = Collections.unmodifiableSet(eventTypes);
        // ---- the very last stuff (when everything is already constructed) ---
        // will cause notifications and updateState
        qdEndpoint.addMessageConnectionListener(this);
    }

    /**
     * @deprecated Use {@link #DXEndpointImpl(Role, QDEndpoint, Properties)} or {@link Builder} to
     * {@link Builder#build() build()} endpoints with custom properties.
     */
    public DXEndpointImpl(Role role, QDCollector... collectors) {
        this(role, QDEndpoint.newBuilder().withScheme(collectors[0].getScheme()).build().addCollectors(collectors), new Properties());
        initConnectivity();
    }

    private void initConnectivity() {
        // install RMI or default connection initializer
        if (role == Role.FEED) {
            // BuilderRMImpl provides its own RMIEndpoint before invocation
            if (rmiEndpoint == null)
                rmiEndpoint = new RMIEndpointImpl(RMIEndpoint.Side.CLIENT, qdEndpoint, getMessageAdapterFactory(qdEndpoint, Role.FEED), this);
        } else if (!qdEndpoint.hasConnectorInitializer())
            qdEndpoint.setConnectorInitializer(new DXConnectorInitializer(this));
        // connect if needed
        switch(role) {
            case FEED:
            case ON_DEMAND_FEED:
                if (hasProperty(DXFEED_USER_PROPERTY))
                    qdEndpoint.user(getProperty(DXFEED_USER_PROPERTY));
                if (hasProperty(DXFEED_PreplacedWORD_PROPERTY))
                    qdEndpoint.preplacedword(getProperty(DXFEED_PreplacedWORD_PROPERTY));
                if (hasProperty(DXFEED_ADDRESS_PROPERTY))
                    connectImpl(getProperty(DXFEED_ADDRESS_PROPERTY), role == Role.FEED);
                break;
            case PUBLISHER:
                if (hasProperty(DXPUBLISHER_ADDRESS_PROPERTY))
                    connectImpl(getProperty(DXPUBLISHER_ADDRESS_PROPERTY), true);
                break;
        }
    }

    public static EnumSet<QDContract> getRoleContracts(Role role) {
        return role == Role.STREAM_FEED || role == Role.STREAM_PUBLISHER ? EnumSet.of(QDContract.STREAM) : EnumSet.of(QDContract.TICKER, QDContract.STREAM, QDContract.HISTORY);
    }

    @Override
    public Object getLock() {
        return lock;
    }

    @Override
    public QDEndpoint getQDEndpoint() {
        return qdEndpoint;
    }

    @Override
    public Role getRole() {
        return role;
    }

    public boolean isClosed() {
        return stateHolder.state == State.CLOSED;
    }

    @Override
    public State getState() {
        return stateHolder.state;
    }

    @Override
    public void addStateChangeListener(PropertyChangeListener listener) {
        stateChangeListeners.add(listener);
    }

    @Override
    public void removeStateChangeListener(PropertyChangeListener listener) {
        stateChangeListeners.remove(listener);
    }

    @Override
    public Set<Clreplaced<? extends EventType<?>>> getEventTypes() {
        return eventTypes;
    }

    @Override
    public DXFeed getFeed() {
        DXFeedImpl feed = this.feed;
        return feed == null ? createFeedInternal() : feed;
    }

    private DXFeed createFeedInternal() {
        synchronized (lock) {
            if (feed == null)
                feed = new DXFeedImpl(this);
            return feed;
        }
    }

    @Override
    public DXPublisher getPublisher() {
        DXPublisherImpl publisher = this.publisher;
        return publisher == null ? createPublisherInternal() : publisher;
    }

    private DXPublisher createPublisherInternal() {
        synchronized (lock) {
            if (publisher == null)
                publisher = new DXPublisherImpl(this);
            return publisher;
        }
    }

    public Executor getOrCreateExecutor() {
        return executorReference.getOrCreateExecutor();
    }

    @Override
    public ExecutorProvider.Reference getExecutorReference() {
        return executorReference;
    }

    @Override
    public DXEndpoint executor(Executor executor) {
        executorReference.setExecutor(executor);
        return this;
    }

    @Override
    public DXEndpoint user(String user) {
        qdEndpoint.user(user);
        return this;
    }

    @Override
    public DXEndpoint preplacedword(String preplacedword) {
        qdEndpoint.preplacedword(preplacedword);
        return this;
    }

    @Override
    public DXEndpoint connect(String address) {
        connectImpl(address, true);
        return this;
    }

    @Override
    public void reconnect() {
        synchronized (lock) {
            if (isClosed() || qdEndpoint.isClosed())
                return;
            qdEndpoint.reconnectActiveConnectors();
        }
    }

    // Note "start == false" is used internally to initializeConnectorsForAddress (only) in ON_DEMAND_FEED role
    private void connectImpl(String address, boolean start) {
        if (address == null)
            throw new NullPointerException();
        synchronized (lock) {
            if (stateHolder.state == State.CLOSED || address.equals(this.address))
                return;
            disconnect();
            qdEndpoint.initializeConnectorsForAddress(address);
            if (start) {
                qdEndpoint.startConnectors();
                setConnectedAddressSync(address);
                if (rmiEndpoint != null)
                    rmiEndpoint.setConnectedAddressSync(address);
            }
        }
    }

    // GuardedBy(lock)
    @Override
    public void setConnectedAddressSync(String address) {
        this.address = address;
        stateHolder.updateNow();
    }

    @Override
    public void disconnect() {
        synchronized (lock) {
            if (address == null)
                return;
            address = null;
            qdEndpoint.cleanupConnectors();
            if (rmiEndpoint != null)
                rmiEndpoint.disconnect();
            stateHolder.updateNow();
        }
    }

    @Override
    public void disconnectAndClear() {
        synchronized (lock) {
            // stop everything first
            qdEndpoint.stopConnectorsAndWaitUninterruptibly();
            // cleanup storage
            clearImpl();
            // disconnect to properly update address, change state, etc.
            disconnect();
        }
    }

    /**
     * This method expects connectors being stopped (disconnected) and also properly guarded by lock.
     * Although it will work even with running connectors and without locks, but will asynchronously
     * intersect with live data updates.
     */
    public void clearImpl() {
        clearCollector(qdEndpoint.getCollector(QDContract.TICKER), false);
        clearCollector(qdEndpoint.getCollector(QDContract.HISTORY), true);
    }

    private void clearCollector(QDCollector collector, boolean keepTime) {
        if (collector == null)
            return;
        RecordBuffer buf = RecordBuffer.getInstance();
        collector.examineData(buf);
        DXFeedImpl.clearDataInBuffer(buf, keepTime);
        buf.rewind();
        QDDistributor distributor = collector.distributorBuilder().build();
        try {
            distributor.process(buf);
        } finally {
            distributor.close();
        }
        buf.release();
    }

    @Override
    public void awaitNotConnected() throws InterruptedException {
        stateHolder.awaitNotConnected();
    }

    @Override
    public void awaitProcessed() throws InterruptedException {
        qdEndpoint.awaitProcessed();
    }

    @Override
    public void close() {
        if (prepareToClose()) {
            disconnect();
            if (feed != null)
                feed.closeImpl();
            closeRest();
        }
    }

    @Override
    public void closeAndAwaitTermination() throws InterruptedException {
        if (prepareToClose()) {
            synchronized (lock) {
                qdEndpoint.stopConnectorsAndWait();
                disconnect();
            }
            if (feed != null)
                feed.awaitTerminationAndCloseImpl();
            stateHolder.awaitClosed();
            closeRest();
        }
    }

    private boolean prepareToClose() {
        State oldState = makeClosed();
        if (oldState == State.CLOSED)
            return false;
        return true;
    }

    private void closeRest() {
        if (publisher != null)
            publisher.closeImpl();
        qdEndpoint.close();
        // Close executorReference as the last step. We should not need executor anymore
        executorReference.close();
    }

    private void fireStateChangeEvent(State oldState, State newState) {
        if (stateChangeListeners.isEmpty())
            return;
        PropertyChangeEvent event = new PropertyChangeEvent(this, "state", oldState, newState);
        for (PropertyChangeListener listener : stateChangeListeners) try {
            listener.propertyChange(event);
        } catch (Throwable t) {
            QDLog.log.error("Exception in DXEndpoint state change listener", t);
        }
    }

    private State makeClosed() {
        synchronized (lock) {
            return stateHolder.makeClosed();
        }
    }

    public boolean hasProperty(String key) {
        return props.getProperty(key) != null;
    }

    public String getProperty(String key) {
        return props.getProperty(key);
    }

    public Set<QDContract> getContracts() {
        return qdEndpoint.getContracts();
    }

    public QDCollector getCollector(QDContract contract) {
        return qdEndpoint.getCollector(contract);
    }

    public int encode(String symbol) {
        return codec.encode(symbol);
    }

    public String decode(int cipher, String symbol) {
        return codec.decode(cipher, symbol);
    }

    public EventDelegateSet<?, ?> getDelegateSetByEventType(Clreplaced<?> eventType) {
        return delegateSetsByEventType.getByKey(eventType);
    }

    public List<EventDelegate<?>> getDelegateListByContractAndRecord(QDContract contract, DataRecord record) {
        return delegateListsByContractAndRecord.get(contract).getByKey(record);
    }

    @Override
    public String toString() {
        return "DXEndpoint{" + "role=" + role + ", scheme=" + scheme.getClreplaced().getSimpleName() + ", address=" + LogUtil.hideCredentials(address) + (isClosed() ? ", closed" : "") + '}';
    }

    public RMIEndpoint getRMIEndpoint() {
        return rmiEndpoint;
    }

    private void createDelegates(EventDelegateFactory factory, DataRecord record) {
        Collection<EventDelegate<?>> delegates = role == Role.STREAM_FEED || role == Role.STREAM_PUBLISHER ? factory.createStreamOnlyDelegates(record) : factory.createDelegates(record);
        if (delegates == null)
            return;
        delegates.forEach(this::registerDelegate);
    }

    @SuppressWarnings({ "unchecked", "rawtypes" })
    void registerDelegate(EventDelegate<?> delegate) {
        QDContract contract = delegate.getContract();
        if (qdEndpoint.getCollector(contract) == null)
            return;
        EventDelegateSet delegateSet = delegateSetsByEventType.getByKey(delegate.getEventType());
        if (delegateSet == null)
            delegateSetsByEventType.add(delegateSet = delegate.createDelegateSet());
        try {
            delegateSet.add(delegate);
        } catch (ClreplacedCastException e) {
            throw new IllegalArgumentException("Cannot mix events of incompatible types", e);
        }
        IndexedSet<DataRecord, List<EventDelegate<?>>> lists = delegateListsByContractAndRecord.get(contract);
        List<EventDelegate<?>> delegateList = lists.getByKey(delegate.getRecord());
        if (delegateList == null) {
            delegateList = new ArrayList<>(1);
            delegateList.add(delegate);
            lists.add(delegateList);
        } else
            delegateList.add(delegate);
    }

    @Override
    public void stateChanged(MessageConnector connector) {
        // This method is not synchronized to avoid deadlocks and will recompute state in different thread
        stateHolder.scheduleUpdate();
    }

    @ServiceProvider
    public static clreplaced BuilderImpl extends Builder {

        private static final Set<String> SUPPORTED_PROPERTIES = new LinkedHashSet<>(Arrays.asList(DXFEED_PROPERTIES_PROPERTY, DXFEED_THREAD_POOL_SIZE_PROPERTY, DXFEED_AGGREGATION_PERIOD_PROPERTY, DXFEED_ADDRESS_PROPERTY, DXFEED_USER_PROPERTY, DXFEED_PreplacedWORD_PROPERTY, DXFEED_WILDCARD_ENABLE_PROPERTY, DXENDPOINT_EVENT_TIME_PROPERTY, DXENDPOINT_STORE_EVERYTHING_PROPERTY, DXSCHEME_NANO_TIME_PROPERTY, DXPUBLISHER_PROPERTIES_PROPERTY, DXPUBLISHER_ADDRESS_PROPERTY, DXPUBLISHER_THREAD_POOL_SIZE_PROPERTY));

        private static final Set<String> MASKED_PROPERTIES = new HashSet<>(Arrays.asList(DXFEED_USER_PROPERTY, DXFEED_PreplacedWORD_PROPERTY));

        private final Properties props = new Properties();

        private QDEndpoint.Builder qdEndpointBuilder = QDEndpoint.newBuilder();

        public BuilderImpl() {
            updateSubscribeSupport();
        }

        private void updateSubscribeSupport() {
            // support permanent subscription for FEED role
            qdEndpointBuilder.withSubscribeSupport(role == Role.FEED ? "dxfeed.qd.subscribe." : null);
        }

        @Override
        public Builder withRole(Role role) {
            super.withRole(role);
            updateSubscribeSupport();
            return this;
        }

        @Override
        public Builder withProperty(String key, String value) {
            if (key == null || value == null)
                throw new NullPointerException();
            if (supportsProperty(key)) {
                // store property locally... preplaced to qdEndpointBuilder in build() method
                props.setProperty(key, value);
            }
            return this;
        }

        @Override
        public boolean supportsProperty(String key) {
            return SUPPORTED_PROPERTIES.contains(key) || key.startsWith(DXSCHEME_ENABLED_PROPERTY_PREFIX) || qdEndpointBuilder.supportsProperty(key);
        }

        private void loadPropertiesDefaults(Properties defaultProps, boolean ignoreName) {
            // Properties.stringPropertyNames() is properly synchronized to avoid ConcurrentModificationException.
            for (String key : defaultProps.stringPropertyNames()) {
                if (ignoreName && key.equals(NAME_PROPERTY))
                    continue;
                String value = defaultProps.getProperty(key);
                if (value != null && !props.containsKey(key))
                    withProperty(key, value);
            }
        }

        private void loadPropertiesDefaultsFromStream(InputStream in, String name, String propFileKey) {
            if (in == null)
                return;
            QDLog.log.info("DXEndpoint is loading properties from " + name);
            Properties props = new Properties();
            try {
                try {
                    props.load(in);
                } finally {
                    in.close();
                }
            } catch (IOException e) {
                failedToLoadFrom(name, propFileKey, e);
                return;
            }
            loadPropertiesDefaults(props, false);
        }

        private void loadPropertiesDefaultsFromFile(String fileName, String propFileKey) {
            try {
                loadPropertiesDefaultsFromStream(new URLInputStream(fileName), fileName, propFileKey);
            } catch (IOException e) {
                failedToLoadFrom("file '" + fileName + "'", propFileKey, e);
            }
        }

        private void failedToLoadFrom(String name, String propFileKey, IOException e) {
            QDLog.log.error("Failed to load " + propFileKey + " from " + LogUtil.hideCredentials(name), e);
        }

        @Override
        public DXEndpoint build() {
            loadProperties();
            // Create scheme
            DataScheme scheme = QDFactory.getDefaultScheme();
            SchemeProperties schemeProperties = new SchemeProperties(props);
            if (scheme == DXFeedScheme.getInstance())
                scheme = DXFeedScheme.withProperties(schemeProperties);
            // create QD endpoint
            QDEndpoint qdEndpoint = qdEndpointBuilder.withProperties(props).withCollectors(getRoleContracts(role)).withScheme(scheme).withEventTimeSequence(Boolean.parseBoolean(props.getProperty(DXENDPOINT_EVENT_TIME_PROPERTY, "false"))).withStoreEverything(Boolean.parseBoolean(props.getProperty(DXENDPOINT_STORE_EVERYTHING_PROPERTY, "false"))).build();
            // log DXEndpoint properties
            for (Map.Entry<Object, Object> entry : new TreeMap<>(props).entrySet()) {
                String key = (String) entry.getKey();
                if (!qdEndpointBuilder.supportsProperty(key) && supportsProperty(key))
                    QDLog.log.info(qdEndpoint.getName() + " DXEndpoint with " + key + "=" + (MASKED_PROPERTIES.contains(key) ? "****" : props.getProperty(key)));
            }
            // create DXEndpoint
            DXEndpointImpl dxEndpoint = new DXEndpointImpl(role, qdEndpoint, props);
            dxEndpoint.initConnectivity();
            return dxEndpoint;
        }

        private void loadProperties() {
            String propFileKey;
            switch(role) {
                case FEED:
                case ON_DEMAND_FEED:
                    propFileKey = DXFEED_PROPERTIES_PROPERTY;
                    break;
                case PUBLISHER:
                    propFileKey = DXPUBLISHER_PROPERTIES_PROPERTY;
                    break;
                default:
                    // properties are not supported
                    return;
            }
            // resolve prop file name
            String fileName = props.getProperty(propFileKey);
            if (fileName == null)
                fileName = SystemProperties.getProperty(propFileKey, null);
            // load from file
            if (fileName != null)
                loadPropertiesDefaultsFromFile(fileName, propFileKey);
            // fill in last-resort defaults from system properties (but name)
            try {
                loadPropertiesDefaults(System.getProperties(), true);
            } catch (SecurityException e) {
            // just ignore exception if we cannot access system properties due to security manager
            }
            // and load very last defaults from resource if file was not explicitly specified
            if (fileName == null) {
                String resourceName = "/" + propFileKey;
                loadPropertiesDefaultsFromStream(DXEndpointImpl.clreplaced.getResourcereplacedtream(resourceName), "resource '" + resourceName + "'", propFileKey);
            }
        }
    }

    @ServiceProvider
    public static clreplaced BuilderRMIImpl extends RMIEndpointImpl.Builder {

        private QDEndpoint.Builder qdEndpointBuilder = QDEndpoint.newBuilder();

        @Override
        public RMIEndpoint build() {
            DXEndpointImpl dxEndpoint = null;
            qdEndpointBuilder.withProperties(props);
            if (scheme != null)
                qdEndpointBuilder.withScheme(scheme);
            if (dxRole != null)
                qdEndpointBuilder.withCollectors(getRoleContracts(dxRole));
            qdEndpointBuilder.withName(getOrCreateName());
            qdEndpointBuilder.withEventTimeSequence(Boolean.parseBoolean(props.getProperty(DXENDPOINT_EVENT_TIME_PROPERTY, "false")));
            QDEndpoint qdEndpoint = qdEndpointBuilder.build();
            if (// create dxEndpoint if dxRole is set
            dxRole != null)
                dxEndpoint = new DXEndpointImpl(dxRole, qdEndpoint, props);
            if (// feed needs RMI client
            dxRole == Role.FEED)
                side = side.withClient();
            RMIEndpointImpl rmiEndpoint = new RMIEndpointImpl(side, qdEndpoint, (dxRole != null) ? getMessageAdapterFactory(qdEndpoint, dxRole) : null, dxEndpoint);
            if (dxEndpoint != null) {
                // store rmiEndpoint's reference
                dxEndpoint.rmiEndpoint = rmiEndpoint;
                dxEndpoint.initConnectivity();
            }
            return rmiEndpoint;
        }

        @Override
        public boolean supportsProperty(String key) {
            return super.supportsProperty(key) || qdEndpointBuilder.supportsProperty(key);
        }
    }

    private clreplaced StateHolder implements Runnable {

        // SYNC(lock+this) on write to State.CLOSE; SYNC(this) on any other writes
        volatile State state = State.NOT_CONNECTED;

        private State lastFiredState = state;

        // at most one task is scheduled at any time
        // > 0 when run() is scheduled to run or is running, increment on any change
        private int scheduled;

        // current thread that processes notifications
        private volatile Thread processingThread;

        // SYNC(lock), for connect & disconnect method to immediately recompute state
        synchronized void updateNow() {
            if (state != State.CLOSED) {
                State computedState = computeState();
                if (state != computedState) {
                    state = computedState;
                    scheduleImpl();
                }
            }
        }

        synchronized void scheduleUpdate() {
            if (state != State.CLOSED)
                scheduleImpl();
        }

        @GuardedBy("this")
        private void scheduleImpl() {
            if (TRACE_LOG)
                QDLog.log.trace("Schedule state update to " + state);
            if (scheduled++ > 0) {
                // wakeup awaitInner
                notifyAll();
                return;
            }
            getOrCreateExecutor().execute(this);
        }

        @Override
        public void run() {
            int lastScheduled = 0;
            while (true) {
                // loop while we need to fire state change events
                State oldState;
                State newState;
                synchronized (this) {
                    state = computeState();
                    oldState = lastFiredState;
                    newState = state;
                    if (newState == oldState && scheduled == lastScheduled) {
                        // no change in state -- leave event processing loop
                        processingThread = null;
                        scheduled = 0;
                        // wakeup awaitOuter
                        notifyAll();
                        return;
                    }
                    lastScheduled = scheduled;
                    lastFiredState = newState;
                    // keep reference to processing thread to catch inner wait
                    processingThread = Thread.currentThread();
                }
                if (oldState != newState)
                    fireStateChangeEvent(oldState, newState);
            }
        }

        @GuardedBy("this")
        private State computeState() {
            if (state == State.CLOSED)
                return State.CLOSED;
            boolean hasConnecting = false;
            for (MessageConnector connector : qdEndpoint.getConnectors()) {
                switch(connector.getState()) {
                    case CONNECTING:
                        hasConnecting = true;
                        break;
                    case CONNECTED:
                        return State.CONNECTED;
                }
            }
            return hasConnecting ? State.CONNECTING : State.NOT_CONNECTED;
        }

        // SYNC(lock) (then syncs on this)
        synchronized State makeClosed() {
            State oldState = state;
            if (oldState != State.CLOSED) {
                state = State.CLOSED;
                scheduleImpl();
            }
            return oldState;
        }

        void awaitClosed() throws InterruptedException {
            await(State.CLOSED);
        }

        void awaitNotConnected() throws InterruptedException {
            await(State.NOT_CONNECTED);
        }

        private void await(State condition) throws InterruptedException {
            if (processingThread == Thread.currentThread())
                awaitInner(condition);
            else
                awaitOuter(condition);
        }

        private synchronized void awaitInner(State condition) throws InterruptedException {
            // reenter from inside of run() event processing loop - run inner processing loop and wait
            while (true) {
                state = computeState();
                if (isCondition(condition))
                    return;
                // wait until anything changes (see notifyAll in scheduleImpl)
                wait();
            }
        }

        private synchronized void awaitOuter(State condition) throws InterruptedException {
            // just wait normally from another thread
            // wait until processed state is in expected condition and no further changes scheduled
            while (!isCondition(condition) || scheduled != 0) wait();
        }

        @GuardedBy("this")
        private // true when computed state is as expected or closed
        boolean isCondition(State condition) {
            return state == State.CLOSED || state == condition;
        }
    }
}

19 Source : OrderBookList.java
with Mozilla Public License 2.0
from devexperts

// Uncheck the node and return the previous scope node that must be checked, or null
protected Node<Order> uncheckNode(Node<Order> node) {
    boolean checked = uncheck(node);
    Scope scope = node.getValue().getScope();
    if (scope == Scope.COMPOSITE) {
        composite = null;
        return null;
    }
    // Decrement count for non-composite scope orders
    if (checked) {
        replacedert (nonCompositeCount > 0);
        nonCompositeCount--;
    }
    char exchangeCode = node.getValue().getExchangeCode();
    if (scope == Scope.REGIONAL) {
        regionals[exchangeCode] = null;
        return !checked ? null : (nonCompositeCount == 0) ? composite : null;
    }
    // Find entry for exchange and market-maker
    IndexedSet<String, MMIDEntry> aggregate = aggregates[exchangeCode];
    if (aggregate == null)
        return null;
    String marketMaker = node.getValue().getMarketMaker();
    MMIDEntry entry = aggregate.getByKey(marketMaker);
    if (entry == null)
        return null;
    Node<Order> regional = regionals[exchangeCode];
    if (scope == Scope.AGGREGATE) {
        entry.aggregate = null;
        if (entry.orderCount == 0)
            aggregate.removeKey(marketMaker);
        return !checked ? null : (regional != null) ? regional : (nonCompositeCount == 0) ? composite : null;
    }
    if (scope == Scope.ORDER) {
        if (checked)
            entry.orderCount--;
        replacedert (entry.orderCount >= 0);
        if (entry.orderCount == 0 && entry.aggregate == null)
            aggregate.removeKey(marketMaker);
        return !checked ? null : (entry.orderCount > 0) ? null : (entry.aggregate != null) ? entry.aggregate : (regional != null) ? regional : (nonCompositeCount == 0) ? composite : null;
    }
    return null;
}

19 Source : OrderBookList.java
with Mozilla Public License 2.0
from devexperts

// Check the node and return previous scope node that must be unchecked, or null.
protected Node<Order> checkNode(Node<Order> node) {
    Scope scope = node.getValue().getScope();
    if (!filter.allowScope(scope))
        return null;
    if (scope == Scope.COMPOSITE) {
        composite = node;
        if (nonCompositeCount == 0)
            check(node);
        return null;
    }
    char exchangeCode = node.getValue().getExchangeCode();
    IndexedSet<String, MMIDEntry> aggregate = aggregates[exchangeCode];
    if (scope == Scope.REGIONAL) {
        regionals[exchangeCode] = node;
        if (aggregate == null || aggregate.isEmpty()) {
            check(node);
            nonCompositeCount++;
        }
        return composite;
    }
    // Create entry for exchange and market-maker if needed
    if (aggregate == null) {
        aggregate = IndexedSet.create((IndexerFunction<String, MMIDEntry>) entry -> entry.mmid);
        aggregates[exchangeCode] = aggregate;
    }
    String marketMaker = node.getValue().getMarketMaker();
    MMIDEntry entry = aggregate.getByKey(marketMaker);
    if (entry == null)
        entry = aggregate.putIfAbsentAndGet(new MMIDEntry(marketMaker));
    Node<Order> regional = regionals[exchangeCode];
    if (scope == Scope.AGGREGATE) {
        entry.aggregate = node;
        if (entry.orderCount == 0) {
            check(node);
            nonCompositeCount++;
        }
        return (regional != null) ? regional : composite;
    }
    if (scope == Scope.ORDER) {
        entry.orderCount++;
        check(node);
        nonCompositeCount++;
        return (entry.aggregate != null) ? entry.aggregate : (regional != null) ? regional : composite;
    }
    return null;
}

19 Source : InstrumentProfileCollector.java
with Mozilla Public License 2.0
from devexperts

/**
 * Collects instrument profile updates and provides the live list of instrument profiles.
 * This clreplaced contains a map that keeps a unique instrument profile per symbol.
 * This clreplaced is intended to be used with {@link InstrumentProfileConnection} as a repository that keeps
 * profiles of all known instruments. See {@link InstrumentProfileConnection} for a usage example.
 *
 * <p>As set of instrument profiles stored in this collector can be accessed with {@link #view() view} method.
 * A snapshot plus a live stream of updates can be accessed with
 * {@link #addUpdateListener(InstrumentProfileUpdateListener) addUpdateListener} method.
 *
 * <p>Removal of instrument profile is represented by an {@link InstrumentProfile} instance with a
 * {@link InstrumentProfile#getType() type} equal to
 * <code>{@link InstrumentProfileType InstrumentProfileType}.{@link InstrumentProfileType#REMOVED REMOVED}.{@link InstrumentProfileType#name() name}()</code>.
 *
 * <p><b>This clreplaced is thread-safe.</b>
 */
public clreplaced InstrumentProfileCollector {

    private static final Logging log = Logging.getLogging(InstrumentProfileCollector.clreplaced);

    static {
        log.configureDebugEnabled(false);
    }

    // =====================  private instance fields =====================
    // sync
    private final IndexedSet<String, Entry> entriesBySymbol = IndexedSet.create((IndexerFunction<String, Entry>) entry -> entry.symbol);

    // sync
    private final CopyOnWriteArrayList<Agent> agents = new CopyOnWriteArrayList<>();

    // not-null, sync
    private Entry tail = new Entry();

    private long lastUpdateTime = System.currentTimeMillis();

    private long lastReportedTime;

    // =====================  public instance methods =====================
    /**
     * Returns last modification time (in milliseconds) of instrument profiles or zero if it is unknown.
     * Note, that while the time is represented in milliseconds, the actual granularity of time here is a second.
     * @return last modification time (in milliseconds) of instrument profiles or zero if it is unknown.
     */
    public final synchronized long getLastUpdateTime() {
        return lastReportedTime = lastUpdateTime;
    }

    /**
     * Convenience method to update one instrument profile in this collector. This is a shortcut for:
     * <pre><tt>
     *    {@link #updateInstrumentProfiles updateInstrumentProfiles}(Collections.singletonList(ip), null);
     * </tt></pre>
     *
     * <p>Note, that this method stores reference to an instance of a given {@link InstrumentProfile} object
     * inside this collector, unless a protected method
     * {@link #copyInstrumentProfile(InstrumentProfile) copyInstrumentProfile}
     * is overriden to create a copy.
     *
     * @param ip instrument profile.
     */
    public final void updateInstrumentProfile(InstrumentProfile ip) {
        boolean updated = false;
        synchronized (this) {
            if (updateInstrumentProfileImpl(ip, null)) {
                updateTimeImpl();
                updated = true;
            }
        }
        if (updated)
            notifyAgents();
    }

    /**
     * Updates a list of instrument profile and replacedign them a generation tag.
     * A generation tag can be later use with {@link #removeGenerations(Set) removeGenerations} method.
     *
     * <p>Removal of instrument profile is represented by an {@link InstrumentProfile} instance with a
     * {@link InstrumentProfile#getType() type} equal to
     * <code>{@link InstrumentProfileType InstrumentProfileType}.{@link InstrumentProfileType#REMOVED REMOVED}.{@link InstrumentProfileType#name() name}()</code>.
     *
     * <p>Note, that this method stores references to instances of {@link InstrumentProfile} objects from
     * a given list inside this collector, unless a protected method
     * {@link #copyInstrumentProfile(InstrumentProfile) copyInstrumentProfile}
     * is overriden to create a copy.
     *
     * @param ips a list of instrument profiles.
     * @param generation a generation tag, may be {@code null}.
     */
    public final void updateInstrumentProfiles(List<InstrumentProfile> ips, Object generation) {
        if (ips.isEmpty())
            return;
        boolean updated = false;
        synchronized (this) {
            for (InstrumentProfile ip : ips) if (updateInstrumentProfileImpl(ip, generation))
                updated = true;
            if (updated)
                updateTimeImpl();
        }
        if (updated)
            notifyAgents();
    }

    /**
     * Removes instrument profiles with non-null generation tags from a given set.
     * Note, that this method takes O(N) time, where N is the number of stored instrument profiles in this collector.
     * It shall be used sparingly.
     *
     * @param generations a set of generation tags.
     */
    public final void removeGenerations(Set<Object> generations) {
        if (generations == null)
            throw new NullPointerException();
        if (generations.isEmpty())
            // nothing to do
            return;
        boolean removed = false;
        synchronized (this) {
            for (Iterator<Entry> it = entriesBySymbol.iterator(); it.hasNext(); ) {
                Entry entry = it.next();
                if (entry.generation != null && generations.contains(entry.generation)) {
                    // [0] remove from snapshot
                    it.remove();
                    // the rest of removal
                    removeEntryImpl(entry);
                    removed = true;
                    if (log.debugEnabled())
                        log.debug("Removing " + debugString(entry.ip));
                }
            }
            if (removed)
                updateTimeImpl();
        }
        if (removed)
            notifyAgents();
    }

    /**
     * Returns a concurrent view of the set of instrument profiles.
     * Note, that removal of instrument profile is represented by an {@link InstrumentProfile} instance with a
     * {@link InstrumentProfile#getType() type} equal to
     * <code>{@link InstrumentProfileType InstrumentProfileType}.{@link InstrumentProfileType#REMOVED REMOVED}.{@link InstrumentProfileType#name() name}()</code>.
     * Normally, this view exposes only non-removed profiles. However, if iteration is concurrent with removal,
     * then a removed instrument profile (with a removed type) can be exposed by this view.
     *
     * @return a concurrent view of the set of instrument profiles.
     */
    public final Iterable<InstrumentProfile> view() {
        return new Iterable<InstrumentProfile>() {

            @Override
            public Iterator<InstrumentProfile> iterator() {
                return new Iterator<InstrumentProfile>() {

                    private final Iterator<Entry> entryIterator = entriesBySymbol.concurrenreplacederator();

                    @Override
                    public boolean hasNext() {
                        return entryIterator.hasNext();
                    }

                    @Override
                    public InstrumentProfile next() {
                        return entryIterator.next().ip;
                    }

                    @Override
                    public void remove() {
                        throw new UnsupportedOperationException();
                    }
                };
            }
        };
    }

    /**
     * Adds listener that is notified about any updates in the set of instrument profiles.
     * If a set of instrument profiles is not empty, then this listener is immediately
     * {@link InstrumentProfileUpdateListener#instrumentProfilesUpdated(Iterator) notified}
     * right from inside this add method.
     *
     * @param listener profile update listener.
     */
    public final void addUpdateListener(InstrumentProfileUpdateListener listener) {
        if (listener == null)
            throw new NullPointerException("null listener");
        Agent agent = new Agent(listener);
        agents.add(agent);
        agent.update();
    }

    /**
     * Removes listener that is notified about any updates in the set of instrument profiles.
     *
     * @param listener profile update listener.
     */
    public final void removeUpdateListener(InstrumentProfileUpdateListener listener) {
        for (Agent agent : agents) if (agent.listener == listener) {
            agents.remove(agent);
            return;
        }
    }

    // =====================  protected instance methods =====================
    /**
     * A hook to create a defensive copy of the instrument profile that is saved into
     * this collector. This method is invoked from inside of
     * {@link #updateInstrumentProfile(InstrumentProfile) updateInstrumentProfile} and
     * {@link #updateInstrumentProfiles(List, Object) updateInstrumentProfiles} methods
     * when a decision is made to save an instrument profile into this data
     * structure.
     *
     * <p>This implementation returns the original instrument reference.
     * Overriding methods may choose to create and return a copy of the instrument
     * profile object instead.
     *
     * <p>Override this method to safely reuse {@link InstrumentProfile} instances that are
     * preplaceded into this collector. This lazily creates their defensive copies,
     * just when the corresponding instrument profile is detected to have been changed.</p>
     *
     * @param ip the original instrument profile.
     * @return the original or copied instrument profile.
     */
    protected InstrumentProfile copyInstrumentProfile(InstrumentProfile ip) {
        return ip;
    }

    // =====================  private instance methods =====================
    // SYNC(this) required
    private void updateTimeImpl() {
        lastUpdateTime = Math.max(System.currentTimeMillis(), lastReportedTime + 1000);
    }

    private void notifyAgents() {
        for (Agent agent : agents) agent.update();
    }

    // SYNC(this) required
    private boolean updateInstrumentProfileImpl(InstrumentProfile ip, Object generation) {
        if (ip == null)
            throw new NullPointerException("null instrument");
        String symbol = ip.getSymbol();
        Entry oldEntry = entriesBySymbol.getByKey(symbol);
        if (oldEntry != null && oldEntry.ip != ip && oldEntry.ip.equals(ip)) {
            // different instance of the same instrument -- just update the generation (no need to notify)
            oldEntry.generation = generation;
            return false;
        }
        if (log.debugEnabled())
            log.debug((oldEntry == null ? "Adding " : "Updating ") + debugString(ip) + (oldEntry != null && oldEntry.ip == ip ? " (same instance)" : ""));
        // CONCURRENCY NOTE: The order of numbered operations is important
        // [0] Create a copy of the instrument (if copyInstrumentProfile is overriden is a subclreplaced)
        // This captures this instrument profile state in case of its concurrent modifications
        ip = copyInstrumentProfile(ip);
        Entry newEntry = new Entry(symbol, ip);
        newEntry.generation = generation;
        if (ip.getType().equals(InstrumentProfileType.REMOVED.name())) {
            // removing instrument
            if (oldEntry == null) {
                // it was not present before, so there's nothing else to do
                return false;
            } else {
                // [1a] Remove entry from snapshot
                entriesBySymbol.remove(oldEntry);
            }
        } else {
            // [1b] Add/update in snapshot (keep only non-removed ones in a snapshot)
            entriesBySymbol.put(newEntry);
        }
        // [2] link to update list (linearization for publishing of new instrument)
        linkUpdatedEntryImpl(newEntry);
        if (oldEntry != null) {
            // [3] mark old entry (linearization for removal of old instrument profile instance)
            oldEntry.old = true;
            unlinkRemovedEntryImpl(oldEntry);
        }
        return true;
    }

    private void removeEntryImpl(Entry entry) {
        // CONCURRENCY NOTE: The order of numbered operations is important
        // create new removed instrument
        InstrumentProfile removed = new InstrumentProfile();
        removed.setSymbol(entry.symbol);
        removed.setType(InstrumentProfileType.REMOVED.name());
        // [1] link new entry to update list with this removed instrument
        linkUpdatedEntryImpl(new Entry(entry.symbol, removed));
        // [2] mark old entry
        entry.old = true;
        unlinkRemovedEntryImpl(entry);
    }

    private void linkUpdatedEntryImpl(Entry entry) {
        Entry oldTail = tail;
        // new entry linearized in the list here
        oldTail.next = entry;
        entry.prev = oldTail;
        tail = entry;
        // if old tail was removed, it needs to be unlinked
        if (oldTail.old)
            unlinkRemovedEntryImpl(oldTail);
    }

    private void unlinkRemovedEntryImpl(Entry entry) {
        Entry next = entry.next;
        if (next == null)
            // never unlink tail -- need to preserve an update chain
            return;
        Entry prev = entry.prev;
        if (prev == null)
            // already unlinked (also true for dummy head)
            return;
        prev.next = next;
        next.prev = prev;
        // this marks entry as unlinked, just in case
        entry.prev = null;
    }

    private String debugString(InstrumentProfile ip) {
        return ip + " @" + Integer.toHexString(System.idenreplacedyHashCode(ip));
    }

    // =====================  private inner clreplacedes methods =====================
    private static clreplaced Entry {

        final String symbol;

        final InstrumentProfile ip;

        // sync
        Object generation;

        // sync
        Entry prev;

        volatile Entry next;

        // true if this entry was replaced with a fresh, new one
        volatile boolean old;

        // dummy initial tail entry constructor
        Entry() {
            this(null, null);
            old = true;
        }

        Entry(String symbol, InstrumentProfile ip) {
            this.symbol = symbol;
            this.ip = ip;
        }
    }

    private clreplaced Agent implements Iterator<InstrumentProfile> {

        final InstrumentProfileUpdateListener listener;

        Entry nextEntry;

        Entry tailEntry;

        Iterator<Entry> snapshoreplacederator;

        Agent(InstrumentProfileUpdateListener listener) {
            this.listener = listener;
            // CONCURRENCY NOTE: The order of numbered operations is important
            // [1] capture current tail
            tailEntry = tail;
            // [2] capture current snapshot
            snapshoreplacederator = entriesBySymbol.concurrenreplacederator();
        }

        // SYNC(this)
        private void ensureNext() {
            if (nextEntry != null)
                return;
            if (snapshoreplacederator != null) {
                if (snapshoreplacederator.hasNext()) {
                    nextEntry = snapshoreplacederator.next();
                    return;
                } else
                    snapshoreplacederator = null;
            }
            while (true) {
                Entry entry = tailEntry.next;
                if (entry == null)
                    // nothing new -- return
                    return;
                // move to next
                tailEntry = entry;
                if (!entry.old) {
                    nextEntry = entry;
                    return;
                }
            // loop to skip removed
            }
        }

        void update() {
            if (hasNext())
                listener.instrumentProfilesUpdated(this);
        }

        @Override
        public synchronized boolean hasNext() {
            ensureNext();
            return nextEntry != null;
        }

        @Override
        public synchronized InstrumentProfile next() {
            ensureNext();
            if (nextEntry == null)
                throw new NoSuchElementException();
            InstrumentProfile result = nextEntry.ip;
            nextEntry = null;
            return result;
        }

        @Override
        public void remove() {
            throw new UnsupportedOperationException();
        }
    }
}

18 Source : ServiceRouter.java
with Mozilla Public License 2.0
from devexperts

public clreplaced ServiceRouter<T> implements RMIObservableServiceDescriptors {

    // ==================== fields ====================
    private final IndexerFunction<T, Ref<T>> indexerRefByT = (IndexerFunction<T, Ref<T>>) value -> value.obj;

    private final List<Ref<T>> nearest = new ArrayList<>();

    private final Set<EndpointId> intermediateNodes = new HashSet<>();

    private int bestDistance = RMIService.UNAVAILABLE_METRIC;

    private int lastSendDistance = RMIService.UNAVAILABLE_METRIC;

    private final List<RMIServiceDescriptorsListener> listeners = new CopyOnWriteArrayList<>();

    private final IndexedSet<T, Ref<T>> descriptors = IndexedSet.create(indexerRefByT);

    private final RMIServiceId serviceId;

    private final EndpointId endpointId;

    // ==================== static factory methods ====================
    public static <T> ServiceRouter<T> createRouter(EndpointId endpoint, RMIServiceId serviceId) {
        if (serviceId == null)
            throw new NullPointerException("ServiceId can not be null");
        return new ServiceRouter<>(endpoint, serviceId);
    }

    static ServiceRouter<RMIConnection> createAnonymousRouter(EndpointId endpointId) {
        return new AnonymousRouter(endpointId);
    }

    // ==================== private constructor ====================
    private ServiceRouter(EndpointId endpointId, RMIServiceId serviceId) {
        this.endpointId = endpointId;
        this.serviceId = serviceId;
    }

    // ==================== public methods ====================
    public synchronized void updateDescriptor(RMIServiceDescriptor descriptor, int dist, T obj) {
        if (dist == RMIService.UNAVAILABLE_METRIC) {
            removeDescriptor(descriptor, obj);
            return;
        }
        Ref<T> ref = new Ref<>(descriptor, obj);
        descriptors.add(ref);
        if (updateDistanceInfo(ref))
            notifyListener(pickFirstDescriptor(), bestDistance);
    }

    public synchronized void removeDescriptor(RMIServiceDescriptor descriptor, T obj) {
        if (descriptors.removeKey(obj) != null && updateDistanceInfo(new Ref<>(descriptor, obj))) {
            notifyListener(nearest.isEmpty() ? descriptor : pickFirstDescriptor(), bestDistance);
        }
    }

    public synchronized RMIServiceDescriptor pickFirstDescriptor() {
        if (nearest.isEmpty())
            return null;
        int randomIndex = ThreadLocalRandom.current().nextInt(nearest.size());
        RMIServiceDescriptor descriptor = nearest.get(randomIndex).descriptor;
        return RMIServiceDescriptor.createDescriptor(serviceId, descriptor.getDistance(), intermediateNodes, descriptor.getProperties());
    }

    public synchronized T pickRandom() {
        if (nearest.isEmpty())
            return null;
        int randomIndex = ThreadLocalRandom.current().nextInt(nearest.size());
        return nearest.get(randomIndex).obj;
    }

    @Override
    public synchronized void addServiceDescriptorsListener(RMIServiceDescriptorsListener listener) {
        if (!isEmpty())
            listener.descriptorsUpdated(Collections.singletonList(pickFirstDescriptor()));
        listeners.add(listener);
    }

    @Override
    public synchronized void removeServiceDescriptorsListener(RMIServiceDescriptorsListener listener) {
        listener.descriptorsUpdated(Collections.singletonList(RMIServiceDescriptor.createUnavailableDescriptor(serviceId, null)));
        listeners.remove(listener);
    }

    @Override
    public boolean isAvailable() {
        return !isEmpty();
    }

    @Override
    public synchronized String toString() {
        return "Server " + endpointId + " ServiceRoute{" + "serviceId=" + serviceId + ", " + "nearest=" + nearest + ", bestDist=" + bestDistance + ", " + "descriptors=" + descriptors + "lastSendDist=" + lastSendDistance + "}";
    }

    synchronized boolean isEmpty() {
        return descriptors.isEmpty();
    }

    // ==================== private methods ====================
    private boolean updateDistanceInfo(Ref<T> ref) {
        if (!ref.descriptor.isAvailable()) {
            if (nearest.isEmpty())
                return false;
        } else {
            if (nearest.isEmpty() || bestDistance > ref.distance) {
                nearest.clear();
                nearest.add(ref);
                intermediateNodes.clear();
                intermediateNodes.addAll(ref.descriptor.getIntermediateNodes());
                bestDistance = ref.distance;
                return true;
            }
        }
        nearest.remove(ref);
        if (!nearest.isEmpty()) {
            int sizeIntermediate = intermediateNodes.size();
            intermediateNodes.clear();
            for (Ref<T> tRef : nearest) intermediateNodes.addAll(tRef.descriptor.getIntermediateNodes());
            return sizeIntermediate != intermediateNodes.size();
        }
        updateNearest();
        return true;
    }

    private void updateNearest() {
        nearest.clear();
        intermediateNodes.clear();
        bestDistance = RMIService.UNAVAILABLE_METRIC;
        for (Ref<T> ref : descriptors) {
            if (ref.distance == bestDistance) {
                nearest.add(ref);
                intermediateNodes.addAll(ref.descriptor.getIntermediateNodes());
            } else if (ref.distance < bestDistance) {
                nearest.clear();
                intermediateNodes.clear();
                bestDistance = ref.distance;
                nearest.add(ref);
                intermediateNodes.addAll(ref.descriptor.getIntermediateNodes());
            }
        }
    }

    private void notifyListener(RMIServiceDescriptor descriptor, int newDistance) {
        lastSendDistance = newDistance;
        for (RMIServiceDescriptorsListener listener : listeners) try {
            listener.descriptorsUpdated(Collections.singletonList(descriptor));
        } catch (Throwable t) {
            Logging.getLogging(ServiceRouter.clreplaced).error("Failed to update service descriptors", t);
        }
    }

    // ----------------------------- AnonymousRouter for processing old version RMI -----------------------------
    static clreplaced AnonymousRouter extends ServiceRouter<RMIConnection> {

        ArrayList<RMIConnection> connections = new ArrayList<>();

        AnonymousRouter(EndpointId endpointId) {
            super(endpointId, null);
        }

        @Override
        public synchronized void updateDescriptor(RMIServiceDescriptor descriptor, int dist, RMIConnection connection) {
            connections.add(connection);
        }

        @Override
        public synchronized void removeDescriptor(RMIServiceDescriptor descriptor, RMIConnection obj) {
            connections.remove(obj);
        }

        @Override
        public synchronized void addServiceDescriptorsListener(RMIServiceDescriptorsListener listener) {
        }

        @Override
        public synchronized void removeServiceDescriptorsListener(RMIServiceDescriptorsListener listener) {
        }

        @Override
        public synchronized RMIConnection pickRandom() {
            if (connections.isEmpty())
                return null;
            int randomIndex = ThreadLocalRandom.current().nextInt(connections.size());
            return connections.get(randomIndex);
        }
    }

    // ----------------------------- auxiliary container -----------------------------
    static clreplaced Ref<T> implements Comparable<Ref<T>> {

        final int distance;

        final RMIServiceDescriptor descriptor;

        final T obj;

        Ref(RMIServiceDescriptor descriptor, T obj) {
            this.descriptor = descriptor;
            this.distance = descriptor.getDistance();
            this.obj = obj;
        }

        @Override
        public int compareTo(@Nonnull Ref<T> o) {
            return distance - o.distance;
        }

        @Override
        public int hashCode() {
            return (descriptor != null ? descriptor.getServiceId().hashCode() : super.hashCode()) * 27 + obj.hashCode();
        }

        @Override
        public boolean equals(Object obj) {
            if (!(obj instanceof Ref))
                return false;
            Ref<?> other = (Ref<?>) obj;
            return descriptor != null ? descriptor.getServiceId().equals(other.descriptor.getServiceId()) && this.obj.equals(other.obj) : other.descriptor == null && obj.equals(other.obj);
        }

        @Override
        public String toString() {
            return "Ref{" + "descriptor=" + descriptor + ", distance=" + distance + ", obj=" + obj + "}";
        }
    }
}

18 Source : RunningTask.java
with Mozilla Public License 2.0
from devexperts

synchronized RMITaskImpl<?> removeById(long requestId, long channelId, RMIChannelType type) {
    if (channelId == 0)
        return serverChannelTasks.removeKey(requestId);
    IndexedSet<Long, RMITaskImpl<?>> set = getMap(type).get(channelId);
    if (set == null || set.isEmpty())
        return null;
    return set.removeKey(requestId);
}

18 Source : RunningTask.java
with Mozilla Public License 2.0
from devexperts

synchronized void add(RMITaskImpl<?> task) {
    if (!task.isNestedTask()) {
        serverChannelTasks.add(task);
        return;
    }
    IndexedSet<Long, RMITaskImpl<?>> set = getMap(task.getChannel().getType()).get(task.getChannelId());
    if (set == null) {
        set = IndexedSet.createLong(RMITaskImpl.TASK_INDEXER_BY_ID);
        getMap(task.getChannel().getType()).put(task.getChannelId(), set);
    }
    set.add(task);
}

18 Source : RunningTask.java
with Mozilla Public License 2.0
from devexperts

// for top-level tasks
synchronized void remove(RMIChannelOwner owner, long channelId) {
    IndexedSet<Long, RMITaskImpl<?>> set = getMap(owner.getChannelType()).get(channelId);
    if (set != null && !set.isEmpty()) {
        for (RMITaskImpl<?> runTask : set) runTask.completeExceptionally(RMIExceptionType.CHANNEL_CLOSED, null);
    }
    if (owner.getChannelType() == RMIChannelType.SERVER_CHANNEL) {
        // noinspection SuspiciousMethodCalls
        serverChannelTasks.remove(owner);
    }
}

18 Source : RMIChannelImpl.java
with Mozilla Public License 2.0
from devexperts

clreplaced RMIChannelImpl extends RMIClientPortImpl implements RMIChannel {

    static final IndexerFunction.LongKey<RMIChannelImpl> CHANNEL_INDEXER_BY_REQUEST_ID = value -> value.channelId;

    private final ChannelRequestSender requestSender = new ChannelRequestSender();

    private final RMIChannelOwner owner;

    private final long channelId;

    private final RMIChannelType type;

    @GuardedBy("this")
    private final IndexedSet<String, RMIService<?>> handlers = IndexedSet.create(RMIService.RMI_SERVICE_INDEXER);

    @GuardedBy("this")
    private volatile RMIChannelState state = RMIChannelState.NEW;

    @GuardedBy("this")
    private List<RMIRequestImpl<?>> // initialized on first need, can be != null only when state = NEW
    preOpenOutgoingRequests;

    @GuardedBy("this")
    private List<ServerRequestInfo> // initialized on first need, can be != null only when state = NEW
    preOpenIncomingRequests;

    /**
     * Connection is set to non-null by {@link #registerChannel(RMIConnection)}.
     * It means that channel was added to connection's ChannelsManager and needs to be removed from there on close.
     * This shall always happen before channel is open.
     */
    @GuardedBy("this")
    private RMIConnection connection;

    /**
     * This is to control that all task in a given channel execute sequentially.
     * Initialized lazily. The first task in the currently execution one, when it completes or suspends,
     * then it gets removed from this queue and the next one is submitted.
     */
    @GuardedBy("this")
    private ArrayDeque<RMIExecutionTaskImpl<?>> executionTasks;

    @SuppressWarnings("unchecked")
    RMIChannelImpl(RMIEndpointImpl endpoint, Marshalled<?> subject, long channelId, RMIChannelOwner owner) {
        super(endpoint, subject);
        // always captured at the channel creation time
        replacedert subject != null;
        this.owner = owner;
        type = owner.getChannelType();
        this.channelId = channelId;
    }

    @Override
    public Object getOwner() {
        return owner;
    }

    @Override
    public <T> RMIRequest<T> createRequest(RMIRequestType type, RMIOperation<T> operation, Object... parameters) {
        return new RMIRequestImpl<>(requestSender, this, createRequestMessage(type, operation, parameters));
    }

    @Override
    public <T> RMIRequest<T> createRequest(RMIRequestMessage<T> message) {
        return new RMIRequestImpl<>(requestSender, this, updateRequestMessage(message));
    }

    @Override
    public boolean isOpen() {
        return state == RMIChannelState.OPEN;
    }

    @Override
    public synchronized void addChannelHandler(RMIService<?> handler) {
        if (state != RMIChannelState.NEW)
            throw new IllegalStateException("The channel has already been opened or closed");
        if (handlers.containsKey(handler.getServiceName()))
            throw new IllegalArgumentException("Handler named " + handler.getServiceName() + " has been added");
        handlers.add(handler);
    }

    @Override
    public RMIChannelState getState() {
        return state;
    }

    @Override
    public <T> void addChannelHandler(T implementation, Clreplaced<T> handlerInterface) {
        addChannelHandler(new RMIServiceImplementation<>(implementation, handlerInterface, RMIService.getServiceName(handlerInterface)));
    }

    @Override
    public synchronized void removeChannelHandler(RMIService<?> handler) {
        handlers.removeValue(handler);
    }

    @Override
    public RMIChannelType getType() {
        return type;
    }

    @Override
    protected RequestSender getRequestSender() {
        return requestSender;
    }

    long getChannelId() {
        return channelId;
    }

    /**
     * Register this channel with connection, so that is can receive channel messages.
     */
    synchronized void registerChannel(RMIConnection connection) {
        replacedert this.connection == null;
        this.connection = connection;
        connection.channelsManager.addChannel(this);
    }

    @SuppressWarnings("unchecked")
    void open(RMIConnection connection) {
        List<ServerRequestInfo> preOpenIncomingRequests;
        synchronized (this) {
            if (state == RMIChannelState.CLOSED)
                return;
            if (connection.closed)
                return;
            if (type == RMIChannelType.CLIENT_CHANNEL) {
                // Client channels are registered when they become open
                replacedert this.connection == null;
                registerChannel(connection);
            }
            if (state == RMIChannelState.CANCELLING) {
                if (preOpenOutgoingRequests != null && !preOpenOutgoingRequests.isEmpty())
                    connection.requestsManager.addOutgoingRequest(preOpenOutgoingRequests.get(0));
                preOpenOutgoingRequests = null;
                this.preOpenIncomingRequests = null;
                return;
            }
            state = RMIChannelState.OPEN;
            if (preOpenOutgoingRequests != null) {
                preOpenOutgoingRequests.forEach(connection.requestsManager::addOutgoingRequest);
                preOpenOutgoingRequests = null;
            }
            // must submit incoming requests outside of lock
            preOpenIncomingRequests = this.preOpenIncomingRequests;
            this.preOpenIncomingRequests = null;
        }
        if (preOpenIncomingRequests != null) {
            for (ServerRequestInfo requestInfo : preOpenIncomingRequests) connection.messageProcessor.createAndSubmitTask(this, requestInfo);
        }
    }

    synchronized void close() {
        if (state == RMIChannelState.CLOSED)
            return;
        if (state == RMIChannelState.NEW) {
            preOpenOutgoingRequests = null;
            preOpenIncomingRequests = null;
        }
        state = RMIChannelState.CLOSED;
        if (connection != null) {
            connection.channelsManager.removeChannel(channelId, type);
            connection.tasksManager.notifyTaskCompleted(owner, channelId);
        }
    }

    synchronized RMIService<?> getHandler(String handlerName) {
        RMIService<?> result = handlers.getByKey(handlerName);
        if (result != null)
            return result;
        return handlers.getByKey("*");
    }

    @Override
    public String toString() {
        return "Channel{" + "channelId=" + channelId + ", " + "type=" + type + ", " + "owner=" + owner + ", " + "state=" + state + "}";
    }

    synchronized void cancel(RMICancelType cancel) {
        if (type == RMIChannelType.SERVER_CHANNEL) {
            if (cancel == RMICancelType.ABORT_RUNNING)
                ((RMITaskImpl<?>) owner).cancel();
            else
                ((RMITaskImpl<?>) owner).cancelWithConfirmation();
            return;
        }
        switch(state) {
            case NEW:
                if (preOpenOutgoingRequests == null)
                    preOpenOutgoingRequests = new ArrayList<>();
                for (RMIRequestImpl<?> request : preOpenOutgoingRequests) request.setFailedState(RMIExceptionType.CHANNEL_CLOSED, null);
                preOpenOutgoingRequests.clear();
                break;
            case OPEN:
                connection.tasksManager.cancelAllTasks(getChannelId(), cancel.getId(), type);
                break;
            default:
                return;
        }
        // for top-level request
        RMIRequest<Void> cancelChannel = createRequest(new RMIRequestMessage<>(RMIRequestType.ONE_WAY, cancel == RMICancelType.ABORT_RUNNING ? RMIRequestImpl.ABORT_CANCEL : RMIRequestImpl.CANCEL_WITH_CONFIRMATION, 0L));
        cancelChannel.setListener(request -> this.close());
        state = RMIChannelState.CANCELLING;
        cancelChannel.send();
    }

    // returns false when channel is already closed
    boolean addIncomingRequest(ServerRequestInfo request) {
        synchronized (this) {
            switch(state) {
                case NEW:
                    if (preOpenIncomingRequests == null)
                        preOpenIncomingRequests = new ArrayList<>();
                    preOpenIncomingRequests.add(request);
                    // wait until open
                    return true;
                case CLOSED:
                    // don't do anything -- too late (channel already closed)
                    return false;
                default:
                    // try to submit outside of lock
                    break;
            }
        }
        connection.messageProcessor.createAndSubmitTask(this, request);
        return true;
    }

    private void addOutgoingRequestImpl(RMIRequestImpl<?> request) {
        synchronized (this) {
            switch(state) {
                case NEW:
                    if (preOpenOutgoingRequests == null)
                        preOpenOutgoingRequests = new ArrayList<>();
                    preOpenOutgoingRequests.add(request);
                    // ok
                    return;
                case OPEN:
                    connection.requestsManager.addOutgoingRequest(request);
                    // ok
                    return;
                case CANCELLING:
                    if (preOpenOutgoingRequests == null)
                        connection.requestsManager.addOutgoingRequest(request);
                    else
                        preOpenOutgoingRequests.add(request);
                    return;
            }
        // otherwise (state==CLOSED) make failed outside of the lock!
        }
        // Must update request outside of the lock (see Lock Hierarchy in RMIRequestImpl)
        request.setFailedState(RMIExceptionType.CHANNEL_CLOSED, null);
    }

    private synchronized boolean dropPendingRequestImpl(RMIRequestImpl<?> request) {
        return preOpenOutgoingRequests != null && preOpenOutgoingRequests.remove(request);
    }

    Executor getExecutor() {
        return owner.getExecutor();
    }

    void enqueueForSubmissionSerially(RMIExecutionTaskImpl<?> executionTask) {
        boolean submitNow;
        synchronized (this) {
            if (executionTasks == null)
                executionTasks = new ArrayDeque<>(2);
            submitNow = executionTasks.isEmpty();
            executionTasks.add(executionTask);
        }
        if (submitNow) {
            if (!executionTask.submitExecutionNow())
                submitNextTask(executionTask);
        }
    }

    void submitNextTask(RMIExecutionTaskImpl<?> executionTask) {
        do {
            replacedert executionTask.submitNextNow();
            synchronized (this) {
                if (executionTasks.peekFirst() != executionTask)
                    // not a first task -- bail out
                    return;
                executionTasks.removeFirst();
                // look at next task to execute
                executionTask = executionTasks.peekFirst();
                if (executionTask == null)
                    // not more tasks to execute
                    return;
            }
        } while (!executionTask.submitExecutionNow());
    }

    private clreplaced ChannelRequestSender extends RequestSender {

        @Override
        RMIEndpointImpl getEndpoint() {
            return RMIChannelImpl.this.getEndpoint();
        }

        @Override
        public Executor getExecutor() {
            return RMIChannelImpl.this.getExecutor();
        }

        @Override
        public void addOutgoingRequest(RMIRequestImpl<?> request) {
            addOutgoingRequestImpl(request);
        }

        @Override
        public boolean dropPendingRequest(RMIRequestImpl<?> request) {
            return dropPendingRequestImpl(request);
        }
    }
}

18 Source : ChannelsManager.java
with Mozilla Public License 2.0
from devexperts

private IndexedSet<Long, RMIChannelImpl> getSet(RMIChannelType type) {
    IndexedSet<Long, RMIChannelImpl> result = channels.get(type);
    if (result == null) {
        result = IndexedSet.createLong(RMIChannelImpl.CHANNEL_INDEXER_BY_REQUEST_ID);
        channels.put(type, result);
    }
    return result;
}

18 Source : BinaryQTPParser.java
with Mozilla Public License 2.0
from devexperts

/**
 * Parses QTP messages in binary format from byte stream.
 * The input for this parser must be configured with {@link #setInput(BufferedInput)} method
 * immediately after construction.
 *
 * @see AbstractQTPParser
 * @see BinaryQTPComposer
 */
public clreplaced BinaryQTPParser extends AbstractQTPParser {

    // ======================== protected instance fields ========================
    protected final SymbolCodec.Reader symbolReader;

    // ======================== private instance fields ========================
    private final BufferedInputPart msg = new BufferedInputPart();

    private SymbolCodec.Resolver symbolResolver;

    private ProtocolDescriptor protocolDescriptor;

    private HeartbeatPayload lastHeartbeatPayload;

    private BinaryRecordDesc[] recordMap;

    private Set<String> unknownRecordNames;

    private IndexedSet<Long, ParreplacedionedMessage> parreplacedionedMessages;

    // ======================== constructor and instance methods ========================
    /**
     * Constructs parser with a specified scheme.
     *
     * @param scheme data scheme.
     */
    public BinaryQTPParser(DataScheme scheme) {
        super(scheme);
        symbolReader = scheme.getCodec().createReader();
    }

    // ------------------------ configuration methods ------------------------
    // extension point to parse binary data without record description messages
    protected boolean isSchemeKnown() {
        return false;
    }

    // ------------------------ session control ------------------------
    /**
     * Resets session state for this parser.
     * Resets the state of all parsed describes so far, as if this parser was just created.
     */
    @Override
    public void resetSession() {
        if (recordMap != null)
            Arrays.fill(recordMap, null);
        if (unknownRecordNames != null)
            unknownRecordNames.clear();
        protocolDescriptor = null;
        if (lastHeartbeatPayload != null)
            lastHeartbeatPayload.clear();
    }

    // ------------------------ top-level parsing ------------------------
    @Override
    protected void parseImpl(BufferedInput in, MessageConsumer consumer) throws IOException {
        // 
        // Each message consists of three elements:
        // 
        // <messageLength> <messageType> <messagePayload>
        // |                              |
        // +------------------------------+
        // messageBody
        // 
        if (consumer instanceof SymbolCodec.Resolver)
            symbolResolver = (SymbolCodec.Resolver) consumer;
        try {
            // clear symbolResolver in finally...
            // limit and process may overflow (because removeBytes is optional, so need to subtract them before comparison)
            while (in.hasAvailable()) {
                if (!resyncOnParse(in))
                    // failed to sync stream -- bailout
                    return;
                long messageStartPosition = in.totalPosition();
                doBeforeMessageLength(in);
                in.mark();
                long longMessageLength;
                try {
                    longMessageLength = in.readCompactLong();
                } catch (EOFException e) {
                    // reset input to retry parsing when more bytes available
                    in.reset();
                    in.unmark();
                    // message is incomplete -- need more bytes
                    break;
                }
                if (longMessageLength < 0 || longMessageLength > Integer.MAX_VALUE) {
                    dumpParseHeaderErrorReport(in, "Invalid messageLength=" + longMessageLength);
                    if (resyncOnCorrupted(in))
                        // resync again
                        continue;
                    else {
                        // process all good so far
                        processPending(consumer);
                        // handle error
                        consumer.handleCorruptedStream();
                        // cannot continue parsing on stream with corrupted length, do not reset (leave stream on where it is)
                        break;
                    }
                }
                int messageLength = (int) longMessageLength;
                if (!in.hasAvailable(messageLength)) {
                    // reset input to retry parsing when more bytes available
                    in.reset();
                    in.unmark();
                    // message is incomplete -- need more bytes
                    break;
                }
                long messageEndPosition = in.totalPosition() + messageLength;
                try {
                    parseMessageBody(in, consumer, messageLength, messageEndPosition);
                } catch (CorruptedException e) {
                    if (resyncOnCorrupted(in))
                        // resync again
                        continue;
                    else {
                        // process all good so far
                        processPending(consumer);
                        // handle error
                        if (e instanceof CorruptedMessageException)
                            consumer.handleCorruptedMessage(((CorruptedMessageException) e).messageTypeId);
                        else
                            consumer.handleCorruptedStream();
                    }
                }
                // skip the whole message (regardless of whether is was fully parsed or not)
                in.seek(messageEndPosition);
                // undo mark
                in.unmark();
                // count bytes processed and continue to the next message
                stats.updateIOReadBytes(messageEndPosition - messageStartPosition);
            }
        } finally {
            symbolResolver = null;
        }
    }

    // ------------------------ resync support ------------------------
    /**
     * Extension point to resynchronize broken/partial input stream before each message.
     * It is invoked before attempting to parse a message.
     * @param in the input, it is not marked.
     * @return true when stream is in sync and message can be parsed.
     */
    protected boolean resyncOnParse(BufferedInput in) throws IOException {
        return true;
    }

    /**
     * Extension point to resynchronize broken/partial input stream on corrupted message.
     * It is invoked on each failure to parse a message.
     * @param in the input, it is marked.
     * @return true when in is set to the next byte to read and stream shall be resynchronized again,
     *         false if error shall be handled.
     */
    protected boolean resyncOnCorrupted(BufferedInput in) throws IOException {
        return false;
    }

    // ------------------------ helper methods to parse message parts ------------------------
    private void parseMessageBody(BufferedInput in, MessageConsumer consumer, int messageLength, long messageEndPosition) throws IOException, CorruptedException {
        msg.setInput(in, messageLength);
        try {
            msg.mark();
            int typeId = parseMessageType(msg);
            doAfterMessageType(msg);
            parseMessagePayload(msg, consumer, typeId, (int) (messageEndPosition - msg.totalPosition()));
            doAfterMessageBody(msg, typeId);
        } finally {
            // msg will be reused on next calls to parseImpl, we reset it to fail fast
            // if it is accidentally used outside of this block
            msg.resetInput();
        }
    }

    private int parseMessageType(BufferedInput msg) throws IOException, CorruptedException {
        if (!msg.hasAvailable())
            // default message type (zero-length messages are treated as just heartbeats)
            return MESSAGE_HEARTBEAT;
        // parse type if not empty message, empty message is heartbeat
        long longTypeId;
        try {
            longTypeId = msg.readCompactLong();
        } catch (EOFException e) {
            dumpParseMessageErrorReport(msg, "Not enough bytes in message to read message typeId", e, -1);
            throw new CorruptedException();
        }
        if (longTypeId < 0 || longTypeId > Integer.MAX_VALUE) {
            dumpParseMessageErrorReport(msg, "Invalid typeId=" + longTypeId, null, -1);
            throw new CorruptedException();
        }
        return (int) longTypeId;
    }

    private void parseMessagePayload(BufferedInput msg, MessageConsumer consumer, int typeId, int payloadLength) throws IOException, CorruptedMessageException {
        MessageType messageType = MessageType.findById(typeId);
        try {
            if (messageType != null && messageType.hasRecords()) {
                RecordBuffer buf = nextRecordsMessage(consumer, messageType);
                if (!buf.isEmpty() && !buf.hasEstimatedCapacityForBytes(payloadLength)) {
                    // not enough capacity in non-empty buffer -- don't let it grow too big to pool
                    processPending(consumer);
                    buf = nextRecordsMessage(consumer, messageType);
                }
                if (messageType.isData())
                    parseData(msg, buf);
                else if (messageType.isSubscription())
                    parseSubscription(msg, buf, messageType);
                else
                    // must be either data or subscription
                    throw new replacedertionError(messageType.toString());
            } else
                parseOther(msg, consumer, typeId, payloadLength);
        } catch (CorruptedException e) {
            // rethrow with message type id
            throw new CorruptedMessageException(e, typeId);
        }
    }

    private void parseOther(BufferedInput msg, MessageConsumer consumer, int typeId, int payloadLength) throws IOException, CorruptedException {
        switch(typeId) {
            case MESSAGE_HEARTBEAT:
                // shall be in-order with record message
                processPending(consumer);
                parseHeartbeat(msg, consumer);
                break;
            case MESSAGE_DESCRIBE_RECORDS:
                // todo: error handling is not atomic now -- partially parsed message is still being processed.
                parseDescribeRecords(msg);
                break;
            case MESSAGE_DESCRIBE_PROTOCOL:
                // shall be in-order with record message
                processPending(consumer);
                parseDescribeProtocol(msg, consumer);
                break;
            case MESSAGE_PART:
                parseMessagePart(msg, consumer, payloadLength);
                break;
            case MESSAGE_DESCRIBE_RESERVED:
            case MESSAGE_TEXT_FORMAT:
                // just ignore those messages without any processing
                break;
            default:
                if (consumer instanceof MessageConsumerAdapter)
                    ((MessageConsumerAdapter) consumer).processOtherMessage(typeId, msg, payloadLength);
                else {
                    byte[] bytes = new byte[payloadLength];
                    msg.readFully(bytes);
                    consumer.processOtherMessage(typeId, bytes, 0, payloadLength);
                }
        }
    }

    private void parseDescribeProtocol(BufferedInput msg, MessageConsumer consumer) throws CorruptedException {
        try {
            protocolDescriptor = ProtocolDescriptor.newPeerProtocolDescriptor(protocolDescriptor);
            protocolDescriptor.parseFrom(msg);
        } catch (IOException e) {
            dumpParseMessageErrorReport(msg, e.getMessage(), e, -1);
            throw new CorruptedException(e);
        }
        ProtocolDescriptor desc = applyReadAs(protocolDescriptor);
        // MIND THE BUG: [QD-808] Event flags are not sent immediately after connection establishment (random effect)
        // NOTE: MUST INVOKE onDescribeProtocol first
        onDescribeProtocol(desc);
        // then preplaced it to consumer
        consumer.processDescribeProtocol(desc, true);
    }

    /**
     * Extension point for ConnectionQTPParser.
     * Invoked <b>after</b> {@link MessageConsumer#processDescribeProtocol(ProtocolDescriptor, boolean)} .
     */
    void onDescribeProtocol(ProtocolDescriptor desc) {
    }

    private void parseHeartbeat(BufferedInput msg, MessageConsumer consumer) throws CorruptedException {
        try {
            if (!msg.hasAvailable())
                // skip empty heartbeat
                return;
            if (lastHeartbeatPayload == null)
                lastHeartbeatPayload = new HeartbeatPayload();
            else
                lastHeartbeatPayload.clear();
            lastHeartbeatPayload.parseFrom(msg);
        } catch (IOException e) {
            dumpParseMessageErrorReport(msg, e.getMessage(), e, -1);
            throw new CorruptedException(e);
        }
        consumer.processHeartbeat(lastHeartbeatPayload);
        onHeartbeat(lastHeartbeatPayload);
    }

    /**
     * Extension point for ConnectionQTPParser.
     * Invoked <b>after</b> {@link MessageConsumer#processHeartbeat(HeartbeatPayload)}.
     */
    void onHeartbeat(HeartbeatPayload heartbeatPayload) {
    }

    private void parseDescribeRecords(BufferedInput msg) throws CorruptedException {
        long lastRecPosition = msg.totalPosition();
        try {
            while (msg.hasAvailable()) {
                // Read record description from stream
                int id = msg.readCompactInt();
                String recordName = msg.readUTFString();
                int nFld = msg.readCompactInt();
                if (id < 0 || recordName == null || recordName.isEmpty() || nFld < 0)
                    throw new IOException("Corrupted record information");
                // names
                String[] names = new String[nFld];
                // types
                int[] types = new int[nFld];
                for (int i = 0; i < nFld; i++) {
                    String name = msg.readUTFString();
                    int type = msg.readCompactInt();
                    if (name == null || name.isEmpty() || type < SerialFieldType.Bits.MIN_TYPE_ID || type > SerialFieldType.Bits.MAX_TYPE_ID) {
                        throw new IOException("Corrupted field information for field " + name + ", " + "type " + Integer.toHexString(type) + " in record #" + id + " " + recordName);
                    }
                    names[i] = name;
                    types[i] = type;
                }
                // Try to find record
                DataRecord record = scheme.findRecordByName(recordName);
                if (record == null) {
                    // Complain (at most once) if not found
                    if (unknownRecordNames == null)
                        unknownRecordNames = new HashSet<>();
                    if (unknownRecordNames.add(recordName))
                        // complain only once about unknown record, see QD-436
                        QDLog.log.info("Record #" + id + " '" + recordName + "' " + "is not found in data scheme. Incoming data and subscription will be skipped.");
                }
                // Create reader descriptor
                try {
                    remapRecord(id, wrapRecordDesc(new BinaryRecordDesc(record, nFld, names, types, readEventTimeSequence, BinaryRecordDesc.DIR_READ)));
                } catch (BinaryRecordDesc.InvalidDescException e) {
                    QDLog.log.info("Record #" + id + " '" + recordName + "' " + "cannot be parsed: " + e.getMessage());
                }
                lastRecPosition = msg.totalPosition();
            }
        } catch (IOException e) {
            dumpParseMessageErrorReport(msg, e.getMessage(), e, lastRecPosition);
            throw new CorruptedException(e);
        }
    }

    // is overridden by ConnectionByteArrayParser to do the actual job
    void updateCursorTimeMark(RecordCursor cursor) {
    }

    // is overridden by ConnectionByteArrayParser to update rtt
    void updateMoreIOReadSubRecordStats() {
    }

    // is overridden by ConnectionByteArrayParser to update rtt and lag
    void updateMoreIOReadDataRecordStats() {
    }

    private void parseData(BufferedInput msg, RecordBuffer buf) throws CorruptedException {
        symbolReader.reset(ProtocolOption.SUPPORTED_SET);
        long lastRecPosition = msg.totalPosition();
        long startBufLimit = buf.getLimit();
        try {
            while (msg.hasAvailable()) {
                readSymbol(msg);
                int id = readRecordId(msg);
                BinaryRecordDesc rr = getOrCreateRecordDesc(id);
                if (rr == null)
                    throw new IOException("Unknown record #" + id);
                RecordCursor cur = rr.readRecord(msg, buf, symbolReader.getCipher(), symbolReader.getSymbol(), symbolReader.getEventFlags());
                setEventTimeSequenceIfNeeded(cur);
                long position = msg.totalPosition();
                if (cur != null) {
                    updateCursorTimeMark(cur);
                    stats.updateIOReadRecordBytes(cur.getRecord().getId(), position - lastRecPosition);
                    stats.updateIOReadDataRecord();
                    updateMoreIOReadDataRecordStats();
                }
                lastRecPosition = position;
            }
        } catch (IOException | IllegalStateException e) {
            // IllegalStateException Happens when visiting of previous record was not properly finished.
            // IllegalStateException Happens when data schemes are incompatible or message is corrupted.
            dumpParseDataErrorReport(msg, e, buf, startBufLimit, lastRecPosition);
            throw new CorruptedException(e);
        }
    }

    private void parseSubscription(BufferedInput msg, RecordBuffer buf, MessageType messageType) throws CorruptedException {
        symbolReader.reset(ProtocolOption.SUPPORTED_SET);
        long lastRecPosition = msg.totalPosition();
        long startBufLimit = buf.getLimit();
        boolean historySubscriptionAdd = messageType.isHistorySubscriptionAdd();
        try {
            while (msg.hasAvailable()) {
                readSymbol(msg);
                int id = readRecordId(msg);
                BinaryRecordDesc rr = getOrCreateRecordDesc(id);
                if (rr == null)
                    throw new IOException("Unknown record #" + id);
                long time = 0;
                if (historySubscriptionAdd)
                    time = readSubscriptionTime(msg);
                long position = msg.totalPosition();
                DataRecord record = rr.getRecord();
                if (record != null) {
                    RecordCursor cur = buf.add(record, symbolReader.getCipher(), symbolReader.getSymbol());
                    setEventTimeSequenceIfNeeded(cur);
                    cur.setEventFlags(symbolReader.getEventFlags());
                    cur.setTime(time);
                    cur.setEventFlags(messageType.isSubscriptionRemove() ? EventFlag.REMOVE_SYMBOL.flag() : 0);
                    stats.updateIOReadRecordBytes(record.getId(), position - lastRecPosition);
                    stats.updateIOReadSubRecord();
                    updateMoreIOReadSubRecordStats();
                }
                lastRecPosition = position;
            }
        } catch (IOException | IllegalStateException e) {
            // IllegalStateException Happens when visiting of previous record was not properly finished.
            // IllegalStateException Happens when data schemes are incompatible or message is corrupted.
            dumpParseSubscriptionErrorReport(msg, e, buf, startBufLimit, historySubscriptionAdd, lastRecPosition);
            throw new CorruptedException(e);
        }
    }

    protected void readSymbol(BufferedInput msg) throws IOException {
        symbolReader.readSymbol(msg, symbolResolver);
    }

    protected int readRecordId(BufferedInput msg) throws IOException {
        return msg.readCompactInt();
    }

    // overridden in file replacedysis tool
    protected BinaryRecordDesc wrapRecordDesc(BinaryRecordDesc desc) {
        if (fieldReplacers == null || fieldReplacers.isEmpty())
            return desc;
        List<Consumer<RecordCursor>> consumers = new ArrayList<>();
        for (FieldReplacer fieldReplacer : fieldReplacers) {
            Consumer<RecordCursor> replacer = fieldReplacer.createFieldReplacer(desc.getRecord());
            if (replacer != null)
                consumers.add(replacer);
        }
        if (consumers.isEmpty())
            return desc;
        // noinspection unchecked,SuspiciousToArrayCall
        Consumer<RecordCursor>[] consumersArray = (Consumer<RecordCursor>[]) consumers.toArray(new Consumer[consumers.size()]);
        return new BinaryRecordDesc(desc) {

            @Override
            protected void readFields(BufferedInput msg, RecordCursor cur, int nDesc) throws IOException {
                super.readFields(msg, cur, nDesc);
                for (Consumer<RecordCursor> replacer : consumersArray) replacer.accept(cur);
            }
        };
    }

    protected long readSubscriptionTime(BufferedInput msg) throws IOException {
        return msg.readCompactLong();
    }

    @Nonnull
    protected BinaryRecordDesc[] newRecordMap(BinaryRecordDesc[] recordMap, int id) {
        int len = recordMap == null ? 0 : recordMap.length;
        int newLen = Math.max(Math.max(10, id + 1), len * 3 / 2);
        BinaryRecordDesc[] newRecordMap = new BinaryRecordDesc[newLen];
        if (recordMap != null)
            System.arraycopy(recordMap, 0, newRecordMap, 0, len);
        return newRecordMap;
    }

    // Note, this protected method is overridden in MDS to correct records
    protected void remapRecord(int id, BinaryRecordDesc rr) {
        if (recordMap == null || id >= recordMap.length)
            recordMap = newRecordMap(recordMap, id);
        recordMap[id] = rr;
    }

    private BinaryRecordDesc getRecordDesc(int id) {
        BinaryRecordDesc[] recordMap = this.recordMap;
        return recordMap != null && id >= 0 && id < recordMap.length ? recordMap[id] : null;
    }

    private BinaryRecordDesc getOrCreateRecordDesc(int id) {
        BinaryRecordDesc rr = getRecordDesc(id);
        if (rr != null)
            return rr;
        if (!isSchemeKnown())
            return null;
        // No description, but scheme is known
        if (id >= 0 && id < scheme.getRecordCount()) {
            DataRecord record = scheme.getRecord(id);
            try {
                rr = wrapRecordDesc(new BinaryRecordDesc(record, false, BinaryRecordDesc.DIR_READ, true));
                remapRecord(id, rr);
                return rr;
            } catch (BinaryRecordDesc.InvalidDescException e) {
                QDLog.log.info("Record #" + id + " '" + record.getName() + "' " + "cannot be parsed: " + e.getMessage());
            }
        }
        return null;
    }

    // ------------------------ message part support ------------------------
    private static clreplaced ParreplacedionedMessage {

        final long id;

        final long totalLength;

        final ChunkedInput in;

        long remaining;

        ParreplacedionedMessage(long id, long totalLength) {
            this.id = id;
            this.totalLength = totalLength;
            this.in = new ChunkedInput();
            this.remaining = totalLength;
        }
    }

    // todo: optionally: reuse parreplacedioned messages to avoid garbage
    private static final IndexerFunction.LongKey<ParreplacedionedMessage> PARreplacedIONED_MESSAGE_BY_ID_INDEXER = message -> message.id;

    private void parseMessagePart(BufferedInput msg, MessageConsumer consumer, int payloadLength) throws IOException, CorruptedException {
        // 
        // First message part payload consists of these elements:
        // 
        // <id> <totalLength> <messageType> <messagePayloadPart>
        // |                                                |
        // +------------------------------------------------+
        // messagePart
        // 
        // Subsequent parts do not have any structure inside messagePart
        // 
        ParreplacedionedMessage parreplacedionedMessage;
        int partLength;
        try {
            long idPosition = msg.totalPosition();
            long id = msg.readCompactLong();
            partLength = payloadLength - (int) (msg.totalPosition() - idPosition);
            if (parreplacedionedMessages == null)
                parreplacedionedMessages = IndexedSet.createLong(PARreplacedIONED_MESSAGE_BY_ID_INDEXER);
            parreplacedionedMessage = parreplacedionedMessages.getByKey(id);
            if (parreplacedionedMessage == null) {
                if (partLength == 0)
                    return;
                long lengthPosition = msg.totalPosition();
                long totalLength = msg.readCompactLong();
                if (totalLength < 0 || totalLength > Integer.MAX_VALUE) {
                    dumpParseMessageErrorReport(msg, "Invalid totalLength=" + totalLength, null, -1);
                    throw new CorruptedException();
                }
                int lengthLen = (int) (msg.totalPosition() - lengthPosition);
                int typeId = parseMessageType(msg);
                if (typeId < 0)
                    // reported error in typeId
                    return;
                if (typeId == MESSAGE_PART) {
                    dumpParseMessageErrorReport(msg, "Cannot have MESSAGE_PART inside MESSAGE_PART", null, -1);
                    throw new CorruptedException();
                }
                // undo reading length and type, will need to read them into message part
                msg.seek(lengthPosition);
                parreplacedionedMessages.add(parreplacedionedMessage = new ParreplacedionedMessage(id, totalLength + lengthLen));
            }
            if (partLength == 0) {
                // cancellation of an earlier partially sent message
                parreplacedionedMessages.removeKey(id);
                return;
            }
        } catch (EOFException e) {
            dumpParseMessageErrorReport(msg, e.getMessage(), e, -1);
            throw new CorruptedException(e);
        }
        int readLen = (int) Math.min(partLength, parreplacedionedMessage.remaining);
        parreplacedionedMessage.remaining -= readLen;
        while (readLen > 0) {
            Chunk chunk = ChunkPool.DEFAULT.getChunk(this);
            int chunkReadLen = Math.min(chunk.getLength(), readLen);
            msg.readFully(chunk.getBytes(), chunk.getOffset(), chunkReadLen);
            chunk.setLength(chunkReadLen, this);
            readLen -= chunkReadLen;
            parreplacedionedMessage.in.addToInput(chunk, this);
        }
        if (parreplacedionedMessage.remaining <= 0) {
            try {
                parseImpl(parreplacedionedMessage.in, consumer);
            } finally {
                parreplacedionedMessages.remove(parreplacedionedMessage);
                // recycle all remaining unparsed chunks
                parreplacedionedMessage.in.clear();
            }
        }
    }

    // ------------------------ parsing error reporting ------------------------
    private static final char[] HEX = "0123456789ABCDEF".toCharArray();

    private static final int DUMP_LAST_RECORDS = 10;

    // in must be marked
    private void dumpParseHeaderErrorReport(BufferedInput in, String message) {
        StringBuilder sb = new StringBuilder();
        sb.append("Corrupted QTP byte stream: ").append(message);
        long lastPosition = in.totalPosition();
        in.reset();
        // reset and mark again to resync if needed
        in.mark();
        int cnt = (int) (lastPosition - in.totalPosition());
        appendBytes(sb, in, cnt, -1);
        QDLog.log.error(sb.toString());
    }

    private void dumpParseMessageErrorReport(BufferedInput msg, String message, Exception e, long lastRecPosition) {
        StringBuilder sb = new StringBuilder();
        appendMessageErrorHead(sb, message);
        appendMessageErrorTail(msg, sb, lastRecPosition);
        QDLog.log.error(sb.toString(), e);
    }

    private void dumpParseDataErrorReport(BufferedInput msg, Exception e, RecordBuffer buf, long startBufLimit, long lastRecPosition) {
        StringBuilder sb = new StringBuilder();
        appendMessageErrorHead(sb, e.getMessage());
        sb.append("\n++> === Last parsed data ===");
        // dump at most 10 last data records (RecordBuffer.next() advances but does not remove records)
        RecordBuffer rb = prepareToDumpLastRecords(buf);
        RecordCursor cur;
        while ((cur = rb.next()) != null) {
            sb.append("\n++> ");
            DataRecord record = cur.getRecord();
            sb.append(record.getName());
            String symbol = scheme.getCodec().decode(cur.getCipher(), cur.getSymbol());
            sb.append('\t').append(symbol);
            for (int i = 0; i < record.getIntFieldCount(); i++) try {
                sb.append('\t').append(record.getIntField(i).getString(cur));
            } catch (Throwable ignored) {
            }
            for (int i = 0; i < record.getObjFieldCount(); i++) try {
                sb.append('\t').append(record.getObjField(i).getString(cur));
            } catch (Throwable ignored) {
            }
        }
        appendLastSymbol(sb);
        appendMessageErrorTail(msg, sb, lastRecPosition);
        QDLog.log.error(sb.toString(), e);
        recoverBuffer(buf, startBufLimit);
    }

    private void dumpParseSubscriptionErrorReport(BufferedInput msg, Exception e, RecordBuffer buf, long startBufLimit, boolean history, long lastRecPosition) {
        StringBuilder sb = new StringBuilder();
        appendMessageErrorHead(sb, e.getMessage());
        sb.append("\n++> === Last parsed subscription ===");
        // dump at most 10 last data records (RecordBuffer.next() advances but does not remove records)
        RecordBuffer rb = prepareToDumpLastRecords(buf);
        RecordCursor cur;
        while ((cur = rb.next()) != null) {
            sb.append("\n++> ");
            sb.append(cur.getRecord().getName());
            sb.append('\t').append(cur.getDecodedSymbol());
            if (history)
                sb.append('\t').append(cur.getTime());
        }
        appendLastSymbol(sb);
        appendMessageErrorTail(msg, sb, lastRecPosition);
        QDLog.log.error(sb.toString(), e);
        recoverBuffer(buf, startBufLimit);
    }

    private static void appendMessageErrorHead(StringBuilder sb, String message) {
        sb.append("Corrupted QTP message: ").append(message);
    }

    private void appendLastSymbol(StringBuilder sb) {
        sb.append("\n++> Last symbol: (").append(symbolReader.getCipher());
        sb.append(", ").append(symbolReader.getSymbol());
        sb.append(") = ").append(scheme.getCodec().decode(symbolReader.getCipher(), symbolReader.getSymbol()));
    }

    private void appendMessageErrorTail(BufferedInput msg, StringBuilder sb, long lastRecPosition) {
        try {
            // dump at most 160 bytes
            long curPosition = msg.totalPosition();
            msg.reset();
            int parsedBytes = (int) (curPosition - msg.totalPosition());
            int skipBytes = Math.max(0, parsedBytes - 160);
            msg.skip(skipBytes);
            int cnt = parsedBytes - skipBytes;
            appendBytes(sb, msg, cnt, lastRecPosition);
        } catch (IOException e) {
            // cannot happen
            throw new replacedertionError(e);
        }
    }

    private void appendBytes(StringBuilder sb, BufferedInput in, int cnt, long lastRecPosition) {
        try {
            sb.append("\n++> === Last parsed bytes ===");
            if (lastRecPosition >= 0)
                sb.append("\n++> '|' shows where to last record was successfully parsed");
            sb.append("\n++> '!' shows where to last byte was read");
            // show 16-32 extra bytes if available
            long startShowPosition = in.totalPosition();
            int wantToShowCnt = cnt + 32 - cnt % 16;
            int canShowCnt = Math.min(wantToShowCnt, in.available());
            char[] asciiBuf = new char[16];
            for (int i = 0; i < wantToShowCnt; i++) {
                if ((i & 0xf) == 0)
                    sb.append(String.format(Locale.US, "\n++> 0x%08x: ", startShowPosition + i));
                int b = i < canShowCnt ? in.read() : -1;
                sb.append(i == cnt ? '!' : startShowPosition + i == lastRecPosition ? '|' : ' ');
                if (b < 0)
                    sb.append("  ");
                else
                    sb.append(HEX[(b >> 4) & 15]).append(HEX[b & 15]);
                asciiBuf[i & 0xf] = b < 0 ? ' ' : b >= ' ' ? (char) b : '.';
                if ((i & 0xf) == 0xf)
                    sb.append("   ").append(asciiBuf);
            }
            sb.append("\n++> === END ===");
        } catch (IOException e) {
            // cannot happen
            throw new replacedertionError(e);
        }
    }

    private RecordBuffer prepareToDumpLastRecords(RecordBuffer buf) {
        for (int i = 0; i < buf.size() - DUMP_LAST_RECORDS; i++) buf.next();
        return buf;
    }

    private void recoverBuffer(RecordBuffer buf, long startBufLimit) {
        buf.rewind();
        buf.setLimit(startBufLimit);
    }

    // ------------------------ symbol receiver implementation ------------------------
    /**
     * This exception is thrown to indicate that stream is corrupted.
     */
    protected static clreplaced CorruptedException extends Exception {

        private static final long serialVersionUID = 0;

        public CorruptedException() {
        }

        public CorruptedException(Throwable cause) {
            super(cause);
        }
    }

    /**
     * This exception is thrown to indicate that message is corrupted.
     */
    protected static clreplaced CorruptedMessageException extends CorruptedException {

        private static final long serialVersionUID = 0;

        protected final int messageTypeId;

        public CorruptedMessageException(Throwable cause, int messageTypeId) {
            super(cause);
            this.messageTypeId = messageTypeId;
        }
    }

    // ------------------------ hooks for statistical replacedysis  ------------------------
    protected void doBeforeMessageLength(BufferedInput in) {
    }

    protected void doAfterMessageType(BufferedInput in) {
    }

    protected void doAfterMessageBody(BufferedInput in, int messageType) {
    }
}

18 Source : IndexerSerializationTest.java
with Mozilla Public License 2.0
from devexperts

private static void serializeObjects(ObjectOutputStream oos) throws IOException {
    IndexedSet<String, String> set = new IndexedSet<>();
    set.add("xxx");
    set.add("yyy");
    oos.writeObject(set);
}

18 Source : Schedule.java
with Mozilla Public License 2.0
from devexperts

/**
 * <b>Schedule</b> clreplaced provides API to retrieve and explore trading schedules of different exchanges
 * and different clreplacedes of financial instruments. Each instance of schedule covers separate trading schedule
 * of some clreplaced of instruments, i.e. NYSE stock trading schedule or CME corn futures trading schedule.
 * Each schedule splits entire time scale into separate {@link Day days} that are aligned to the specific
 * trading hours of covered trading schedule.
 */
public final clreplaced Schedule {

    private static final Logging log = Logging.getLogging(Schedule.clreplaced);

    private static final String CACHE_LIMIT_PROPERTY = "com.dxfeed.schedule.cache";

    private static final String DOWNLOAD_PROPERTY = "com.dxfeed.schedule.download";

    private static final String DOWNLOAD_AUTO = "http://downloads.dxfeed.com/schedule/schedule.zip,1d";

    // ========== Instance Lookup ==========
    /**
     * Returns default schedule instance for specified instrument profile.
     *
     * @param profile instrument profile those schedule is requested
     * @return default schedule instance for specified instrument profile
     */
    public static Schedule getInstance(InstrumentProfile profile) {
        return getInstance(profile.getTradingHours());
    }

    /**
     * Returns default schedule instance for specified schedule definition.
     *
     * @param scheduleDefinition schedule definition of requested schedule
     * @return default schedule instance for specified schedule definition
     */
    public static Schedule getInstance(String scheduleDefinition) {
        int open = scheduleDefinition.indexOf('(', 0);
        int close = scheduleDefinition.indexOf(')', open + 1);
        if (open < 0 || close < 0)
            throw new IllegalArgumentException("broken schedule " + scheduleDefinition);
        int start = Math.max(scheduleDefinition.lastIndexOf(')', open - 1), scheduleDefinition.lastIndexOf(';', open - 1)) + 1;
        return getSchedule(scheduleDefinition.substring(start, close + 1));
    }

    /**
     * Returns schedule instance for specified instrument profile and trading venue.
     *
     * @param profile instrument profile those schedule is requested
     * @param venue trading venue those schedule is requested
     * @return schedule instance for specified instrument profile and trading venue
     */
    public static Schedule getInstance(InstrumentProfile profile, String venue) {
        String hours = profile.getTradingHours();
        for (int open = -1; (open = hours.indexOf('(', open + 1)) >= 0; ) {
            int close = hours.indexOf(')', open + 1);
            if (close < 0)
                throw new IllegalArgumentException("broken schedule " + hours);
            int start = Math.max(hours.lastIndexOf(')', open - 1), hours.lastIndexOf(';', open - 1)) + 1;
            if (open - start == venue.length() && hours.regionMatches(start, venue, 0, venue.length()))
                return getSchedule(hours.substring(start, close + 1));
        }
        throw new NoSuchElementException("could not find schedule for trading venue " + venue + " in " + hours);
    }

    /**
     * Returns trading venues for specified instrument profile.
     *
     * @param profile instrument profile those trading venues are requested
     * @return trading venues for specified instrument profile
     */
    public static List<String> getTradingVenues(InstrumentProfile profile) {
        List<String> venues = new ArrayList<>();
        Matcher m = VENUE_PATTERN.matcher(profile.getTradingHours());
        while (m.find()) venues.add(m.group(1));
        return venues;
    }

    private static final Pattern VENUE_PATTERN = Pattern.compile("(\\w*)\\([^()]*\\)");

    // ========== Updating Defaults ==========
    private static String downloadURL;

    private static long downloadPeriod;

    private static Thread downloadThread;

    /**
     * Downloads defaults using specified download config and optionally start periodic download.
     * The specified config can be one of the following:<ul>
     * <li>"" or null - stop periodic download
     * <li>URL   - download once from specified URL and stop periodic download
     * <li>URL,period   - start periodic download from specified URL
     * <li>"auto"   - start periodic download from default location
     * </ul>
     *
     * @param downloadConfig download config
     */
    public static void downloadDefaults(String downloadConfig) {
        if (downloadConfig == null)
            downloadConfig = "";
        downloadConfig = downloadConfig.trim();
        if (downloadConfig.equalsIgnoreCase("auto"))
            downloadConfig = DOWNLOAD_AUTO;
        String[] config = downloadConfig.split(",");
        synchronized (Schedule.clreplaced) {
            downloadURL = config[0].trim().length() > 0 ? config[0].trim() : null;
            downloadPeriod = config.length >= 2 ? Math.max(TimePeriod.valueOf(config[1]).getTime(), 0) : 0;
            LockSupport.unpark(downloadThread);
            if (downloadPeriod > 0) {
                downloadThread = new Thread(Schedule::runDownload, "ScheduleDownloader");
                downloadThread.setDaemon(true);
                downloadThread.start();
            } else
                downloadThread = null;
        }
        doDownload();
    }

    private static void runDownload() {
        long next = 0;
        while (true) {
            try {
                long time = System.currentTimeMillis();
                long newNext;
                synchronized (Schedule.clreplaced) {
                    if (downloadURL == null || downloadPeriod == 0 || downloadThread != Thread.currentThread())
                        return;
                    newNext = time + (long) (downloadPeriod * (0.95 + 0.1 * Math.random()));
                }
                if (next == 0)
                    next = newNext;
                if (time < next) {
                    LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(next - time));
                    continue;
                }
                next = newNext;
                doDownload();
            } catch (Throwable t) {
                log.error("Unexpected error", t);
            }
        }
    }

    private static void doDownload() {
        // Atomic read.
        String url = downloadURL;
        if (url == null)
            return;
        byte[] data;
        try {
            data = URLInputStream.readBytes(url);
        } catch (Throwable t) {
            log.error("Failed to download schedule defaults from " + LogUtil.hideCredentials(url), t);
            return;
        }
        try (InputStream in = StreamCompression.detectCompressionByHeaderAndDecompress(new ByteArrayInput(data))) {
            String result = setDefaults(in);
            log.info("Downloaded schedule defaults from " + LogUtil.hideCredentials(url) + " - they are " + result);
        } catch (Throwable t) {
            log.error("Unexpected error", t);
        }
    }

    /**
     * Sets shared defaults that are used by individual schedule instances.
     *
     * @param data content of default data
     * @throws IOException  If an I/O error occurs
     */
    public static void setDefaults(byte[] data) throws IOException {
        try (InputStream in = StreamCompression.detectCompressionByHeaderAndDecompress(new ByteArrayInput(data))) {
            setDefaults(in);
        }
    }

    // ========== Instance API ==========
    /**
     * Returns session that contains specified time.
     * This method will throw {@link IllegalArgumentException} if specified time
     * falls outside of valid date range from 0001-01-02 to 9999-12-30.
     *
     * @param time the time to search for
     * @return session that contains specified time
     * @throws IllegalArgumentException if specified time falls outside of valid date range
     */
    public Session getSessionByTime(long time) {
        return getDayByTime(time).getSessionByTime(time);
    }

    /**
     * Returns day that contains specified time.
     * This method will throw {@link IllegalArgumentException} if specified time
     * falls outside of valid date range from 0001-01-02 to 9999-12-30.
     *
     * @param time the time to search for
     * @return day that contains specified time
     * @throws IllegalArgumentException if specified time falls outside of valid date range
     */
    public Day getDayByTime(long time) {
        checkRange("time", time, MIN_TIME, MAX_TIME);
        usageCounter.incrementAndGet();
        // Calculate approximate dayId and locate proper Day.
        Day d = getDay((int) MathUtil.div(time + rawOffset - dayOffset, DAY_LENGTH));
        while (d.getStartTime() > time) d = getDay(d.getDayId() - 1);
        while (d.getEndTime() <= time) d = getDay(d.getDayId() + 1);
        return d;
    }

    /**
     * Returns day for specified day identifier.
     * This method will throw {@link IllegalArgumentException} if specified day identifier
     * falls outside of valid date range from 0001-01-02 to 9999-12-30.
     *
     * @param dayId day identifier to search for
     * @return day for specified day identifier
     * @throws IllegalArgumentException if specified day identifier falls outside of valid date range
     * @see Day#getDayId()
     */
    public Day getDayById(int dayId) {
        checkRange("dayId", dayId, MIN_ID, MAX_ID);
        usageCounter.incrementAndGet();
        return getDay(dayId);
    }

    /**
     * Returns day for specified year, month and day numbers.
     * Year, month, and day numbers shall be decimally packed in the following way:
     * <pre>YearMonthDay = year * 10000 + month * 100 + day</pre>
     * For example, September 28, 1977 has value 19770928.
     * <p>
     * If specified day does not exist then this method returns day with
     * the lowest valid YearMonthDay that is greater than specified one.
     * This method will throw {@link IllegalArgumentException} if specified year, month and day numbers
     * fall outside of valid date range from 0001-01-02 to 9999-12-30.
     *
     * @param yearMonthDay year, month and day numbers to search for
     * @return day for specified year, month and day numbers
     * @throws IllegalArgumentException if specified year, month and day numbers fall outside of valid date range
     * @see Day#getYearMonthDay()
     */
    @SuppressWarnings("UnusedDeclaration")
    public Day getDayByYearMonthDay(int yearMonthDay) {
        checkRange("yearMonthDay", yearMonthDay, MIN_YMD, MAX_YMD);
        usageCounter.incrementAndGet();
        // Try direct approach - works for fully correct cached days.
        Day d = ymdCache.getByKey(yearMonthDay);
        if (d != null) {
            d.usageCounter = usageCounter.get();
            return d;
        }
        // Disreplacedemble and normalize (roughly) date.
        int year = yearMonthDay / 10000;
        int month = yearMonthDay / 100 % 100;
        int day = yearMonthDay % 100;
        if (day > 31) {
            month++;
            day = 1;
        } else if (day < 1)
            day = 1;
        if (month > 12) {
            year++;
            month = day = 1;
        } else if (month < 1)
            month = day = 1;
        // Try corrected date - works for partially correct cached days.
        d = ymdCache.getByKey(year * 10000 + month * 100 + day);
        if (d != null) {
            d.usageCounter = usageCounter.get();
            return d;
        }
        // Calculate approximate dayId and locate proper Day.
        d = getDay(DayUtil.getDayIdByYearMonthDay(year, month, day));
        while (d.getYearMonthDay() > yearMonthDay) d = getDay(d.getDayId() - 1);
        while (d.getYearMonthDay() < yearMonthDay) d = getDay(d.getDayId() + 1);
        return d;
    }

    /**
     * Returns session that is nearest to the specified time and that is accepted by specified filter.
     * This method will throw {@link IllegalArgumentException} if specified time
     * falls outside of valid date range from 0001-01-02 to 9999-12-30.
     * If no sessions acceptable by specified filter are found within one year this method will throw {@link NoSuchElementException}.
     * <p>
     * To find nearest trading session of any type use this code:
     * <pre>session = schedule.getNearestSessionByTime(time, SessionFilter.TRADING);</pre>
     * To find nearest regular trading session use this code:
     * <pre>session = schedule.getNearestSessionByTime(time, SessionFilter.REGULAR);</pre>
     *
     * @param time the time to search for
     * @param filter the filter to test sessions
     * @return session that is nearest to the specified time and that is accepted by specified filter
     * @throws IllegalArgumentException if specified time falls outside of valid date range
     * @throws NoSuchElementException if no such day was found within one year
     */
    public Session getNearestSessionByTime(long time, SessionFilter filter) {
        Session session = findNearestSessionByTime(time, filter);
        if (session != null)
            return session;
        throw new NoSuchElementException("could not find session nearest to " + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(time) + " for " + filter);
    }

    /**
     * Returns session that is nearest to the specified time and that is accepted by specified filter.
     * This method will throw {@link IllegalArgumentException} if specified time
     * falls outside of valid date range from 0001-01-02 to 9999-12-30.
     * If no sessions acceptable by specified filter are found within one year this method will return <b>null</b>.
     * <p>
     * To find nearest trading session of any type use this code:
     * <pre>session = schedule.findNearestSessionByTime(time, SessionFilter.TRADING);</pre>
     * To find nearest regular trading session use this code:
     * <pre>session = schedule.findNearestSessionByTime(time, SessionFilter.REGULAR);</pre>
     *
     * @param time the time to search for
     * @param filter the filter to test sessions
     * @return session that is nearest to the specified time and that is accepted by specified filter
     * @throws IllegalArgumentException if specified time falls outside of valid date range
     */
    public Session findNearestSessionByTime(long time, SessionFilter filter) {
        Session session = getSessionByTime(time);
        if (filter.accept(session))
            return session;
        Session prev = session.findPrevSession(filter);
        Session next = session.findNextSession(filter);
        if (prev == null)
            return next;
        if (next == null)
            return prev;
        return time - prev.getEndTime() < next.getStartTime() - time ? prev : next;
    }

    /**
     * Returns name of this schedule.
     *
     * @return name of this schedule
     */
    public String getName() {
        return name;
    }

    /**
     * Returns time zone in which this schedule is defined.
     *
     * @return time zone in which this schedule is defined
     */
    public TimeZone getTimeZone() {
        return (TimeZone) calendar.getTimeZone().clone();
    }

    public String toString() {
        return "Schedule(" + name + ")";
    }

    // ========== Implementation Details ==========
    private static final int CACHE_LIMIT = SystemProperties.getIntProperty(CACHE_LIMIT_PROPERTY, 25000, 100, 100000);

    private static final int CACHE_RETAIN = CACHE_LIMIT - CACHE_LIMIT / 4;

    private static final long DAY_LENGTH = 24 * 3600 * 1000;

    private static final int MIN_YMD = 10103;

    private static final int MAX_YMD = 99991229;

    private static final int MIN_ID = DayUtil.getDayIdByYearMonthDay(MIN_YMD / 10000, MIN_YMD / 100 % 100, MIN_YMD % 100);

    private static final int MAX_ID = DayUtil.getDayIdByYearMonthDay(MAX_YMD / 10000, MAX_YMD / 100 % 100, MAX_YMD % 100);

    private static final long MIN_TIME = MIN_ID * DAY_LENGTH;

    private static final long MAX_TIME = MAX_ID * DAY_LENGTH + DAY_LENGTH;

    /**
     * Checks date range and throws exception with proper error message.
     */
    private static void checkRange(String name, long value, long min, long max) {
        if (value < min || value > max)
            throw new IllegalArgumentException("specified " + name + " falls outside of valid date range (from 0001-01-02 to 9999-12-30): " + value);
    }

    /**
     * Returns ordinal day number in the week starting with <b>1=Monday</b> and ending with <b>7=Sunday</b>.
     */
    static int dayOfWeek(int dayId) {
        // 1=Monday, 7=Sunday, protected from overflows and negative numbers
        return (dayId % 7 + 10) % 7 + 1;
    }

    private static final Comparator<Day> USAGE_COMPARATOR = (d1, d2) -> Long.compare(d1.usageCounter, d2.usageCounter);

    private static final clreplaced TimeDef {

        final int day;

        final int hour;

        final int minute;

        final int second;

        TimeDef(TimeDef source, int dayShift) {
            day = source.day + dayShift;
            hour = source.hour;
            minute = source.minute;
            second = source.second;
        }

        // Expects "[+-]*hhmm(ss)?"
        TimeDef(String scheduleDefinition, String def) {
            int d = 0;
            int i = 0;
            for (; i < def.length(); i++) if (def.charAt(i) == '+')
                d++;
            else if (def.charAt(i) == '-')
                d--;
            else
                break;
            day = d;
            if (def.length() != i + 4 && def.length() != i + 6)
                throw new IllegalArgumentException("unmatched data in " + def + " in " + scheduleDefinition);
            hour = parse2(def, i);
            minute = parse2(def, i + 2);
            second = def.length() > i + 4 ? parse2(def, i + 4) : 0;
        }

        private int parse2(String s, int pos) {
            return (s.charAt(pos) - '0') * 10 + (s.charAt(pos + 1) - '0');
        }

        long offset() {
            return day * DAY_LENGTH + hour * 3600000 + minute * 60000 + second * 1000;
        }

        long get(Calendar calendar, long localTime) {
            calendar.setTimeInMillis(localTime);
            calendar.add(Calendar.DAY_OF_YEAR, day);
            calendar.set(Calendar.HOUR_OF_DAY, hour);
            calendar.set(Calendar.MINUTE, minute);
            calendar.set(Calendar.SECOND, second);
            calendar.set(Calendar.MILLISECOND, 0);
            return calendar.getTime().getTime();
        }

        public int compareTo(TimeDef other) {
            if (day != other.day)
                return day - other.day;
            if (hour != other.hour)
                return hour - other.hour;
            if (minute != other.minute)
                return minute - other.minute;
            return second - other.second;
        }

        public String toString() {
            return day + ":" + hour + ":" + minute + ":" + second;
        }
    }

    private static final clreplaced SessionDef {

        final SessionType type;

        final TimeDef start;

        final TimeDef end;

        SessionDef(SessionType type, TimeDef start, TimeDef end) {
            this.type = type;
            this.start = start;
            this.end = end;
        }

        Session create(Day day, Calendar calendar, long time, long startShift, long endShift) {
            long start = this.start.get(calendar, time) + startShift;
            long end = this.end.get(calendar, time) + endShift;
            if (start > end)
                throw new IllegalArgumentException("start=" + start + " > end=" + end);
            return new Session(day, type, start, end);
        }

        boolean isTooShort(Calendar calendar, long time, long earlyClose) {
            return start.get(calendar, time) >= end.get(calendar, time) - earlyClose;
        }

        public String toString() {
            return type + "(" + start + "," + end + ")";
        }
    }

    private static final clreplaced DayDef {

        private static final Pattern GROUP_SEARCH = Pattern.compile("([a-zA-Z_]*)([^a-zA-Z_]+)");

        private static final Pattern MINUTE_SEARCH = Pattern.compile("([+-]*[0-9]{4})/?([+-]*[0-9]{4})");

        private static final Pattern MINUTE_MATCH = Pattern.compile("(" + MINUTE_SEARCH.pattern() + ")*");

        private static final Pattern SECOND_SEARCH = Pattern.compile("([+-]*[0-9]{6})/?([+-]*[0-9]{6})");

        private static final Pattern SECOND_MATCH = Pattern.compile("(" + SECOND_SEARCH.pattern() + ")*");

        final TimeDef dayStart;

        final TimeDef dayEnd;

        final TimeDef resetTime;

        final SessionDef[] sessions;

        DayDef(String scheduleDefinition, TimeDef dayStart, TimeDef dayEnd, TimeDef resetTime, String def) {
            List<SessionDef> s = new ArrayList<>();
            Matcher m = GROUP_SEARCH.matcher(def);
            int matched = 0;
            while (m.find()) {
                if (matched == m.start())
                    matched = m.end();
                String key = m.group(1);
                if (key.equalsIgnoreCase("rt")) {
                    resetTime = new TimeDef(scheduleDefinition, m.group(2));
                } else {
                    SessionType type = key.equalsIgnoreCase("d") ? SessionType.NO_TRADING : key.equalsIgnoreCase("p") ? SessionType.PRE_MARKET : key.equalsIgnoreCase("r") ? SessionType.REGULAR : key.equalsIgnoreCase("a") ? SessionType.AFTER_MARKET : key.equalsIgnoreCase("") ? SessionType.REGULAR : null;
                    if (type == null)
                        throw new IllegalArgumentException("unknown session type in " + def + " in " + scheduleDefinition);
                    Matcher mm = MINUTE_MATCH.matcher(m.group(2)).matches() ? MINUTE_SEARCH.matcher(m.group(2)) : SECOND_MATCH.matcher(m.group(2)).matches() ? SECOND_SEARCH.matcher(m.group(2)) : null;
                    if (mm == null)
                        throw new IllegalArgumentException("unmatched data in " + def + " in " + scheduleDefinition);
                    while (mm.find()) {
                        TimeDef td1 = new TimeDef(scheduleDefinition, mm.group(1));
                        TimeDef td2 = new TimeDef(scheduleDefinition, mm.group(2));
                        if (type == SessionType.NO_TRADING) {
                            dayStart = td1;
                            dayEnd = td2;
                        } else
                            s.add(new SessionDef(type, td1, td2));
                        type = SessionType.REGULAR;
                    }
                }
            }
            if (matched != def.length())
                throw new IllegalArgumentException("unmatched data in " + def + " in " + scheduleDefinition);
            if (resetTime == null)
                resetTime = dayStart;
            if (resetTime.compareTo(dayStart) < 0 || resetTime.compareTo(dayEnd) >= 0)
                throw new IllegalArgumentException("illegal reset time " + resetTime + " for " + dayStart + " and " + dayEnd + " in " + scheduleDefinition);
            this.dayStart = dayStart;
            this.dayEnd = dayEnd;
            this.resetTime = resetTime;
            if (s.isEmpty()) {
                s.add(new SessionDef(SessionType.NO_TRADING, dayStart, dayEnd));
            } else {
                for (// Note: size() changes dynamically in fillGap()
                int i = 1; // Note: size() changes dynamically in fillGap()
                i < s.size(); // Note: size() changes dynamically in fillGap()
                i++) fillGap(scheduleDefinition, s, i, s.get(i - 1).end, s.get(i).start);
                fillGap(scheduleDefinition, s, 0, dayStart, s.get(0).start);
                fillGap(scheduleDefinition, s, s.size(), s.get(s.size() - 1).end, dayEnd);
            }
            sessions = s.toArray(new SessionDef[s.size()]);
        }

        private static void fillGap(String scheduleDefinition, List<SessionDef> s, int index, TimeDef start, TimeDef end) {
            if (start.compareTo(end) > 0)
                throw new IllegalArgumentException("illegal session order at " + index + " for " + start + " and " + end + " in " + scheduleDefinition);
            if (start.compareTo(end) < 0)
                s.add(index, new SessionDef(SessionType.NO_TRADING, start, end));
        }

        boolean isTrading() {
            for (SessionDef session : sessions) {
                if (session.type != SessionType.NO_TRADING)
                    return true;
            }
            return false;
        }
    }

    // Generic strategy pattern is "name[params]", where name is alpha string and params shall start with non-alpha.
    private static final Pattern STRATEGY_PATTERN = Pattern.compile("([a-zA-Z_]+)([^a-zA-Z_].*)?");

    private static final Pattern SDS_EC_PATTERN = Pattern.compile("ec([0-9]{4})");

    private static final Pattern HDS_JNTD_PATTERN = Pattern.compile("jntd([0-9])");

    private static Matcher getStrategy(String def, Map<String, String> props, String key, String name, Pattern pattern) {
        String value = props.get(key);
        if (value == null || value.isEmpty())
            return null;
        Matcher m = STRATEGY_PATTERN.matcher(value);
        if (m.matches()) {
            if (!m.group(1).equals(name)) {
                // In case of unknown strategy - log it and ignore it as if none were specified. For compatibility.
                // Once several different strategies are supported - this warning shall be moved elsewhere
                // or method signature shall be changed to avoid misreporting of "unknown" strategy.
                log.warn("Unknown " + key + " strategy " + m.group(1) + " for " + def);
                return null;
            }
            Matcher result = pattern.matcher(value);
            if (result.matches())
                return result;
        // If strategy does not match specified pattern - fall through to throw exception below.
        }
        // If property does not match neither generic strategy pattern nor specific strategy pattern - throw exception.
        throw new IllegalArgumentException("broken " + key + " strategy for " + def);
    }

    final String def;

    private String name;

    private Calendar calendar;

    private long rawOffset;

    private long dayOffset;

    private LongHashSet holidays;

    private LongHashSet shortdays;

    private long earlyClose;

    private int joinNextTradingDay;

    private DayDef[] weekDays;

    private LongHashMap<DayDef> specialDays;

    private final Object lock = new Object();

    private final IndexedSet<Integer, Day> idCache = IndexedSet.createInt(Day::getDayId);

    private final IndexedSet<Integer, Day> ymdCache = IndexedSet.createInt(Day::getYearMonthDay);

    private final AtomicLong creationCounter = new AtomicLong();

    private final AtomicLong usageCounter = new AtomicLong();

    private Schedule(String def) {
        if (!VENUE_PATTERN.matcher(def).matches())
            throw new IllegalArgumentException("broken schedule " + def);
        this.def = def;
        init();
    }

    private void init() {
        // Atomic volatile read.
        Defaults defaults = DEFAULTS;
        String venue = def.substring(0, def.indexOf('('));
        Map<String, String> props = new HashMap<>();
        if (defaults.venues.containsKey(venue))
            props.putAll(defaults.venues.get(venue));
        props.putAll(readProps(def.substring(def.indexOf('(') + 1, def.length() - 1)));
        String name = props.get("name");
        if (name == null)
            name = venue;
        String tz = props.get("tz");
        if (tz == null || tz.isEmpty())
            throw new IllegalArgumentException("missing time zone for " + def);
        TimeZone timeZone = TimeZone.getTimeZone(tz);
        LongHashSet holidays = readDays(defaults.holidays, props.get("hd"));
        LongHashSet shortdays = readDays(defaults.shortdays, props.get("sd"));
        Matcher ec = getStrategy(def, props, "sds", "ec", SDS_EC_PATTERN);
        long earlyClose = ec == null ? 0 : new TimeDef(def, ec.group(1)).offset();
        Matcher jntd = getStrategy(def, props, "hds", "jntd", HDS_JNTD_PATTERN);
        int joinNextTradingDay = jntd == null ? 0 : Integer.parseInt(jntd.group(1));
        String td = props.get("td");
        if (td == null)
            td = "12345";
        String de = props.get("de");
        if (de == null)
            de = "+0000";
        String rt = props.get("rt");
        TimeDef dayEnd = new TimeDef(def, de);
        TimeDef dayStart = new TimeDef(dayEnd, -1);
        TimeDef resetTime = rt == null ? null : new TimeDef(def, rt);
        DayDef[] weekDays = new DayDef[9];
        for (int i = 0; i <= 8; i++) {
            String s = props.get(String.valueOf(i));
            if (s != null)
                weekDays[i] = new DayDef(def, dayStart, dayEnd, resetTime, s);
        }
        if (weekDays[8] == null) {
            DayDef d0 = weekDays[0];
            weekDays[8] = d0 == null ? new DayDef(def, dayStart, dayEnd, resetTime, "") : new DayDef(def, d0.dayStart, d0.dayEnd, d0.resetTime, "");
        }
        for (int i = 1; i <= 7; i++) if (weekDays[i] == null)
            if ((weekDays[i] = weekDays[td.indexOf('0' + i) >= 0 ? 0 : 8]) == null)
                throw new IllegalArgumentException("incomplete schedule for " + def);
        LongHashMap<DayDef> specialDays = new LongHashMap<>();
        for (Map.Entry<String, String> e : props.entrySet()) if (e.getKey().length() == 8 && e.getKey().matches("\\d{8}"))
            specialDays.put(Integer.parseInt(e.getKey()), new DayDef(def, dayStart, dayEnd, resetTime, e.getValue()));
        if (specialDays.isEmpty())
            specialDays = EMPTY_MAP;
        synchronized (lock) {
            this.name = name;
            this.calendar = Calendar.getInstance(timeZone);
            this.rawOffset = calendar.getTimeZone().getRawOffset();
            this.dayOffset = dayStart.offset();
            this.holidays = holidays;
            this.shortdays = shortdays;
            this.earlyClose = earlyClose;
            this.joinNextTradingDay = joinNextTradingDay;
            this.weekDays = weekDays;
            this.specialDays = specialDays;
            idCache.clear();
            ymdCache.clear();
            checkEarlyClose();
        }
    }

    private void checkEarlyClose() {
        if (earlyClose == 0)
            return;
        for (PrimitiveIterator.OfLong it = shortdays.longIterator(); it.hasNext(); ) {
            long sd = it.nextLong();
            if (holidays.contains(sd))
                continue;
            if (isTooShortForEarlyClose((int) sd))
                return;
        }
    }

    private boolean isTooShortForEarlyClose(int yearMonthDay) {
        int dayId = DayUtil.getDayIdByYearMonthDay(yearMonthDay);
        DayDef dayDef = getDayDef(dayId, yearMonthDay);
        int lastRegular = getLastRegularSessionIndex(dayDef);
        if (lastRegular < dayDef.sessions.length) {
            SessionDef session = dayDef.sessions[lastRegular];
            long time = getTimeByDayId(dayId);
            if (session.isTooShort(calendar, time, earlyClose)) {
                log.warn("Last regular session of short day " + yearMonthDay + " in " + def + " is too short for early close strategy. Day won't be shortened.");
                return true;
            }
        }
        return false;
    }

    private Day getDay(int dayId) {
        Day day = idCache.getByKey(dayId);
        if (day == null)
            synchronized (lock) {
                day = idCache.getByKey(dayId);
                if (day == null) {
                    checkCacheSize();
                    day = createDay(dayId);
                    idCache.put(day);
                    ymdCache.put(day);
                }
            }
        day.usageCounter = usageCounter.get();
        return day;
    }

    // GuardedBy: lock
    private void checkCacheSize() {
        if (idCache.size() < CACHE_LIMIT)
            return;
        Day[] days = idCache.toArray(new Day[idCache.size()]);
        QuickSort.sort(days, USAGE_COMPARATOR);
        for (int i = days.length - CACHE_RETAIN; --i >= 0; ) {
            idCache.removeKey(days[i].getDayId());
            ymdCache.removeKey(days[i].getYearMonthDay());
        }
    }

    private long getTimeByDayId(int dayId) {
        return dayId * DAY_LENGTH - rawOffset + DAY_LENGTH / 2;
    }

    private void addTradingDaySessions(Day day, DayDef def, long time, boolean shortday, List<Session> sessions) {
        int lastRegular = (shortday && earlyClose != 0) ? getLastRegularSessionIndex(def) : def.sessions.length;
        if (lastRegular < def.sessions.length) {
            SessionDef session = def.sessions[lastRegular];
            if (session.isTooShort(calendar, time, earlyClose))
                // last regular session is too short for early close short day strategy - switch off it
                lastRegular = def.sessions.length;
        }
        long shift = -earlyClose;
        for (int i = 0; i < def.sessions.length; i++) {
            SessionDef session = def.sessions[i];
            if (i < lastRegular)
                sessions.add(session.create(day, calendar, time, 0, 0));
            else if (i == lastRegular)
                sessions.add(session.create(day, calendar, time, 0, shift));
            else if (i < def.sessions.length - 1)
                sessions.add(session.create(day, calendar, time, shift, shift));
            else
                sessions.add(session.create(day, calendar, time, shift, 0));
        }
        if (lastRegular == def.sessions.length - 1) {
            long endTime = sessions.get(sessions.size() - 1).getEndTime();
            sessions.add(new Session(day, SessionType.NO_TRADING, endTime, endTime - shift));
        }
    }

    private int getEarliestTradingHolidayOffset(int dayId, int maxOffset) {
        int earliestOffset = -1;
        for (int offset = 1; offset <= maxOffset; offset++) {
            int yearMonthDay = DayUtil.getYearMonthDayByDayId(dayId - offset);
            boolean isHoliday = holidays.contains(yearMonthDay);
            boolean isTrading = getDayDef(dayId - offset, yearMonthDay).isTrading();
            if (!isHoliday && isTrading) {
                // found true trading day - abort farther search and return best result
                return earliestOffset;
            }
            if (isHoliday && isTrading) {
                // found candidate - update best result and look for even better one
                earliestOffset = offset;
            }
        }
        return earliestOffset;
    }

    private int getNextTradingDayOffset(int dayId, int maxOffset) {
        for (int offset = 0; offset <= maxOffset; offset++) {
            int yearMonthDay = DayUtil.getYearMonthDayByDayId(dayId + offset);
            if (!holidays.contains(yearMonthDay) && getDayDef(dayId + offset, yearMonthDay).isTrading()) {
                return offset;
            }
        }
        return -1;
    }

    private DayDef getDayDef(int dayId, int yearMonthDay) {
        DayDef dayDef = specialDays.get(yearMonthDay);
        if (dayDef == null)
            dayDef = weekDays[dayOfWeek(dayId)];
        return dayDef;
    }

    private void createJntdDays(int startDayId, int endDayId) {
        int startYearMonthDay = DayUtil.getYearMonthDayByDayId(startDayId);
        int endYearMonthDay = DayUtil.getYearMonthDayByDayId(endDayId);
        DayDef startDef = getDayDef(startDayId, startYearMonthDay);
        DayDef endDef = getDayDef(endDayId, endYearMonthDay);
        // create all initial empty days
        long startTime = startDef.dayStart.get(calendar, getTimeByDayId(startDayId));
        for (int dayId = startDayId; dayId < endDayId; dayId++) {
            int yearMonthDay = DayUtil.getYearMonthDayByDayId(dayId);
            Day day = new Day(this, dayId, yearMonthDay, holidays.contains(yearMonthDay), shortdays.contains(yearMonthDay), startTime);
            day.setSessions(Collections.singletonList(new Session(day, SessionType.NO_TRADING, startTime, startTime)));
            idCache.put(day);
            ymdCache.put(day);
        }
        // create final trading day
        boolean isShortday = shortdays.contains(endYearMonthDay);
        long resetTime = startDef.resetTime.get(calendar, getTimeByDayId(startDayId));
        Day endDay = new Day(this, endDayId, endYearMonthDay, false, isShortday, resetTime);
        ArrayList<Session> sessions = new ArrayList<>();
        addTradingDaySessions(endDay, startDef, getTimeByDayId(startDayId), false, sessions);
        for (int dayId = startDayId + 1; dayId < endDayId; dayId++) {
            DayDef dayDef = getDayDef(dayId, DayUtil.getYearMonthDayByDayId(dayId));
            long time = getTimeByDayId(dayId);
            sessions.add(new Session(endDay, SessionType.NO_TRADING, dayDef.dayStart.get(calendar, time), dayDef.dayEnd.get(calendar, time)));
        }
        addTradingDaySessions(endDay, endDef, getTimeByDayId(endDayId), isShortday, sessions);
        sessions.trimToSize();
        endDay.setSessions(Collections.unmodifiableList(sessions));
        idCache.put(endDay);
        ymdCache.put(endDay);
    }

    // GuardedBy: lock
    private Day createDay(int dayId) {
        creationCounter.incrementAndGet();
        if (joinNextTradingDay != 0) {
            int tradingOffset = getNextTradingDayOffset(dayId, joinNextTradingDay);
            if (tradingOffset >= 0) {
                int endDayId = dayId + tradingOffset;
                int earliestOffset = getEarliestTradingHolidayOffset(endDayId, joinNextTradingDay);
                if (earliestOffset >= tradingOffset) {
                    createJntdDays(endDayId - earliestOffset, endDayId);
                    return idCache.getByKey(dayId);
                }
            }
        }
        long time = getTimeByDayId(dayId);
        int yearMonthDay = DayUtil.getYearMonthDayByDayId(dayId);
        boolean isHoliday = holidays.contains(yearMonthDay);
        boolean isShortDay = shortdays.contains(yearMonthDay);
        DayDef dayDef = getDayDef(dayId, yearMonthDay);
        Day day = new Day(this, dayId, yearMonthDay, isHoliday, isShortDay, dayDef.resetTime.get(calendar, time));
        if (isHoliday) {
            day.setSessions(Collections.singletonList(new Session(day, SessionType.NO_TRADING, dayDef.dayStart.get(calendar, time), dayDef.dayEnd.get(calendar, time))));
        } else {
            ArrayList<Session> sessions = new ArrayList<>();
            addTradingDaySessions(day, dayDef, time, isShortDay, sessions);
            sessions.trimToSize();
            day.setSessions(Collections.unmodifiableList(sessions));
        }
        return day;
    }

    private static int getLastRegularSessionIndex(DayDef def) {
        for (int i = def.sessions.length - 1; i >= 0; i--) if (def.sessions[i].type == SessionType.REGULAR)
            return i;
        return def.sessions.length;
    }

    private static final LongHashSet EMPTY_SET = new LongHashSet();

    private static final LongHashMap<DayDef> EMPTY_MAP = new LongHashMap<>();

    private static clreplaced Defaults {

        long date;

        final Map<String, LongHashSet> holidays = new HashMap<>();

        final Map<String, LongHashSet> shortdays = new HashMap<>();

        final Map<String, Map<String, String>> venues = new HashMap<>();

        Defaults() {
        }
    }

    private static volatile Defaults DEFAULTS = new Defaults();

    private static final SynchronizedIndexedSet<String, Schedule> SCHEDULES = SynchronizedIndexedSet.create((IndexerFunction<String, Schedule>) schedule -> schedule.def);

    private static Schedule getSchedule(String def) {
        Schedule schedule = SCHEDULES.getByKey(def);
        if (schedule == null)
            schedule = SCHEDULES.putIfAbsentAndGet(new Schedule(def));
        return schedule;
    }

    static {
        try (InputStream in = Schedule.clreplaced.getResourcereplacedtream("schedule.properties")) {
            if (in != null)
                setDefaults(in);
        } catch (Throwable t) {
            log.error("Unexpected error", t);
        }
        downloadDefaults(SystemProperties.getProperty(DOWNLOAD_PROPERTY, null));
    }

    private static String setDefaults(InputStream in) throws IOException {
        Defaults newDef = new Defaults();
        // noinspection IOResourceOpenedButNotSafelyClosed
        BufferedReader br = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8));
        for (String line; (line = br.readLine()) != null; ) {
            while (line.endsWith("\\")) {
                line = line.substring(0, line.length() - 1);
                String nextLine = br.readLine();
                if (nextLine == null)
                    break;
                line = line + nextLine;
            }
            if (line.startsWith("date=")) {
                newDef.date = TimeFormat.GMT.parse(line.substring("date=".length())).getTime();
                continue;
            }
            int dot = line.indexOf('.');
            int eq = line.indexOf('=');
            if (line.isEmpty() || line.startsWith("#") || dot <= 0 || eq <= 0 || dot > eq)
                continue;
            String key = line.substring(0, eq);
            String subkey = key.substring(dot + 1);
            String value = line.substring(eq + 1);
            if (key.startsWith("hd.")) {
                if (newDef.holidays.put(subkey, readDays(newDef.holidays, value)) != null)
                    throw new IllegalArgumentException("duplicate holiday list " + line);
            } else if (key.startsWith("sd.")) {
                if (newDef.shortdays.put(subkey, readDays(newDef.shortdays, value)) != null)
                    throw new IllegalArgumentException("duplicate short day list " + line);
            } else if (key.startsWith("tv.")) {
                if (newDef.venues.put(subkey, readProps(value)) != null)
                    throw new IllegalArgumentException("duplicate venue " + line);
            }
        }
        // Atomic volatile read.
        Defaults oldDef = DEFAULTS;
        if (newDef.date < oldDef.date)
            return "older than current - ignored";
        // Atomic volatile write.
        DEFAULTS = newDef;
        if (newDef.holidays.equals(oldDef.holidays) && newDef.shortdays.equals(oldDef.shortdays) && newDef.venues.equals(oldDef.venues))
            return "identical to current";
        for (Schedule schedule : SCHEDULES) try {
            schedule.init();
        } catch (Throwable t) {
            log.error("Unexpected error", t);
        }
        return "newer than current - applied";
    }

    private static LongHashSet readDays(Map<String, LongHashSet> refs, String list) {
        if (list == null || list.isEmpty())
            return EMPTY_SET;
        LongHashSet days = new LongHashSet();
        LongHashSet ref = null;
        for (String s : list.split(",")) {
            if (s.isEmpty())
                continue;
            boolean minus = s.startsWith("-");
            if (minus)
                s = s.substring(1);
            ref = refs.get(s);
            if (ref == null) {
                try {
                    int d = Integer.parseInt(s);
                    if (minus)
                        days.remove(d);
                    else
                        days.add(d);
                } catch (NumberFormatException e) {
                    throw new IllegalArgumentException("cannot find day list " + s + " when parsing " + list);
                }
            } else {
                if (minus)
                    days.removeAll(ref);
                else
                    days.addAll(ref);
            }
        }
        if (days.isEmpty())
            return EMPTY_SET;
        if (ref != null && ref.equals(days))
            return ref;
        return days;
    }

    private static Map<String, String> readProps(String props) {
        Map<String, String> m = new HashMap<>();
        for (String s : props.split(";")) {
            String[] ss = s.split("=", -1);
            for (int i = 0; i < ss.length - 1; i++) m.put(ss[i], ss[ss.length - 1]);
        }
        return m;
    }
}

18 Source : OrderBookList.java
with Mozilla Public License 2.0
from devexperts

/**
 * Implements buy and sell order books with filtering out lower scope order events.
 */
clreplaced OrderBookList extends CheckedTreeList<Order> implements ObservableListModel<Order> {

    // turn on to to debug (validate tree after every change and log changes)
    // SET TO FALSE IN PRODUCTION CODE (!!!)
    private static final boolean DEBUG_TREE = false;

    private static clreplaced MMIDEntry {

        final String mmid;

        // Scope.AGGREGATE
        Node<Order> aggregate;

        // Number of orders with Scope.ORDER
        int orderCount;

        MMIDEntry(String mmid) {
            this.mmid = (mmid == null) ? "" : mmid;
        }
    }

    private volatile boolean closed;

    private final List<ObservableListModelListener<? super Order>> listeners = new CopyOnWriteArrayList<>();

    private OrderBookModelFilter filter;

    private boolean modelChanged;

    private final ObservableListModelListener.Change<Order> change = new ObservableListModelListener.Change<>(this);

    // Nodes for orders of different scopes for easy filtration
    // Scope.COMPOSITE
    private Node<Order> composite;

    // Number of orders with scope higher than Scope.COMPOSITE
    private int nonCompositeCount;

    @SuppressWarnings("unchecked")
    private Node<Order>[] // for all exchanges
    regionals = new Node[128];

    @SuppressWarnings("unchecked")
    private IndexedSet<String, MMIDEntry>[] // for all exchanges
    aggregates = new IndexedSet[128];

    protected OrderBookList(Comparator<? super Order> c) {
        super(c);
    }

    void close() {
        closed = true;
        listeners.clear();
    }

    // ObservableListModel<Order> Interface Implementation
    @Override
    public void addListener(ObservableListModelListener<? super Order> listener) {
        if (closed)
            return;
        listeners.add(listener);
    }

    @Override
    public void removeListener(ObservableListModelListener<? super Order> listener) {
        listeners.remove(listener);
    }

    // Utility methods
    protected void setFilter(OrderBookModelFilter filter) {
        this.filter = filter;
    }

    @Override
    public void clear() {
        clearFilters();
        super.clear();
        modelChanged = true;
    }

    protected void clearFilters() {
        composite = null;
        nonCompositeCount = 0;
        for (int i = 0; i < 128; i++) {
            regionals[i] = null;
            if (aggregates[i] != null)
                aggregates[i].clear();
        }
    }

    // It is used in tests only (in actual usage existing nodes are reinserted)
    protected Node<Order> insertOrder(Order value) {
        return insertOrderNode(new Node<>(value));
    }

    protected Node<Order> insertOrderNode(Node<Order> node) {
        insertNode(node);
        Node<Order> oldNode = checkNode(node);
        if (oldNode != null && uncheck(oldNode)) {
            if (oldNode.getValue().getScope() != Scope.COMPOSITE)
                nonCompositeCount--;
        }
        modelChanged = true;
        if (DEBUG_TREE)
            validateTree();
        return node;
    }

    protected void deleteOrderNode(Node<Order> node) {
        if (node.isRemoved())
            // nothing to do on removed node
            return;
        Node<Order> newNode = uncheckNode(node);
        deleteNode(node);
        if (newNode != null && check(newNode)) {
            if (newNode.getValue().getScope() != Scope.COMPOSITE)
                nonCompositeCount++;
        }
        if (DEBUG_TREE)
            validateTree();
        modelChanged = true;
    }

    protected Node<Order> updateOrderNode(Node<Order> node) {
        deleteOrderNode(node);
        return insertOrderNode(node);
    }

    protected void beginChange() {
        modelChanged = false;
    }

    protected boolean endChange() {
        if (!modelChanged)
            return false;
        fireModelChanged();
        return true;
    }

    protected void fireModelChanged() {
        for (ObservableListModelListener<? super Order> listener : listeners) listener.modelChanged(change);
    }

    // Check the node and return previous scope node that must be unchecked, or null.
    protected Node<Order> checkNode(Node<Order> node) {
        Scope scope = node.getValue().getScope();
        if (!filter.allowScope(scope))
            return null;
        if (scope == Scope.COMPOSITE) {
            composite = node;
            if (nonCompositeCount == 0)
                check(node);
            return null;
        }
        char exchangeCode = node.getValue().getExchangeCode();
        IndexedSet<String, MMIDEntry> aggregate = aggregates[exchangeCode];
        if (scope == Scope.REGIONAL) {
            regionals[exchangeCode] = node;
            if (aggregate == null || aggregate.isEmpty()) {
                check(node);
                nonCompositeCount++;
            }
            return composite;
        }
        // Create entry for exchange and market-maker if needed
        if (aggregate == null) {
            aggregate = IndexedSet.create((IndexerFunction<String, MMIDEntry>) entry -> entry.mmid);
            aggregates[exchangeCode] = aggregate;
        }
        String marketMaker = node.getValue().getMarketMaker();
        MMIDEntry entry = aggregate.getByKey(marketMaker);
        if (entry == null)
            entry = aggregate.putIfAbsentAndGet(new MMIDEntry(marketMaker));
        Node<Order> regional = regionals[exchangeCode];
        if (scope == Scope.AGGREGATE) {
            entry.aggregate = node;
            if (entry.orderCount == 0) {
                check(node);
                nonCompositeCount++;
            }
            return (regional != null) ? regional : composite;
        }
        if (scope == Scope.ORDER) {
            entry.orderCount++;
            check(node);
            nonCompositeCount++;
            return (entry.aggregate != null) ? entry.aggregate : (regional != null) ? regional : composite;
        }
        return null;
    }

    // Uncheck the node and return the previous scope node that must be checked, or null
    protected Node<Order> uncheckNode(Node<Order> node) {
        boolean checked = uncheck(node);
        Scope scope = node.getValue().getScope();
        if (scope == Scope.COMPOSITE) {
            composite = null;
            return null;
        }
        // Decrement count for non-composite scope orders
        if (checked) {
            replacedert (nonCompositeCount > 0);
            nonCompositeCount--;
        }
        char exchangeCode = node.getValue().getExchangeCode();
        if (scope == Scope.REGIONAL) {
            regionals[exchangeCode] = null;
            return !checked ? null : (nonCompositeCount == 0) ? composite : null;
        }
        // Find entry for exchange and market-maker
        IndexedSet<String, MMIDEntry> aggregate = aggregates[exchangeCode];
        if (aggregate == null)
            return null;
        String marketMaker = node.getValue().getMarketMaker();
        MMIDEntry entry = aggregate.getByKey(marketMaker);
        if (entry == null)
            return null;
        Node<Order> regional = regionals[exchangeCode];
        if (scope == Scope.AGGREGATE) {
            entry.aggregate = null;
            if (entry.orderCount == 0)
                aggregate.removeKey(marketMaker);
            return !checked ? null : (regional != null) ? regional : (nonCompositeCount == 0) ? composite : null;
        }
        if (scope == Scope.ORDER) {
            if (checked)
                entry.orderCount--;
            replacedert (entry.orderCount >= 0);
            if (entry.orderCount == 0 && entry.aggregate == null)
                aggregate.removeKey(marketMaker);
            return !checked ? null : (entry.orderCount > 0) ? null : (entry.aggregate != null) ? entry.aggregate : (regional != null) ? regional : (nonCompositeCount == 0) ? composite : null;
        }
        return null;
    }
}

18 Source : DXFeedSubscription.java
with Mozilla Public License 2.0
from devexperts

/**
 * Subscription for a set of symbols and event types.
 *
 * <h3>Symbols and event types</h3>
 *
 * Symbols are represented by objects, simple keys are represented by {@link String} objects,
 * complex ones use specialized object as described in the corresponding event types.
 * However, every symbol has a unique string representation that can be used.
 * Each event is represented by a concrete instance of
 * event clreplaced. Event type is represented by a clreplaced literal. For example,
 * <ul>
 * <li>"SPY" is a symbol for SPDR S&P 500 ETF,
 * <li>{@code Quote.clreplaced} is an event type for a best market quote.
 * </ul>
 * Events can be represented by any clreplacedes that annotated by an inheritable annotation {@link EventType}.
 * Common types of market events extend {@link MarketEvent MarketEvent} clreplaced
 * and reside in <a href="../event/market/package-summary.html"><code>com.dxfeed.event.market</code></a> package.
 *
 * <p> The set of subscribed symbols and the set of subscribed event types are maintained separately.
 * The subscription is considered to be interested in the cross-product of these sets. That is, subscription
 * is interested in any event whose type is in the set of subscribed event types and whose symbol is in the
 * set of subscribed symbols.
 *
 * <p> The set of event types is specified when subscription is created and cannot be changed afterward.
 * The set of symbols can be modified with
 * {@link #setSymbols(Collection) setSymbols}, {@link #addSymbols(Collection) addSymbols},
 * {@link #removeSymbols(Collection) removeSymbols}, and {@link #clear() clear} methods.
 * Each of {@code xxxSymbols} method exists in two version. One version accepts a {@link Collection}
 * of symbols and it recommended for bulk modifications of symbol set. The other version accepts
 * varargs arrays of symbols and is provided as a convenience method to set/add/remove one or few
 * symbols.
 *
 * <p>Symbols in a set are compared using their {@link Object#equals(Object) equals} method. All symbol objects
 * must properly support {@link Object#hashCode() hashCode} method. A set of symbols cannot contain two equal symbols.
 * If a symbol that is {@link #addSymbols(Collection) added} to a set of symbols is equal to the other one,
 * then the old symbol is removed from the set and the new symbol instance is retained. However,
 * the {@link ObservableSubscriptionChangeListener} (see below) in this case is notified on the change of
 * subscription only when new symbol object implements {@link FilteredSubscriptionSymbol} marker interface.
 *
 * <h3>Special symbol types and restrictions</h3>
 *
 * A single object {@link WildcardSymbol#ALL WildcardSymbol.ALL} represents subscription to all possible symbols
 * and have the effect of subscribing to all of them. See {@link WildcardSymbol} for more details. It
 * is always represented by a separate clreplaced (instead of a usual {@link String}) in order
 * to avoid potential resource consumption problems that may stem from an accidental subscription via wildcard.
 * All string symbols that start with {@link WildcardSymbol#RESERVED_PREFIX WildcardSymbol.RESERVED_PREFIX} ("*")
 * are reserved for the same reason. Subscription to the corresponding string does not have any effect
 * (no events will be received). Thus, it is safe to add user-specified strings to the subscription via
 * {@link #addSymbols(Object...) addSymbols} method without any prior validation of the user input.
 *
 * <p><b>NOTE:</b> Wildcard subscription can create extremely high network and CPU load for certain kinds of
 * high-frequency events like quotes. It requires a special arrangement on the side of upstream data provider and
 * is disabled by default in upstream feed configuration. Make that sure you have adequate resources and understand
 * the impact before using it. It can be used for low-frequency events only (like Forex quotes), because each instance
 * of {@code DXFeedSubscription} processes events in a single thread and there is no provision to load-balance wildcard
 * subscription amongst multiple threads.
 *
 * Contact your data provider for the corresponding configuration arrangement if needed.
 * <p> A special clreplaced of {@link TimeSeriesSubscriptionSymbol} symbols is used internally to represent a subscription
 * to time-series of events. Two instances of {@link TimeSeriesSubscriptionSymbol} objects are
 * {@link TimeSeriesSubscriptionSymbol#equals(Object) equal} when their
 * {@link TimeSeriesSubscriptionSymbol#getEventSymbol() underlying symbols} are equal, thus only one, most recently
 * added, instance of {@link TimeSeriesSubscriptionSymbol} will be kept in the set of subscribed symbols.
 *
 * <p>It is recommended to use {@link DXFeedTimeSeriesSubscription} convenience clreplaced for time-series subscriptions.
 *
 * <p>{@link Candle} events use {@link CandleSymbol} clreplaced to represent the complex structure of the candle symbol
 * and its attributes. However, both {@link String} and {@link CandleSymbol} objects can be used in subscription.
 * The corresponding strings will be converted to {@link CandleSymbol} using its {@link CandleSymbol#valueOf(String) valueOf}
 * method. Do not mix {@link String} and {@link CandleSymbol} subscription in a single {@code DXFeedSubscription} instance.
 *
 * <h3>Subscription listeners</h3>
 *
 * This clreplaced keeps a list of {@link DXFeedEventListener} instances that are notified on any events.
 * Event listeners are added with {@link #addEventListener(DXFeedEventListener) addEventListener} method.
 * <b>Event listeners must be installed before changing the set of subscribed symbols</b>.
 * When the set of subscribed symbols changes all registered event listeners receive update on the
 * last events for all newly added symbols.
 *
 * <p> This clreplaced keeps a set of {@link ObservableSubscriptionChangeListener} instances that are notified on any
 * change in subscription. These listeners are installed by {@link DXFeed} to keep
 * track of the subscription state and communicate subscription upstream to data providers.
 *
 * <h3>Detached and attached subscriptions</h3>
 *
 * Subscription that is created via constructor is <i>detached</i>. It is not attached
 * to any feed and thus it does not actually receive any events. Detached subscription still maintains
 * a set of symbols and a list of event listeners. Detached subscription can be attached to
 * any feed with {@link DXFeed#attachSubscription DXFeed.attachSubscription} method.
 *
 * <p> Subscription that is created via {@link DXFeed#createSubscription DXFeed.createSubscription}
 * is <i>attached</i> to the
 * corresponding feed. The feed tracks all changes in subscription by installing
 * {@link ObservableSubscriptionChangeListener} and invokes
 * {@link #processEvents processEvents} for all received events.
 * Subscription can be detached from the feed with
 * {@link DXFeed#detachSubscription DXFeed.detachSubscription} method.
 *
 * <p> Subscription can be attached to multiple feeds at the same time. In this case it receives events from
 * all feeds but there is no way distinguish which feed the corresponding event came from.
 *
 * <h3>Resource management and closed subscriptions</h3>
 *
 * Attached subscription is a potential memory leak. If the pointer to attached subscription is lost, then there is
 * no way to detach this subscription from the feed and the subscription will not be reclaimed by the garbage collector
 * as long as the corresponding feed is still used. Detached subscriptions can be reclaimed by the garbage collector,
 * but detaching subscription requires knowing the pointer to the feed at the place of the call, which is not always convenient.
 *
 * <p> The convenient way to detach subscription from the feed is to call its {@link #close close} method. Closed subscription
 * becomes permanently detached from all feeds, removes all its listeners and is guaranteed to be reclaimable by the garbage
 * collector as soon as all external references to it are cleared.
 *
 * <p> The other way is to close an replacedociated feed. This cannot be done via a {@link DXFeed} instance, but it can be done
 * indirectly by closing the replacedociated {@link DXEndpoint endpoint}.
 *
 * <h3>Serialization</h3>
 *
 * This clreplaced's serialized state includes only serializable listeners. {@link ObservableSubscriptionChangeListener}
 * that is installed by {@link DXFeed} when this subscription is attached is not serializable.
 * Thus, freshly deserialized instance of this clreplaced will be <i>detached</i>. It has to be attached
 * to the feed after deserialization in order for it to start receiving events.
 *
 * <h3><a name="threadsAndLocksSection">Threads and locks</a></h3>
 *
 * This clreplaced is thread-safe and can be used concurrently from multiple threads without external synchronization.
 *
 * <p> Installed {@link DXFeedEventListener} instances are invoked from a separate thread via the executor.
 * Default executor for all subscriptions is configured with {@link DXEndpoint#executor(Executor) DXEndpoint.executor}
 * method. Each subscription can individually override its executor with {@link #setExecutor(Executor) setExecutor}
 * method.
 *
 * <p> Event listeners are invoked in a serial manner with respect to a given DXFeedSubscription instance.
 * That is, next event notification will not be performed until
 * {@link DXFeedEventListener#eventsReceived(List) DXFeedEventListener.eventsReceived} method for the previous
 * notification completes. It guarantees that the order of events in a given instance of {@code DXFeedSubscription}
 * is preserved.
 *
 * <p> This requirement on ordering limits concurrency of event processing. Effectively, each
 * subscription can use at most one CPU core to process its events. If there is a need to simultaneously
 * process events on a large number of symbols and this event processing is so resource-consuming, that
 * one CPU core is not enough to process all events in real-time, then it is advised to split symbols between multiple
 * {@code DXFeedSubscription} instances. If event processing is mostly CPU-bound, then the good rule of thumb
 * is to have as many {@code DXFeedSubscription} instances as there are CPU cores in the system.
 *
 * <p> However, multiple tasks can get submitted to the executor at the same time. In the current implementation,
 * at most two tasks are submitted at any time if
 * {@link DXEndpoint#DXFEED_AGGREGATION_PERIOD_PROPERTY DXFEED_AGGREGATION_PERIOD_PROPERTY} is used.
 * One task for immediate processing of data snapshots via {@link Executor#execute(Runnable) Executor.execute} method
 * and another task for delayed processing of data updates via
 * {@link ScheduledExecutorService#schedule(Runnable, long, TimeUnit) ScheduledExecutorService.schedule} method
 * if the executor implements {@link ScheduledExecutorService} interface.
 * At most one task is submitted at any time if this property is not used.
 *
 * <p> Installed {@link ObservableSubscriptionChangeListener} instances are notified on symbol set changes
 * while holding the lock on this subscription and in the same thread that changed the set of subscribed symbols
 * in this subscription.
 *
 * <p>Custom executor can be used by backend applications that do not need to immediately retrieve a collection
 * of events, but want to poll for new events at a later time, for example, from inside of a servlet request.
 * The following code pattern is suggested in this case to initialize subscription's executor:
 *
 * <pre><tt>
 * {@link ConcurrentLinkedQueue}<{@link Runnable Runnable}> taskQueue = <b>new</b> ConcurrentLinkedQueue<>();
 * subscription.{@link #setExecutor(Executor) setExecutor}(<b>new</b> {@link Executor}() {
 *     <b>public void</b> execute({@link Runnable Runnable} task) {
 *         taskQueue.{@link ConcurrentLinkedQueue#add add}(task);
 *     }
 * });
 * subscription.{@link #addEventListener(DXFeedEventListener) addEventListener}(...);
 * </tt></pre>
 *
 * When there is a time to poll for new events, the following code can be used:
 *
 * <pre><tt>
 * {@link Runnable Runnable} task;
 * <b>while</b> ((task = taskQueue.{@link ConcurrentLinkedQueue#poll poll}()) != <b>null</b>)
 *     task.run();
 * </tt></pre>
 *
 * and event listener's {@link DXFeedEventListener#eventsReceived(List) eventsReceived} method will be invoked
 * in this thread from inside of {@code task.run()} invocation.
 *
 * <p> This approach has a clear advantage with {@link LastingEvent LastingEvent} types.
 * If the above polling code is delayed or is executed only periodically, then incoming events have a chance
 * to get conflated and listener will receive only on the most recent event updates for processing.
 *
 * @param <E> the type of events.
 */
public clreplaced DXFeedSubscription<E> implements Serializable, ObservableSubscription<E>, AutoCloseable {

    private static final long serialVersionUID = 0;

    // closed state
    private volatile boolean closed;

    // initialized during construction in init() method
    private transient Set<Clreplaced<? extends E>> eventTypeSet;

    private transient IndexerFunction<Object, Object> eventSymbolIndexer;

    private transient IndexedSet<Object, Object> symbols;

    private transient ObservableSubscriptionChangeListener changeListeners;

    private transient volatile Executor executor;

    // fires without synchronization
    private transient volatile DXFeedEventListener<E> eventListeners;

    // initialized on first use
    private transient Set<?> undecoratedSymbols;

    private transient Set<?> decoratedSymbols;

    /**
     * Creates <i>detached</i> subscription for a single event type.
     *
     * @param eventType the event type.
     * @throws NullPointerException if event type is null.
     */
    public DXFeedSubscription(Clreplaced<? extends E> eventType) {
        init(eventType);
    }

    /**
     * Creates <i>detached</i> subscription for the given list of event types.
     *
     * @param eventTypes the list of event types.
     * @throws IllegalArgumentException if the list of event types is empty.
     * @throws NullPointerException if any event type is null.
     */
    @SafeVarargs
    public DXFeedSubscription(Clreplaced<? extends E>... eventTypes) {
        init(eventTypes);
    }

    /**
     * Attaches subscription to the specified feed.
     *
     * @param feed feed to attach to.
     */
    public void attach(DXFeed feed) {
        feed.attachSubscription(this);
    }

    /**
     * Detaches subscription from the specified feed.
     *
     * @param feed feed to detach from.
     */
    public void detach(DXFeed feed) {
        feed.detachSubscription(this);
    }

    /**
     * Returns <code>true</code> if this subscription is closed.
     *
     * @see #close
     */
    @Override
    public boolean isClosed() {
        return closed;
    }

    /**
     * Closes this subscription and makes it <i>permanently detached</i>.
     * This method notifies
     * all installed {@link ObservableSubscriptionChangeListener} instances by invoking
     * {@link ObservableSubscriptionChangeListener#subscriptionClosed subscriptionClosed}
     * while holding the lock for this subscription. This method clears lists of all installed
     * event listeners and subscription change listeners and makes sure that no more listeners
     * can be added.
     *
     * <p> This method ensures that subscription can be safely garbage-collected when all outside references
     * to it are lost.
     */
    @Override
    public synchronized void close() {
        if (closed)
            return;
        closed = true;
        eventListeners = null;
        ObservableSubscriptionChangeListener notifyListeners = changeListeners;
        changeListeners = null;
        if (notifyListeners != null)
            notifyListeners.subscriptionClosed();
    }

    /**
     * Returns a set of subscribed event types. The resulting set cannot be modified.
     */
    @Override
    public Set<Clreplaced<? extends E>> getEventTypes() {
        return eventTypeSet;
    }

    /**
     * Returns <code>true</code> if this subscription contains the corresponding event type.
     * @see #getEventTypes()
     */
    @Override
    public boolean containsEventType(Clreplaced<?> eventType) {
        return eventTypeSet.contains(eventType);
    }

    /**
     * Clears the set of subscribed symbols. This implementation calls
     * <pre><tt>{@link #setSymbols setSymbols}(Collections.EMPTY_LIST)</tt></pre>
     */
    public void clear() {
        setSymbols(Collections.EMPTY_LIST);
    }

    /**
     * Returns a set of subscribed symbols. The resulting set cannot be modified. The contents of the resulting set
     * are undefined if the set of symbols is changed after invocation of this method, but the resulting set is
     * safe for concurrent reads from any threads. The resulting set maybe either a snapshot of the set of
     * the subscribed symbols at the time of invocation or a weakly consistent view of the set.
     */
    public Set<?> getSymbols() {
        if (undecoratedSymbols == null)
            undecoratedSymbols = new SymbolView(true);
        return undecoratedSymbols;
    }

    /**
     * Returns a set of decorated symbols (depending on the actual implementation clreplaced of {@link DXFeedSubscription}).
     *
     * <p>The resulting set cannot be modified. The contents of the resulting set
     * are undefined if the set of symbols is changed after invocation of this method, but the resulting set is
     * safe for concurrent reads from any threads. The resulting set maybe either a snapshot of the set of
     * the subscribed symbols at the time of invocation or a weakly consistent view of the set.
     */
    public Set<?> getDecoratedSymbols() {
        if (decoratedSymbols == null)
            decoratedSymbols = new SymbolView(false);
        return decoratedSymbols;
    }

    /**
     * Changes the set of subscribed symbols so that it contains just the symbols from the specified collection.
     * To conveniently set subscription for just one or few symbols you can use
     * {@link #setSymbols(Object...) setSymbols(Object... symbols)} method.
     * All registered event listeners will receive update on the last events for all
     * newly added symbols.
     *
     * <h3>Implementation notes</h3>
     *
     * This method notifies
     * all installed {@link ObservableSubscriptionChangeListener} instances of any resulting changes in the set of
     * subscribed symbols while holding the lock for this subscription.
     *
     * <p> This implementation decorates symbols via protected {@link #decorateSymbol(Object)} method,
     * that can be overridden in subclreplacedes of {@code DXFeedSubscription}. Installed
     * {@code ObservableSubscriptionChangeListener} instances receive decorated symbols.
     *
     * @param symbols the collection of symbols.
     */
    public void setSymbols(Collection<?> symbols) {
        setSymbolsImpl(decorateSymbols(symbols));
    }

    /**
     * Changes the set of subscribed symbols so that it contains just the symbols from the specified array.
     * This is a convenience method to set subscription to one or few symbols at a time.
     * When setting subscription to multiple
     * symbols at once it is preferable to use
     * {@link #setSymbols(Collection) setSymbols(Collection<?> symbols)} method.
     * All registered event listeners will receive update on the last events for all
     * newly added symbols.
     *
     * <h3>Implementation notes</h3>
     *
     * This method notifies
     * all installed {@link ObservableSubscriptionChangeListener} instances of any resulting changes in the set of
     * subscribed symbols while holding the lock for this subscription.
     *
     * <p> This implementation decorates symbols via protected {@link #decorateSymbol(Object)} method,
     * that can be overridden in subclreplacedes of {@code DXFeedSubscription}. Installed
     * {@code ObservableSubscriptionChangeListener} instances receive decorated symbols.
     *
     * @param symbols the array of symbols.
     */
    public void setSymbols(Object... symbols) {
        setSymbolsImpl(decorateSymbols(symbols));
    }

    /**
     * Adds the specified collection of symbols to the set of subscribed symbols.
     * To conveniently add one or few symbols you can use
     * {@link #addSymbols(Object...) addSymbols(Object... symbols)} method.
     * All registered event listeners will receive update on the last events for all
     * newly added symbols.
     *
     * <h3>Implementation notes</h3>
     *
     * This method notifies
     * all installed {@link ObservableSubscriptionChangeListener} instances of any resulting changes in the set of
     * subscribed symbols while holding the lock for this subscription.
     *
     * <p> This implementation decorates symbols via protected {@link #decorateSymbol(Object)} method,
     * that can be overridden in subclreplacedes of {@code DXFeedSubscription}. Installed
     * {@code ObservableSubscriptionChangeListener} instances receive decorated symbols.
     *
     * @param symbols the collection of symbols.
     */
    public void addSymbols(Collection<?> symbols) {
        if (symbols.isEmpty())
            return;
        addSymbolsImpl(decorateSymbols(symbols));
    }

    /**
     * Adds the specified array of symbols to the set of subscribed symbols.
     * This is a convenience method to subscribe to one or few symbols at a time.
     * When subscribing to multiple
     * symbols at once it is preferable to use
     * {@link #addSymbols(Collection) addSymbols(Collection<?> symbols)} method.
     * All registered event listeners will receive update on the last events for all
     * newly added symbols.
     *
     * <h3>Implementation notes</h3>
     *
     * This method notifies
     * all installed {@link ObservableSubscriptionChangeListener} instances of any resulting changes in the set of
     * subscribed symbols while holding the lock for this subscription.
     *
     * <p> This implementation decorates symbols via protected {@link #decorateSymbol(Object)} method
     * that can be overridden in subclreplacedes of {@code DXFeedSubscription}. Installed
     * {@code ObservableSubscriptionChangeListener} instances receive decorated symbols.
     *
     * @param symbols the array of symbols.
     */
    public void addSymbols(Object... symbols) {
        if (symbols.length == 0)
            // no symbols -- nothing to do
            return;
        if (symbols.length == 1)
            // shortcut to optimized one symbol case
            addSymbolImpl(decorateSymbol(symbols[0]));
        else
            // multiple symbols
            addSymbolsImpl(decorateSymbols(symbols));
    }

    /**
     * Adds the specified symbol to the set of subscribed symbols.
     * This is a convenience method to subscribe to one symbol at a time that
     * has a return fast-path for a case when the symbol is already in the set.
     * When subscribing to multiple
     * symbols at once it is preferable to use
     * {@link #addSymbols(Collection) addSymbols(Collection<?> symbols)} method.
     * All registered event listeners will receive update on the last events for all
     * newly added symbols.
     *
     * <h3>Implementation notes</h3>
     *
     * This method notifies
     * all installed {@link ObservableSubscriptionChangeListener} instances of any resulting changes in the set of
     * subscribed symbols while holding the lock for this subscription.
     *
     * <p> This implementation decorates symbols via protected {@link #decorateSymbol(Object)} method
     * that can be overridden in subclreplacedes of {@code DXFeedSubscription}. Installed
     * {@code ObservableSubscriptionChangeListener} instances receive decorated symbols.
     *
     * @param symbol the symbol.
     */
    public void addSymbols(Object symbol) {
        addSymbolImpl(decorateSymbol(symbol));
    }

    /**
     * Removes the specified collection of symbols from the set of subscribed symbols.
     * To conveniently remove one or few symbols you can use
     * {@link #removeSymbols(Object...) removeSymbols(Object... symbols)} method.
     *
     * <h3>Implementation notes</h3>
     *
     * This method notifies
     * all installed {@link ObservableSubscriptionChangeListener} instances of any resulting changes in the set of
     * subscribed symbols while holding the lock for this subscription.
     * <p>
     * This implementation decorates symbols via protected {@link #decorateSymbol(Object)} method,
     * that can be overridden in subclreplacedes of {@code DXFeedSubscription}. Installed
     * {@code ObservableSubscriptionChangeListener} instances receive decorated symbols.
     *
     * @param symbols the collection of symbols.
     */
    public void removeSymbols(Collection<?> symbols) {
        if (symbols.isEmpty())
            return;
        removeSymbolsImpl(decorateSymbols(symbols));
    }

    /**
     * Removes the specified array of symbols from the set of subscribed symbols.
     * This is a convenience method to remove one or few symbols at a time.
     * When removing multiple
     * symbols at once it is preferable to use
     * {@link #removeSymbols(Collection) removeSymbols(Collection<?> symbols)} method.
     *
     * <h3>Implementation notes</h3>
     *
     * This method notifies
     * all installed {@link ObservableSubscriptionChangeListener} instances of any resulting changes in the set of
     * subscribed symbols while holding the lock for this subscription.
     *
     * <p> This implementation decorates symbols via protected {@link #decorateSymbol(Object)} method,
     * that can be overridden in subclreplacedes of {@code DXFeedSubscription}. Installed
     * {@code ObservableSubscriptionChangeListener} instances receive decorated symbols.
     *
     * @param symbols the array of symbols.
     */
    public void removeSymbols(Object... symbols) {
        if (symbols.length == 0)
            return;
        removeSymbolsImpl(decorateSymbols(symbols));
    }

    /**
     * Returns executor for processing event notifications on this subscription.
     * See <a href="#threadsAndLocksSection">Threads and locks</a> section of this clreplaced doreplacedentation.
     * @return executor for processing event notifications on this subscription,
     *         or {@code null} if default executor of the attached {@link DXFeed} is used.
     */
    public Executor getExecutor() {
        return executor;
    }

    /**
     * Changes executor for processing event notifications on this subscription.
     * See <a href="#threadsAndLocksSection">Threads and locks</a> section of this clreplaced doreplacedentation.
     * @param executor executor for processing event notifications on this subscription,
     *         or {@code null} if default executor of the attached {@link DXFeed} is used.
     */
    public void setExecutor(Executor executor) {
        this.executor = executor;
    }

    /**
     * Adds listener for events.
     * Event lister can be added only when subscription is not producing any events.
     * The subscription must be either empty
     * (its set of {@link #getSymbols() symbols} is empty) or not {@link #attach(DXFeed) attached} to any feed
     * (its set of change listeners is empty).
     *
     * This method does nothing if this subscription is closed.
     *
     * @param listener the event listener.
     * @throws NullPointerException if listener is null.
     * @throws IllegalStateException if subscription is attached and is not empty.
     */
    public synchronized void addEventListener(DXFeedEventListener<E> listener) {
        if (listener == null)
            throw new NullPointerException();
        if (closed)
            return;
        if (changeListeners != null && !symbols.isEmpty())
            throw new IllegalStateException("Cannot add event listener to non-empty attached subscription. Add event listeners first");
        eventListeners = addListener(eventListeners, listener, false, EventListeners::new);
    }

    /**
     * Removes listener for events.
     *
     * @param listener the event listener.
     * @throws NullPointerException if listener is null.
     */
    public synchronized void removeEventListener(DXFeedEventListener<E> listener) {
        if (listener == null)
            throw new NullPointerException();
        eventListeners = removeListener(eventListeners, listener, EventListeners::new);
    }

    /**
     * Adds subscription change listener. This method does nothing if the given listener is already
     * installed as subscription change listener for this subscription or if subscription is closed.
     * Otherwise, it installs the
     * corresponding listener and immediately invokes {@link ObservableSubscriptionChangeListener#symbolsAdded}
     * on the given listener while holding the lock for this
     * subscription. This way the given listener synchronously receives existing subscription state and and
     * is synchronously notified on all changes in subscription afterwards.
     *
     * <p>Whenever a symbol in a set of subscribed symbols is replaced by the other symbol that is
     * {@link #equals(Object) equal} to the old one, the decision on whether to notify installed listeners
     * about the change is based on the result of
     * {@link #shallNotifyOnSymbolUpdate(Object, Object) shallNotifyOnSymbolUpdate} method.
     *
     * @param listener the subscription change listener.
     * @throws NullPointerException if listener is null.
     */
    @Override
    public synchronized void addChangeListener(ObservableSubscriptionChangeListener listener) {
        if (listener == null)
            throw new NullPointerException();
        if (closed)
            return;
        ObservableSubscriptionChangeListener oldListeners = changeListeners;
        changeListeners = addListener(changeListeners, listener, true, ChangeListeners::new);
        // notify new listener on a set of symbols
        if (changeListeners != oldListeners && !symbols.isEmpty())
            listener.symbolsAdded(symbols);
    }

    /**
     * Removes subscription change listener. This method does nothing if the given listener was not
     * installed or was already removed as subscription change listener for this subscription.
     * Otherwise it removes the corresponding listener and immediately invokes
     * {@link ObservableSubscriptionChangeListener#subscriptionClosed} on the given listener while
     * holding the lock for this subscription.
     *
     * @param listener the subscription change listener.
     * @throws NullPointerException if listener is null.
     */
    @Override
    public synchronized void removeChangeListener(ObservableSubscriptionChangeListener listener) {
        if (listener == null)
            throw new NullPointerException();
        ObservableSubscriptionChangeListener oldListeners = changeListeners;
        changeListeners = removeListener(changeListeners, listener, ChangeListeners::new);
        // notify listener on close
        if (changeListeners != oldListeners)
            listener.subscriptionClosed();
    }

    // ----------------------- package-private API for DXFeed -----------------------
    /**
     * Processes received events. This methods invokes {@link DXFeedEventListener#eventsReceived} on all installed
     * event listeners. This is a package-private method for use by {@link DXFeed} clreplaced only.
     * @param events the list of received events.
     */
    void processEvents(List<E> events) {
        // atomic volatile read
        DXFeedEventListener<E> eventListeners = this.eventListeners;
        if (eventListeners != null)
            eventListeners.eventsReceived(events);
    }

    // ----------------------- protected API for subclreplacedes -----------------------
    /**
     * Decorates the specified symbol after it was received from the {@code DXFeedSubscription} client code
     * before it goes to installed {@link ObservableSubscriptionChangeListener} instances.
     * This method can be overridden in subclreplacedes. See {@link DXFeedTimeSeriesSubscription} for an example.
     * This implementation throws {@code NullPointerException} if {@code symbol} is null
     * or returns {@code symbol} otherwise.
     *
     * @param symbol the symbol to decorate.
     * @throws NullPointerException if symbol is null.
     * @return decorated symbol
     */
    protected Object decorateSymbol(Object symbol) {
        if (symbol == null)
            throw new NullPointerException();
        return symbol;
    }

    /**
     * Undoes the decoration of the specified symbol doing the reverse operation to {@link #decorateSymbol(Object)}.
     * This method can be overridden in subclreplacedes. See {@link DXFeedTimeSeriesSubscription} for an example.
     * This implementation throws {@code NullPointerException} if {@code symbol} is null
     * or returns {@code symbol} otherwise.
     *
     * @param symbol the symbol to undecorate.
     * @throws NullPointerException if symbol is null.
     * @return undecorated symbol
     */
    protected Object undecorateSymbol(Object symbol) {
        if (symbol == null)
            throw new NullPointerException();
        return symbol;
    }

    // ----------------------- private implementation details -----------------------
    private static void writeCompactCollection(ObjectOutput out, Collection<?> collection) throws IOException {
        IOUtil.writeCompactInt(out, collection.size());
        for (Object o : collection) out.writeObject(o);
    }

    @SuppressWarnings("unchecked")
    private static <T> T[] readCompactCollection(ObjectInput in) throws IOException, ClreplacedNotFoundException {
        int n = IOUtil.readCompactInt(in);
        T[] a = (T[]) new Object[n];
        for (int i = 0; i < n; i++) a[i] = (T) in.readObject();
        return a;
    }

    @SafeVarargs
    private final void init(Clreplaced<? extends E>... eventTypes) {
        if (eventTypes.length == 0) {
            throw new IllegalArgumentException();
        } else if (eventTypes.length == 1) {
            eventTypeSet = Collections.singleton(Objects.requireNonNull(eventTypes[0]));
        } else {
            Set<Clreplaced<? extends E>> set = IndexedSet.create();
            for (Clreplaced<? extends E> eventType : eventTypes) set.add(Objects.requireNonNull(eventType));
            eventTypeSet = Collections.unmodifiableSet(set);
        }
        eventSymbolIndexer = getClreplaced() == DXFeedSubscription.clreplaced ? IndexerFunction.DEFAULT : this::undecorateSymbol;
        symbols = IndexedSet.create(eventSymbolIndexer);
    }

    private IndexedSet<Object, Object> decorateSymbols(Collection<?> symbols) {
        return symbols.stream().map(this::decorateSymbol).collect(IndexedSet.collector(eventSymbolIndexer));
    }

    private IndexedSet<Object, Object> decorateSymbols(Object... symbols) {
        return Arrays.stream(symbols).map(this::decorateSymbol).collect(IndexedSet.collector(eventSymbolIndexer));
    }

    private synchronized void setSymbolsImpl(IndexedSet<Object, Object> added) {
        IndexedSet<Object, Object> removed = IndexedSet.create(eventSymbolIndexer);
        for (Iterator<Object> it = symbols.iterator(); it.hasNext(); ) {
            Object oldSymbol = it.next();
            if (!added.containsValue(oldSymbol)) {
                it.remove();
                removed.add(oldSymbol);
            }
        }
        addAndNotify(added, removed);
    }

    private synchronized void addSymbolsImpl(IndexedSet<Object, Object> added) {
        addAndNotify(added, null);
    }

    private void addAndNotify(IndexedSet<Object, Object> added, IndexedSet<Object, Object> removed) {
        // was there and "the same" -- don't process it
        added.removeIf(o -> !putSymbol(o));
        if (changeListeners != null && removed != null && !removed.isEmpty())
            changeListeners.symbolsRemoved(removed);
        // make 2nd check for changeListeners in case it was nullified during 1st notification
        if (changeListeners != null && !added.isEmpty())
            changeListeners.symbolsAdded(added);
    }

    private synchronized void addSymbolImpl(Object symbol) {
        if (!putSymbol(symbol))
            // was there and "the same" -- nothing to do
            return;
        if (changeListeners != null)
            changeListeners.symbolsAdded(Collections.singleton(symbol));
    }

    // returns false when "the same" symbol was already in the symbols set
    private boolean putSymbol(Object symbol) {
        Object oldSymbol = symbols.put(symbol);
        return shallNotifyOnSymbolUpdate(symbol, oldSymbol);
    }

    /**
     * Compares newly added symbol with the old one that was present before for
     * the purpose of notifying {@link #addChangeListener(ObservableSubscriptionChangeListener) installed}
     * {@link ObservableSubscriptionChangeListener} about the change.
     * This method returns {@code false} if the new symbol shall be considered <em>the same</em> one and
     * no notification is needed. The attached {@link ObservableSubscriptionChangeListener listeners} get
     * {@link ObservableSubscriptionChangeListener#symbolsAdded(Set) symbolsAdded} notification for all
     * symbols that this method returns {@code true} for.
     *
     * <p> This implementation compares instances of {@link FilteredSubscriptionSymbol} by their reference
     * (this implementation returns {@code true} when {@code symbol != oldSymbol}), because their equals and hashCode do
     * not take input account their filter part. Notification for all other types of symbols (like {@link String}) is
     * optimized away (this implementation returns {@code true} only when {@code oldSymbol == null}).
     *
     * @param symbol the recently added symbol to this subscription (not null).
     * @param oldSymbol the previous symbol from the set of symbol based on equal/hashCode search in a hash set or
     *                  {@code null} if it did not present before.
     * @return {@code true} if listeners shall be notified on the change in the set of subscribed symbols.
     */
    protected boolean shallNotifyOnSymbolUpdate(@Nonnull Object symbol, @Nullable Object oldSymbol) {
        return symbol instanceof FilteredSubscriptionSymbol ? symbol != oldSymbol : oldSymbol == null;
    }

    private synchronized void removeSymbolsImpl(IndexedSet<Object, Object> removed) {
        // remove symbols by key (regardless of identify)
        for (Iterator<Object> it = removed.concurrenreplacederator(); it.hasNext(); ) {
            Object symbol = it.next();
            Object oldSymbol = symbols.removeValue(symbol);
            if (oldSymbol == null)
                // was not there -- nothing to remove
                removed.remove(symbol);
            else if (oldSymbol != symbol)
                // replace with actually removed idenreplacedy
                removed.add(oldSymbol);
        }
        if (removed.isEmpty())
            return;
        if (changeListeners != null)
            changeListeners.symbolsRemoved(removed);
    }

    private synchronized void writeObject(ObjectOutputStream out) throws IOException {
        writeCompactCollection(out, eventTypeSet);
        writeCompactCollection(out, symbols);
        writeCompactCollection(out, getSerializable(eventListeners));
        writeCompactCollection(out, getSerializable(changeListeners));
    }

    @SuppressWarnings("unchecked")
    private void readObject(ObjectInputStream in) throws IOException, ClreplacedNotFoundException {
        Object[] eventTypes = readCompactCollection(in);
        init(Arrays.copyOf(eventTypes, eventTypes.length, Clreplaced[].clreplaced));
        Collections.addAll(symbols, readCompactCollection(in));
        eventListeners = simplifyListener(readCompactCollection(in), EventListeners::new);
        changeListeners = simplifyListener(readCompactCollection(in), ChangeListeners::new);
    }

    private clreplaced SymbolView extends AbstractSet<Object> {

        private final boolean undecorate;

        public SymbolView(boolean undecorate) {
            this.undecorate = undecorate;
        }

        @Nonnull
        @Override
        public Iterator<Object> iterator() {
            return new SymbolViewIterator(undecorate, symbols.concurrenreplacederator());
        }

        @Override
        public int size() {
            return symbols.size();
        }

        @Override
        public boolean contains(Object o) {
            // 'symbols' field always uses undecorating indexer
            return undecorate ? symbols.containsKey(o) : symbols.containsValue(o);
        }
    }

    private clreplaced SymbolViewIterator implements Iterator<Object> {

        private final boolean undecorate;

        private final Iterator<Object> it;

        SymbolViewIterator(boolean undecorate, Iterator<Object> it) {
            this.undecorate = undecorate;
            this.it = it;
        }

        @Override
        public boolean hasNext() {
            return it.hasNext();
        }

        @Override
        public Object next() {
            Object next = it.next();
            return undecorate ? undecorateSymbol(next) : next;
        }

        @Override
        public void remove() {
            throw new UnsupportedOperationException();
        }
    }

    // ----------------------- private listener lists -----------------------
    private static Collection<Object> getSerializable(Object oneOrList) {
        if (oneOrList instanceof Serializable)
            return Collections.singletonList(oneOrList);
        if (oneOrList instanceof ListenerList) {
            Collection<Object> result = new ArrayList<>();
            for (Object o : ((ListenerList<?>) oneOrList).a) if (o instanceof Serializable)
                result.add(o);
            return result;
        }
        return Collections.emptyList();
    }

    @SuppressWarnings("unchecked")
    private static <L> L simplifyListener(Object[] a, Function<Object[], L> listWrapper) {
        return a.length == 0 ? null : a.length == 1 ? (L) a[0] : listWrapper.apply(a);
    }

    @SuppressWarnings("unchecked")
    private static <L> L addListener(L oneOrList, L listener, boolean idempotent, Function<Object[], L> listWrapper) {
        if (oneOrList == null)
            return listener;
        if (idempotent && listener.equals(oneOrList))
            return oneOrList;
        if (!(oneOrList instanceof ListenerList))
            return listWrapper.apply(new Object[] { oneOrList, listener });
        ListenerList<L> list = (ListenerList<L>) oneOrList;
        if (idempotent && findListener(list, listener) >= 0)
            return oneOrList;
        Object[] a = Arrays.copyOf(list.a, list.a.length + 1);
        a[a.length - 1] = listener;
        return listWrapper.apply(a);
    }

    @SuppressWarnings("unchecked")
    private static <L> L removeListener(L oneOrList, L listener, Function<Object[], L> listWrapper) {
        if (oneOrList == null)
            return null;
        if (listener.equals(oneOrList))
            return null;
        if (!(oneOrList instanceof ListenerList))
            return oneOrList;
        ListenerList<L> list = (ListenerList<L>) oneOrList;
        int i = findListener(list, listener);
        if (i < 0)
            return oneOrList;
        return list.a.length == 2 ? (L) list.a[1 - i] : listWrapper.apply(removeListenerAt(list, i));
    }

    static <L> int findListener(ListenerList<L> oldList, L newListener) {
        for (int i = 0; i < oldList.a.length; i++) if (newListener.equals(oldList.a[i]))
            return i;
        return -1;
    }

    static <L> Object[] removeListenerAt(ListenerList<L> oldList, int removeIndex) {
        Object[] a = new Object[oldList.a.length - 1];
        System.arraycopy(oldList.a, 0, a, 0, removeIndex);
        System.arraycopy(oldList.a, removeIndex + 1, a, removeIndex, oldList.a.length - removeIndex - 1);
        return a;
    }

    private abstract static clreplaced ListenerList<L> {

        final Object[] a;

        protected ListenerList(Object[] a) {
            this.a = a;
        }
    }

    private static clreplaced EventListeners<E> extends ListenerList<DXFeedEventListener<E>> implements DXFeedEventListener<E> {

        EventListeners(Object[] a) {
            super(a);
        }

        @Override
        @SuppressWarnings({ "unchecked" })
        public void eventsReceived(List<E> events) {
            Throwable error = null;
            for (Object listener : a) {
                try {
                    ((DXFeedEventListener<E>) listener).eventsReceived(events);
                } catch (RuntimeException | Error e) {
                    error = e;
                }
            }
            rethrow(error);
        }
    }

    private static clreplaced ChangeListeners extends ListenerList<ObservableSubscriptionChangeListener> implements ObservableSubscriptionChangeListener {

        ChangeListeners(Object[] a) {
            super(a);
        }

        @Override
        public void symbolsAdded(Set<?> symbols) {
            Throwable error = null;
            for (Object listener : a) {
                try {
                    ((ObservableSubscriptionChangeListener) listener).symbolsAdded(symbols);
                } catch (RuntimeException | Error e) {
                    error = e;
                }
            }
            rethrow(error);
        }

        @Override
        public void symbolsRemoved(Set<?> symbols) {
            Throwable error = null;
            for (Object listener : a) {
                try {
                    ((ObservableSubscriptionChangeListener) listener).symbolsRemoved(symbols);
                } catch (RuntimeException | Error e) {
                    error = e;
                }
            }
            rethrow(error);
        }

        @Override
        public void subscriptionClosed() {
            Throwable error = null;
            for (Object listener : a) {
                try {
                    ((ObservableSubscriptionChangeListener) listener).subscriptionClosed();
                } catch (RuntimeException | Error e) {
                    error = e;
                }
            }
            rethrow(error);
        }
    }

    static void rethrow(Throwable error) {
        if (error instanceof RuntimeException)
            throw (RuntimeException) error;
        if (error instanceof Error)
            throw (Error) error;
    }
}

17 Source : SentRequests.java
with Mozilla Public License 2.0
from devexperts

synchronized void addSentRequest(RMIRequestImpl<?> request) {
    if (!request.isNestedRequest()) {
        channelRequests.add(request);
        return;
    }
    Map<Long, IndexedSet<Long, RMIRequestImpl<?>>> map = request.getKind().hasClient() ? clientNestedRequests : serverNestedRequests;
    IndexedSet<Long, RMIRequestImpl<?>> set = map.get(request.getChannelId());
    if (set == null) {
        set = IndexedSet.createLong((IndexerFunction.LongKey<RMIRequestImpl<?>>) RMIRequestImpl::getId);
        map.put(request.getChannelId(), set);
    }
    set.add(request);
}

17 Source : RunningTask.java
with Mozilla Public License 2.0
from devexperts

synchronized Set<RMITaskImpl<?>> removeAllById(long channelId, RMIChannelType type) {
    IndexedSet<Long, RMITaskImpl<?>> set = getMap(type).get(channelId);
    if (set == null || set.isEmpty())
        return null;
    Set<RMITaskImpl<?>> result = new HashSet<>((getMap(type).get(channelId)));
    set.clear();
    return result;
}

17 Source : RunningTask.java
with Mozilla Public License 2.0
from devexperts

// for inner task
synchronized void remove(RMITaskImpl<?> task) {
    replacedert task.isNestedTask();
    IndexedSet<Long, RMITaskImpl<?>> set = getMap(task.getChannel().getType()).get(task.getChannelId());
    if (set == null)
        return;
    set.remove(task);
    if (set.isEmpty())
        getMap(task.getChannel().getType()).remove(task.getChannelId());
}

17 Source : IndexerSerializationTest.java
with Mozilla Public License 2.0
from devexperts

private void deserializeAndCheckObjects(ObjectInputStream ois) throws IOException, ClreplacedNotFoundException {
    Object obj = ois.readObject();
    replacedertTrue(obj instanceof IndexedSet);
    IndexedSet<String, String> set = (IndexedSet<String, String>) obj;
    replacedertEquals(2, set.size());
    replacedertEquals("xxx", set.getByKey("xxx"));
    replacedertEquals("yyy", set.getByKey("yyy"));
    replacedertEquals("com.devexperts.util.Indexer$DefaultIndexer", set.getIndexerFunction().getClreplaced().getName());
}

17 Source : IndexedSetTest.java
with Mozilla Public License 2.0
from devexperts

public void testLongIndexerFunction() {
    IndexedSet<Long, Long> set = IndexedSet.createLong(Long::longValue);
    set.add(1L);
    set.add(2L);
    replacedertTrue(set.containsKey(1L));
    replacedertTrue(set.containsKey(2L));
}

17 Source : IndexedSetTest.java
with Mozilla Public License 2.0
from devexperts

private void doTestBig(IndexedSet<Long, String> is) {
    int cnt = 10000;
    replacedertTrue(is.isEmpty());
    String[] va = new String[cnt];
    for (int k = 0; k < cnt; k++) {
        if (k == 100)
            // just test this method once...
            is.ensureCapacity(600);
        String v = String.valueOf(k);
        va[k] = v;
        replacedertFalse(is.containsKey(k));
        replacedertFalse(is.containsValue(v));
        replacedertTrue(is.add(v));
        replacedertTrue(is.containsKey(k));
        replacedertTrue(is.containsValue(v));
        replacedertEquals(k + 1, is.size());
        replacedertFalse(is.isEmpty());
    }
    String[] a = is.toArray(new String[is.size()]);
    replacedertEquals(cnt, a.length);
    replacedertTrue(Arrays.equals(scopy(a), scopy(va)));
    replacedertTrue(checkSerial(is));
    int cnt2 = cnt / 5;
    for (int k = 0; k < cnt - cnt2; k++) {
        String v = String.valueOf(k);
        replacedertTrue(is.containsKey(k));
        replacedertTrue(is.containsValue(v));
        switch(k % 4) {
            case 0:
                replacedertTrue(is.remove(v));
                break;
            case 1:
                replacedertEquals(v, is.removeValue(v));
                break;
            case 2:
                replacedertEquals(v, is.removeKey(k));
                break;
            case 3:
                replacedertEquals(v, is.removeKey((Long) (long) k));
                break;
        }
        replacedertFalse(is.containsKey(k));
        replacedertFalse(is.containsValue(v));
        replacedertEquals(cnt - k - 1, is.size());
        replacedertFalse(is.isEmpty());
    }
    replacedertTrue(checkSerial(is));
    a = is.toArray(new String[is.size()]);
    replacedertEquals(cnt2, a.length);
    replacedertTrue(Arrays.equals(scopy(a), scopy(va, cnt - cnt2, cnt2)));
    IndexedSet<Long, String> is2 = new IndexedSet<>(is);
    is.trimToSize();
    a = is.toArray(new String[is.size()]);
    replacedertEquals(cnt2, a.length);
    replacedertTrue(Arrays.equals(scopy(a), scopy(va, cnt - cnt2, cnt2)));
    is.clear();
    replacedertTrue(is.isEmpty());
    a = is.toArray(new String[is.size()]);
    replacedertEquals(0, a.length);
    // check is2 at the time it was captured
    a = is2.toArray(new String[is2.size()]);
    replacedertEquals(cnt2, a.length);
    replacedertTrue(Arrays.equals(scopy(a), scopy(va, cnt - cnt2, cnt2)));
    replacedertTrue(checkSerial(is2));
    // remove all by divisible by 5 entries from is2
    int cnt3 = 0;
    for (Iterator<Long> it = is2.keyIterator(); it.hasNext(); ) {
        if (it.next() % 5 != 0)
            it.remove();
        else
            cnt3++;
    }
    for (int i = 0; i < cnt; i++) replacedertEquals(i >= cnt - cnt2 && i % 5 == 0, is2.containsKey((Long) (long) i));
    replacedertEquals(cnt3, is2.size());
    replacedertTrue(checkSerial(is2));
}

17 Source : IndexedSetTest.java
with Mozilla Public License 2.0
from devexperts

public void testIntIndexerFunction() {
    IndexedSet<Integer, Integer> set = IndexedSet.createInt(Integer::intValue);
    set.add(1);
    set.add(2);
    replacedertTrue(set.containsKey(1));
    replacedertTrue(set.containsKey(2));
}

17 Source : OrderBookCorrector.java
with Mozilla Public License 2.0
from devexperts

/**
 * Changes the set of subscribed symbols so that it contains just the symbols from the specified collection.
 *
 * @param symbols the collection of symbols.
 */
public void setSymbols(Collection<String> symbols) {
    for (String symbol : symbols) data.putIfAbsentAndGet(new Book(symbol));
    IndexedSet<String, String> set = new IndexedSet<>(symbols);
    for (Iterator<Book> it = data.iterator(); it.hasNext(); ) if (!set.containsKey(it.next().symbol))
        it.remove();
    subscription.setSymbols(symbols);
}

16 Source : RunningTask.java
with Mozilla Public License 2.0
from devexperts

@SuppressWarnings("unchecked")
synchronized void close() {
    // task.cancel invokes runningTask.remove(task), so we need to avoid ConcurrentModificationException
    for (RMIChannelType type : RMIChannelType.values()) {
        IndexedSet<Long, RMITaskImpl<?>>[] mapNestedTasksArray = getMap(type).values().toArray(new IndexedSet[getMap(type).size()]);
        for (IndexedSet<Long, RMITaskImpl<?>> nestedTasks : mapNestedTasksArray) {
            RMITaskImpl<?>[] nestedTasksArray = nestedTasks.toArray(new RMITaskImpl[nestedTasks.size()]);
            for (RMITaskImpl<?> task : nestedTasksArray) task.cancel(RMIExceptionType.DISCONNECTION);
        }
    }
    RMITaskImpl<?>[] channelTasksArray = serverChannelTasks.toArray(new RMITaskImpl[serverChannelTasks.size()]);
    for (RMITaskImpl<?> task : channelTasksArray) task.cancel(RMIExceptionType.DISCONNECTION);
}

16 Source : IndexedSetTest.java
with Mozilla Public License 2.0
from devexperts

public void testBoxedLongKeys() {
    IndexedSet<Long, Long[]> set = IndexedSet.createLong((Long[] value) -> value[0]);
    set.add(new Long[] { 1L });
    set.add(new Long[] { 2L });
    replacedertTrue(set.containsKey(1L));
    replacedertTrue(set.containsKey(2L));
    replacedertTrue(set.containsValue(new Long[] { 1L }));
    replacedertTrue(set.containsValue(new Long[] { 2L }));
}

16 Source : IndexedSetTest.java
with Mozilla Public License 2.0
from devexperts

public void testIndexerFunction() {
    IndexedSet<Clreplaced<?>, Object> set = IndexedSet.create(Object::getClreplaced);
    set.add("HABA");
    set.add(1);
    replacedertTrue(set.containsKey(String.clreplaced));
    replacedertTrue(set.containsKey(Integer.clreplaced));
}

16 Source : IndexedSetTest.java
with Mozilla Public License 2.0
from devexperts

private void doTestCollector(Clreplaced setClreplaced, Collector<Object, ?, ? extends IndexedSet<?, Object>> collector) {
    List<Object> list = new ArrayList<>();
    for (int i = 0; i < 100; i++) list.add(new Object());
    IndexedSet<?, Object> set = list.stream().collect(collector);
    replacedertTrue(set.getClreplaced() == setClreplaced);
    replacedertEquals(new HashSet<>(list), new HashSet<>(set));
}

16 Source : IndexedSetTest.java
with Mozilla Public License 2.0
from devexperts

public void testBoxedLongs() {
    IndexedSet<Long, Long> set = new IndexedSet<>();
    set.add(1L);
    set.add(2L);
    replacedertTrue(set.containsKey(1L));
    replacedertTrue(set.containsKey(2L));
    replacedertTrue(set.containsValue(1L));
    replacedertTrue(set.containsValue(2L));
}

16 Source : DXFeedSubscription.java
with Mozilla Public License 2.0
from devexperts

private synchronized void setSymbolsImpl(IndexedSet<Object, Object> added) {
    IndexedSet<Object, Object> removed = IndexedSet.create(eventSymbolIndexer);
    for (Iterator<Object> it = symbols.iterator(); it.hasNext(); ) {
        Object oldSymbol = it.next();
        if (!added.containsValue(oldSymbol)) {
            it.remove();
            removed.add(oldSymbol);
        }
    }
    addAndNotify(added, removed);
}

See More Examples