Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
///////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2010-2022 60East Technologies Inc., All Rights Reserved.
//
// This computer software is owned by 60East Technologies Inc. and is
// protected by U.S. copyright laws and other laws and by international
// treaties. This computer software is furnished by 60East Technologies
// Inc. pursuant to a written license agreement and may be used, copied,
// transmitted, and stored only in accordance with the terms of such
// license agreement and with the inclusion of the above copyright notice.
// This computer software or any other copies thereof may not be provided
// or otherwise made available to any other person.
//
// U.S. Government Restricted Rights. This computer software: (a) was
// developed at private expense and is in all respects the proprietary
// information of 60East Technologies Inc.; (b) was not developed with
// government funds; (c) is a trade secret of 60East Technologies Inc.
// for all purposes of the Freedom of Information Act; and (d) is a
// commercial item and thus, pursuant to Section 12.212 of the Federal
// Acquisition Regulations (FAR) and DFAR Supplement Section 227.7202,
// Government's use, duplication or disclosure of the computer software
// is subject to the restrictions set forth by 60East Technologies Inc..
//
////////////////////////////////////////////////////////////////////////////
package com.crankuptheamps.client;
import java.beans.ExceptionListener;
import java.lang.Iterable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import com.crankuptheamps.client.fields.Field;
import com.crankuptheamps.client.fields.BookmarkField;
/**
* An implementation of the {@link RecoveryPointAdapter} interface that is meant
* to act as a wrapper around another recovery point adapter instance,
* delegating periodic updates to its wrapped adapter on an asynchronous
* update thread (which this instance starts). The intent is to decouple
* recovery state updates from message discards on a bookmark replay
* subscriptions so that fast paced subscriptions don't cause a rate of
* updates that consume too much bandwidth or overwhelm recovery point
* adapter implementations that use slower storage (slowing message
* discards).
*/
public class ConflatingRecoveryPointAdapter implements RecoveryPointAdapter
{
/**
* The wrapped recovery point adapter instance we delegate to.
*/
protected RecoveryPointAdapter _adapter = null;
/**
* A concurrent hash map of subscription id's (known to this adapter)
* mapped to Long's that count how many updates have
* occurred for that subscription.
*/
protected final ConcurrentHashMap _counts =
new ConcurrentHashMap();
/**
* A concurrent hash map of subscription id's (known to this adapter)
* mapped to Long's that represent the timestamp of when
* the subscription's state was last written to the adapter.
*/
protected final ConcurrentHashMap _timers =
new ConcurrentHashMap();
/**
* A concurrent hash map of subscription id's mapped to the RecoveryPoint
* received from the most recent update.
*/
protected final ConcurrentHashMap _latestUpdates =
new ConcurrentHashMap();
/**
* The threshold used to determine if a subscription is due for an update
* operation, based upon whether the subscription's update count exceeds
* this threshold since the last update operation.
*/
protected volatile long _updateThreshold = 10;
/**
* The threshold used to determine if a subscription is due for an update
* operation, based upon whether this many milliseconds have elapsed
* since the last update operation.
*/
protected volatile long _timeoutMillis = 2000L;
/**
* The threshold for idle time between updates when timers should
* be checked again.
*/
protected volatile long _updateIntervalMillis = 2000L;
/**
* The background worker thread that persists subscription discard state
* to the SOW.
*/
protected UpdateThread _thread = null;
/**
* Indicates whether this bookmark store has been closed.
*/
protected volatile boolean _closed = false;
/**
* Indicates whether all updates should be flushed regardless.
*/
protected volatile boolean _updateAll = false;
/**
* The exception listener for this client and both underlying HAClient
* instances.
*/
protected volatile ExceptionListener _exceptionListener = null;
private final Lock lock= new ReentrantLock();
private final Condition _updatesReady = lock.newCondition();
/**
* Constructs an instance of the recovery point adapter interface that is
* meant to act as a wrapper around another recovery point adapter instance,
* delegating periodic updates to its wrapped adapter on an asynchronous
* update thread (which this instance starts). The intent is to decouple
* recovery state updates from message discards on a bookmark replay
* subscriptions so that fast paced subscriptions don't cause a rate of
* updates that consume too much bandwidth or overwhelm recovery point
* adapter implementations that use slower storage (slowing message
* discards).
*
* The update threshold is set to default of 10.
* The update timeout is set to default 2,000 milliseconds.
* The update interval, at which the update thread will check for updates,
* is set to the default of 2,000 milliseconds
* To use any non-default values, use the other constructor.
*
* @param adapter The adapter that is sent conflated updates.
*/
public ConflatingRecoveryPointAdapter(RecoveryPointAdapter adapter) {
_adapter = adapter;
// Start the worker to asynchronously handle updates
_thread = new UpdateThread(
"ConflatingRecoveryPointAdapter" + CommandId.nextIdentifier());
_thread.start();
}
/**
* Constructs an instance of the recovery point adapter interface that is
* meant to act as a wrapper around another recovery point adapter instance,
* delegating periodic updates to its wrapped adapter on an asynchronous
* update thread (which this instance starts). The intent is to decouple
* recovery state updates from message discards on a bookmark replay
* subscriptions so that fast paced subscriptions don't cause a rate of
* updates that consume too much bandwidth or overwhelm recovery point
* adapter implementations that use slower storage (slowing message
* discards).
*
* @param adapter The adapter that is sent conflated updates.
* @param updateThreshold The maximum number of updates to a subId
* before a conflated update is delivered.
* @param timeoutMillis The maximum amount of time in milliseconds between
* conflated updates for each subId.
* @param updateIntervalMillis The maximum amount of time in milliseconds
* the update thread can sit idle before checking for timeouts.
*/
public ConflatingRecoveryPointAdapter(RecoveryPointAdapter adapter,
long updateThreshold,
long timeoutMillis,
long updateIntervalMillis) {
_adapter = adapter;
_updateThreshold = updateThreshold;
_timeoutMillis = timeoutMillis;
_updateIntervalMillis = updateIntervalMillis;
// Start the worker to asynchronously handle updates
_thread = new UpdateThread(
"ConflatingRecoveryPointAdapter" + CommandId.nextIdentifier());
_thread.start();
}
/**
* Sets the {@link java.beans.ExceptionListener} instance used for
* communicating absorbed exceptions.
*
* @param exceptionListener The exception listener instance to invoke for
* internal exceptions.
*/
public void setExceptionListener(ExceptionListener exceptionListener) {
this._exceptionListener = exceptionListener;
}
/**
* Returns the {@link java.beans.ExceptionListener} instance used for
* communicating absorbed exceptions.
*
* @return The current exception listener.
*/
public ExceptionListener getExceptionListener() {
return this._exceptionListener;
}
/**
* Confalte an update to the newest recovery point.
* @param recoveryPoint The newest recovery point to save.
*/
public void update(RecoveryPoint recoveryPoint) {
RecoveryPoint rpCopy = recoveryPoint.copy();
Field subId = recoveryPoint.getSubId();
lock.lock();
try {
// If it's a repeat subid
if (_latestUpdates.replace(subId, rpCopy) != null) {
long count = _counts.get(subId);
_counts.put(subId, ++count);
if (count >= _updateThreshold) {
_updatesReady.signalAll();
}
}
else { // New sub id
_latestUpdates.put(subId.copy(), rpCopy);
_counts.put(subId.copy(), 1L);
if (_timeoutMillis != 0) {
_timers.put(subId.copy(), System.currentTimeMillis());
}
}
}
finally {
lock.unlock();
}
}
/**
* Remove all recovery point information from self and adapter.
*/
public void purge() throws Exception {
lock.lock();
try {
if (_adapter != null) {
runUpdateAll();
_adapter.purge();
}
_latestUpdates.clear();
_counts.clear();
_timers.clear();
}
finally {
lock.unlock();
}
}
/**
* Remove all recovery point information for subId from self and adapter.
*
* @param subId The subId to remove.
*/
public void purge(Field subId) throws Exception {
lock.lock();
try {
if (_adapter != null) {
runUpdateAll();
_adapter.purge(subId);
}
_latestUpdates.remove(subId);
_counts.remove(subId);
_timers.remove(subId);
}
finally {
lock.unlock();
}
}
/**
* Force all updates to the underlying adapter, then close it.
*/
public void close() throws Exception {
lock.lock();
try {
if (!_closed) {
runUpdateAll();
_closed = true;
_updatesReady.signalAll();
}
}
finally {
lock.unlock();
}
if (_thread != null) {
_thread.join();
_thread = null;
}
if (_adapter != null) {
_adapter.close();
}
}
/**
* Implements {@link Iterator#hasNext}.
*/
public boolean hasNext()
{
return _adapter.hasNext();
}
/**
* Implements {@link Iterator#next} for a message stream.
*/
public RecoveryPoint next()
{
return _adapter.next();
}
/**
* Operation not supported. Implements {@link Iterator#remove} to throw UnsupportedOperationException.
*/
public void remove()
{
throw new UnsupportedOperationException();
}
/**
* Implements {@link Iterable#iterator} to return this instance.
*/
public Iterator iterator()
{
return this;
}
/**
* Force all held updates to be flushed to the underlying adapter.
*/
public void updateAll() {
lock.lock();
try {
runUpdateAll();
}
finally {
lock.unlock();
}
}
// Lock must already be held
protected void runUpdateAll() {
_updateAll = true;
while (!_counts.isEmpty()) {
_updatesReady.signalAll();
try {
// Give up the lock for update thread, looping in case
// it missed the signal.
_updatesReady.await(250, TimeUnit.MILLISECONDS);
}
catch (InterruptedException e) {
if (_exceptionListener != null) {
_exceptionListener.exceptionThrown(e);
}
}
}
}
/**
* Internal thread for asynchronously delegating recovery state updates
* to the wrapper recovery point adapter.
*/
protected class UpdateThread extends Thread {
protected ArrayList _updates = new ArrayList();
public UpdateThread(String threadName) {
super(threadName);
}
/**
* Entry point for the runnable. Retrieves the next subId from the
* queue and writes an update to the adapter.
*/
public void run() {
boolean closed = _closed;
boolean updateAll;
while (!closed) {
try {
lock.lock();
try {
// Pause here until we're told there are updates or it
// is time to check for delayed updates.
_updatesReady.await(_updateIntervalMillis, TimeUnit.MILLISECONDS);
updateAll = _updateAll;
if (!updateAll) {
long now = System.currentTimeMillis();
for (Map.Entry entry : _timers.entrySet()) {
if (entry.getValue() + _timeoutMillis >= now) {
Field subId = entry.getKey();
RecoveryPoint update = _latestUpdates.get(subId);
if (update != null) {
_latestUpdates.remove(subId);
_updates.add(update);
}
}
}
}
for (Map.Entry entry : _counts.entrySet()) {
if (updateAll
|| entry.getValue() >= _updateThreshold) {
Field subId = entry.getKey();
RecoveryPoint update = _latestUpdates.get(subId);
if (update != null) {
_latestUpdates.remove(subId);
_updates.add(update);
}
}
}
for (RecoveryPoint rp : _updates) {
Field subId = rp.getSubId();
_counts.remove(subId);
_timers.remove(subId);
}
closed = _closed;
}
finally { lock.unlock(); }
if (updateAll) {
lock.lock();
}
try {
for (RecoveryPoint update : _updates) {
_adapter.update(update);
}
}
catch(Exception e) {
if (_exceptionListener != null) {
_exceptionListener.exceptionThrown(e);
}
}
finally {
if (updateAll) {
_updateAll = false;
_updatesReady.signalAll();
lock.unlock();
}
}
_updates.clear();
} catch (Exception e) {
if (_exceptionListener != null) {
_exceptionListener.exceptionThrown(e);
}
}
}
}
}
}