All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.aegisql.conveyor.parallel.ParallelConveyor Maven / Gradle / Ivy

/* 
 * COPYRIGHT (C) AEGIS DATA SOLUTIONS, LLC, 2015
 */
package com.aegisql.conveyor.parallel;

import com.aegisql.conveyor.*;
import com.aegisql.conveyor.cart.*;
import com.aegisql.conveyor.cart.command.GeneralCommand;
import com.aegisql.conveyor.consumers.result.ForwardResult.ForwardingConsumer;
import com.aegisql.conveyor.consumers.result.ResultConsumer;
import com.aegisql.conveyor.consumers.scrap.ScrapConsumer;
import com.aegisql.conveyor.loaders.*;
import com.aegisql.conveyor.meta.ConveyorMetaInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.management.ObjectName;
import java.time.Duration;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.function.*;

import static com.aegisql.conveyor.cart.LoadType.STATIC_PART;

// TODO: Auto-generated Javadoc
/**
 * The Class ParallelConveyor.
 *
 * @author Mikhail Teplitskiy
 * @version 1.0.0
 * @param  the key type
 * @param  the label type
 * @param  the Product type
 */
public abstract class ParallelConveyor implements Conveyor {

	/** The Constant LOG. */
	private final static Logger LOG = LoggerFactory.getLogger(ParallelConveyor.class);

	/** The expiration collection interval. */
	protected long expirationCollectionInterval = 0;
	
	/** The expiration collection unit. */
	protected TimeUnit expirationCollectionUnit = TimeUnit.MILLISECONDS;
	
	/** The builder timeout. */
	protected long builderTimeout = 0;
	
	/** The on timeout action. */
	protected Consumer> timeoutAction;
	
	/** The running. */
	protected volatile boolean running = true;
	
	protected volatile boolean suspended = false;
	
	protected volatile CompletableFuture conveyorFuture = null;
	
	private final Object conveyorFutureLock = new Object();
	

	/** The conveyors. */
	protected final List> conveyors = new ArrayList<>();

	/** The pf. */
	protected int pf;
	
	/** The balancing command. */
	protected Function, List>> balancingCommand;
	
	/** The balancing cart. */
	protected Function, List>> balancingCart;

	/** The name. */
	protected String name = "ParallelConveyor"+Thread.currentThread().threadId();

	/** The l balanced. */
	protected boolean lBalanced = false;

	/** The accepted labels. */
	private final Set acceptedLabels = new HashSet<>();

	/** The forwarding results. */
	protected boolean forwardingResults = false;
	
	/** The object name. */
	private ObjectName objectName;
	
	/** The builder supplier. */
	protected BuilderSupplier builderSupplier = () -> {
		throw new IllegalStateException("Builder Supplier is not set");
	};
	
	/** The result consumer. */
	protected ResultConsumer  resultConsumer = null;

	private ScrapConsumer scrapConsumer = null;
	
	/**
	 * Instantiates a new parallel conveyor.
	 */
	protected ParallelConveyor() {
		this.setMbean(name);
	}
	
	
	@Override
	public PartLoader part() {
		return new PartLoader<>(cl -> {
            ShoppingCart cart = new ShoppingCart<>(cl.key, cl.partValue, cl.label, cl.creationTime, cl.expirationTime, cl.priority);
            cl.getAllProperties().forEach(cart::addProperty);
            return place(cart);
        });
	}
	
	@Override
	public StaticPartLoader staticPart() {
		return new StaticPartLoader<>(cl -> {
            Map properties = new HashMap<>();
            properties.put("CREATE", cl.create);
            Cart staticPart = new ShoppingCart<>(null, cl.staticPartValue, cl.label, System.currentTimeMillis(), 0, properties, STATIC_PART, cl.priority);
            cl.getAllProperties().forEach(staticPart::addProperty);
            return place(staticPart);
        });
	}

	
	@Override
	public BuilderLoader build() {
		return new BuilderLoader<>(cl -> {
            BuilderSupplier bs = cl.value;
            if (bs == null) {
                bs = builderSupplier;
            }
            CreatingCart cart = new CreatingCart<>(cl.key, bs, cl.creationTime, cl.expirationTime, cl.priority);
            cl.getAllProperties().forEach(cart::addProperty);
            return createBuildWithCart(cart);
        }, cl -> {
            BuilderSupplier bs = cl.value;
            if (bs == null) {
                bs = builderSupplier;
            }
            return createBuildFutureWithCart(supplier -> new CreatingCart<>(cl.key, supplier, cl.creationTime, cl.expirationTime, cl.getAllProperties(), cl.priority), bs);//builderSupplier);
        });
	}

	@Override
	public FutureLoader future() {
		return new FutureLoader<>(cl -> getFutureByCart(new FutureCart<>(cl.key, new CompletableFuture<>(), cl.creationTime, cl.expirationTime, cl.getAllProperties(), cl.priority)));
	}

	
	/**
	 * Instantiates a new parallel conveyor.
	 *
	 * @param  the value type
	 * @param cart the cart
	 * @return the completable future
	 */
	
	/* (non-Javadoc)
	 * @see com.aegisql.conveyor.Conveyor#addCommand(com.aegisql.conveyor.Cart)
	 */
	@Override
	public abstract  CompletableFuture command(GeneralCommand cart);
	
	@Override
	public CommandLoader command() {
		return new CommandLoader<>(this::command);
	}
	
	/* (non-Javadoc)
	 * @see com.aegisql.conveyor.Conveyor#add(com.aegisql.conveyor.Cart)
	 */
	@Override
	public abstract  CompletableFuture place(Cart cart);

	/**
	 * Creates the build future.
	 *
	 * @param cartSupplier the cart supplier
	 * @return the completable future
	 */
	protected CompletableFuture createBuildFuture(Function, CreatingCart> cartSupplier) {
		return createBuildFutureWithCart(cartSupplier,builderSupplier);
	}
	
	
	/**
	 * Creates the build with cart.
	 *
	 * @param  the value type
	 * @param cart the cart
	 * @return the completable future
	 */
	protected abstract  CompletableFuture createBuildWithCart(Cart cart);

	/**
	 * Creates the build future with cart.
	 *
	 * @param cartSupplier the cart supplier
	 * @param builderSupplier the builder supplier
	 * @return the completable future
	 */
	protected abstract CompletableFuture createBuildFutureWithCart(Function, CreatingCart> cartSupplier, BuilderSupplier builderSupplier);

	/**
	 * Gets the future by cart.
	 *
	 * @param futureCart the future cart
	 * @return the future by cart
	 */
	protected abstract CompletableFuture getFutureByCart(FutureCart futureCart);
		
	/**
	 * Gets the number of conveyors.
	 *
	 * @return the number of conveyors
	 */
	public int getNumberOfConveyors() {
		return conveyors.size();
	}
	
	/**
	 * Gets the collector size.
	 *
	 * @param idx the idx
	 * @return the collector size
	 */
	public int getCollectorSize(int idx) {
		if(idx < 0 || idx >= pf) {
			return 0;
		} else {
			return conveyors.get(idx).getCollectorSize();
		}
	}

	/**
	 * Gets the input queue size.
	 *
	 * @param idx the idx
	 * @return the input queue size
	 */
	public int getInputQueueSize(int idx) {
		if(idx < 0 || idx >= pf) {
			return 0;
		} else {
			return conveyors.get(idx).getInputQueueSize();
		}
	}

	/**
	 * Gets the delayed queue size.
	 *
	 * @param idx the idx
	 * @return the delayed queue size
	 */
	public int getDelayedQueueSize(int idx) {
		if(idx < 0 || idx >= pf) {
			return 0;
		} else {
			return conveyors.get(idx).getDelayedQueueSize();
		}
	}

	/**
	 * Stop.
	 */
	public void stop() {
		this.running = false;
		this.conveyors.forEach(Conveyor::stop);
	}

	/**
	 * Complete tasks and stop.
	 */
	@Override
	public CompletableFuture completeAndStop() {
		if(this.conveyorFuture == null) {
			synchronized (this.conveyorFutureLock) {
				if(this.conveyorFuture == null) {
					this.conveyorFuture = new CompletableFuture<>();
					this.conveyorFuture.complete(true);
					for(Conveyor c:conveyors) {
						CompletableFuture f = c.completeAndStop();
						this.conveyorFuture = this.conveyorFuture.thenCombine(f, (a,b)-> a && b);
					}
				}
			}
		}
		return this.conveyorFuture;
	}
	
	/**
	 * Gets the expiration collection interval.
	 *
	 * @return the expiration collection interval
	 */
	public long getExpirationCollectionIdleInterval() {
		return expirationCollectionInterval;
	}
	
	/**
	 * Gets the expiration collection idle time unit.
	 *
	 * @return the expiration collection idle time unit
	 */
	public TimeUnit getExpirationCollectionIdleTimeUnit() {
		return expirationCollectionUnit;
	}

	/**
	 * Sets the expiration collection interval.
	 *
	 * @param expirationCollectionInterval the expiration collection interval
	 * @param unit the unit
	 */
	public void setIdleHeartBeat(long expirationCollectionInterval, TimeUnit unit) {
		this.expirationCollectionInterval = expirationCollectionInterval;
		this.expirationCollectionUnit = unit;
		this.conveyors.forEach(conv->conv.setIdleHeartBeat(expirationCollectionInterval,unit));
	}

	/**
	 * Gets the builder timeout.
	 *
	 * @return the builder timeout
	 */
	public long getDefaultBuilderTimeout() {
		return builderTimeout;
	}

	/**
	 * Sets the builder timeout.
	 *
	 * @param builderTimeout the builder timeout
	 * @param unit the unit
	 */
	public void setDefaultBuilderTimeout(long builderTimeout, TimeUnit unit) {
		this.builderTimeout = unit.toMillis(builderTimeout);
		this.conveyors.forEach(conv->conv.setDefaultBuilderTimeout(builderTimeout,unit));
	}
	
	@Override
	public void setDefaultBuilderTimeout(Duration duration) {
		this.setDefaultBuilderTimeout(duration.toMillis(), TimeUnit.MILLISECONDS);
	}

	/**
	 * Reject unexpireable carts older than.
	 *
	 * @param timeout the timeout
	 * @param unit the unit
	 */
	public void rejectUnexpireableCartsOlderThan(long timeout, TimeUnit unit) {
		this.conveyors.forEach(conv->conv.rejectUnexpireableCartsOlderThan(timeout,unit));
	}

	
	/**
	 * Checks if is on timeout action.
	 *
	 * @return true, if is on timeout action
	 */
	public boolean isOnTimeoutAction() {
		return timeoutAction != null;
	}

	/**
	 * Sets the on timeout action.
	 *
	 * @param timeoutAction the new on timeout action
	 */
	public void setOnTimeoutAction(Consumer> timeoutAction) {
		this.timeoutAction = timeoutAction;
		this.conveyors.forEach(conv->conv.setOnTimeoutAction(timeoutAction));
	}

	/**
	 * Sets the cart consumer.
	 *
	 * @param cartConsumer the cart consumer
	 */
	public > void setDefaultCartConsumer(LabeledValueConsumer cartConsumer) {
		this.conveyors.forEach(conv->conv.setDefaultCartConsumer(cartConsumer));
	}

	/**
	 * Sets the readiness evaluator.
	 *
	 * @param ready the ready
	 */
	public void setReadinessEvaluator(BiPredicate, Supplier> ready) {
		this.conveyors.forEach(conv->conv.setReadinessEvaluator(ready));
	}

	/**
	 * Sets the readiness evaluator.
	 *
	 * @param readiness the ready
	 */
	public void setReadinessEvaluator(Predicate> readiness) {
		this.conveyors.forEach(conv->conv.setReadinessEvaluator(readiness));
	}

	/**
	 * Sets the builder supplier.
	 *
	 * @param builderSupplier the new builder supplier
	 */
	public void setBuilderSupplier(BuilderSupplier builderSupplier) {
		this.builderSupplier = builderSupplier;
		this.conveyors.forEach(conv->conv.setBuilderSupplier(builderSupplier));
	}

	/**
	 * Sets the name.
	 *
	 * @param name the new name
	 */
	public void setName(String name) {
		String oldName = this.name;
		this.name = name;
		try {
			//Forget old name
			Conveyor.unRegister(oldName);
		} catch (Exception e) {
			//Ignore. Might be already unregistered
		}
		this.setMbean(name);
		int i = 0;
		for(Conveyor conv: conveyors) {
			conv.setName(name+" ["+i+"]");
			i++;
		}
	}
	
	/**
	 * Checks if is running.
	 *
	 * @return true, if is running
	 */
	public boolean isRunning() {
		return running;
	}

	/**
	 * Checks if is running.
	 *
	 * @param idx the idx
	 * @return true, if is running
	 */
	public boolean isRunning(int idx) {
		if(idx < 0 || idx >= pf) {
			return false;
		} else {
			return conveyors.get(idx).isRunning();
		}
	}
	
	/* (non-Javadoc)
	 * @see com.aegisql.conveyor.Conveyor#addCartBeforePlacementValidator(java.util.function.Consumer)
	 */
	//Think about consequences
	public void addCartBeforePlacementValidator(Consumer> cartBeforePlacementValidator) {
		if(cartBeforePlacementValidator != null) {
			this.conveyors.forEach(conv->conv.addCartBeforePlacementValidator(cartBeforePlacementValidator));
		}
	}


	/* (non-Javadoc)
	 * @see com.aegisql.conveyor.Conveyor#addBeforeKeyEvictionAction(java.util.function.Consumer)
	 */
	@Override
	public void addBeforeKeyEvictionAction(Consumer> keyBeforeEviction) {
		if(keyBeforeEviction != null) {
			this.conveyors.forEach(conv->conv.addBeforeKeyEvictionAction(keyBeforeEviction));
		}
	}

	/* (non-Javadoc)
	 * @see com.aegisql.conveyor.Conveyor#addBeforeKeyReschedulingAction(java.util.function.BiConsumer)
	 */
	public void addBeforeKeyReschedulingAction(BiConsumer keyBeforeRescheduling) {
		if(keyBeforeRescheduling != null) {
			this.conveyors.forEach(conv->conv.addBeforeKeyReschedulingAction(keyBeforeRescheduling));
		}
	}

	/* (non-Javadoc)
	 * @see com.aegisql.conveyor.Conveyor#getExpirationTime(java.lang.Object)
	 */
	public long getExpirationTime(K key) {
		if(!lBalanced) {
			return this.conveyors.get(0).getExpirationTime(key);
		} else {
			throw new RuntimeException("Method cannot be called for L-Balanced conveyor '"+name+"'. Use getExpirationTime(K key, L label)");
		}
	}

	/* (non-Javadoc)
	 * @see com.aegisql.conveyor.Conveyor#getCollectorSize()
	 */
	@Override
	public int getCollectorSize() {
		return -1;
	}

	/* (non-Javadoc)
	 * @see com.aegisql.conveyor.Conveyor#getInputQueueSize()
	 */
	@Override
	public int getInputQueueSize() {
		return -1;
	}

	/* (non-Javadoc)
	 * @see com.aegisql.conveyor.Conveyor#getDelayedQueueSize()
	 */
	@Override
	public int getDelayedQueueSize() {
		return -1;
	}

	/**
	 * Sets the balancing command algorithm.
	 *
	 * @param balancingCommand the balancing command
	 */
	public void setBalancingCommandAlgorithm(Function, List>> balancingCommand) {
		this.balancingCommand = balancingCommand;
	}

	/**
	 * Sets the balancing cart algorithm.
	 *
	 * @param balancingCart the balancing cart
	 */
	public void setBalancingCartAlgorithm(Function, List>> balancingCart) {
		this.balancingCart = balancingCart;
	}

	/* (non-Javadoc)
	 * @see com.aegisql.conveyor.Conveyor#isLBalanced()
	 */
	@Override
	public boolean isLBalanced() {
		return lBalanced;
	}

	/* (non-Javadoc)
	 * @see com.aegisql.conveyor.Conveyor#getAcceptedLabels()
	 */
	@Override
	public Set getAcceptedLabels() {
		return acceptedLabels ;
	}

	/* (non-Javadoc)
	 * @see com.aegisql.conveyor.Conveyor#acceptLabels(java.lang.Object[])
	 */
	@Override
	public void acceptLabels(L... labels) {
		if(labels != null && labels.length > 0) {
			acceptedLabels.addAll(Arrays.asList(labels));
			acceptedLabels.add(null);
			this.addCartBeforePlacementValidator(cart->{
				if( ! acceptedLabels.contains(cart.getLabel())) {
					throw new IllegalStateException("Parallel Conveyor '"+this.name+"' cannot process label '"+cart.getLabel()+"'");					
				}
			});
			conveyors.forEach(c->c.acceptLabels(labels));
			lBalanced = true;
			
		}		
	}

	/* (non-Javadoc)
	 * @see com.aegisql.conveyor.Conveyor#getName()
	 */
	@Override
	public String getName() {
		return name;
	}

	/* (non-Javadoc)
	 * @see java.lang.Object#toString()
	 */
	@Override
	public String toString() {
		return "ParallelConveyor [name=" + name + ", pf=" + pf + ", lBalanced=" + lBalanced + "]";
	}
	
	/* (non-Javadoc)
	 * @see com.aegisql.conveyor.Conveyor#enablePostponeExpiration(boolean)
	 */
	@Override
	public void enablePostponeExpiration(boolean flag) {
		this.conveyors.forEach(conv -> conv.enablePostponeExpiration(flag));
	}

	
	
	/* (non-Javadoc)
	 * @see com.aegisql.conveyor.Conveyor#enablePostponeExpirationOnTimeout(boolean)
	 */
	@Override
	public void enablePostponeExpirationOnTimeout(boolean flag) {
		this.conveyors.forEach(conv -> conv.enablePostponeExpirationOnTimeout(flag));
	}

	/* (non-Javadoc)
	 * @see com.aegisql.conveyor.Conveyor#setExpirationPostponeTime(long, java.util.concurrent.TimeUnit)
	 */
	@Override
	public void setExpirationPostponeTime(long time, TimeUnit unit) {
		this.conveyors.forEach(conv -> conv.setExpirationPostponeTime(time, unit));	
	}

	/* (non-Javadoc)
	 * @see com.aegisql.conveyor.Conveyor#isForwardingResults()
	 */
	@Override
	public boolean isForwardingResults() {
		return forwardingResults;
	}

	/**
	 * Sets the mbean.
	 *
	 * @param name the new mbean
	 */
	protected void setMbean(String name) {
			final ParallelConveyor thisConv = this;

			Conveyor.register(this, new ParallelConveyorMBean() {
				@Override
				public String getName() {
					return name;
				}

				@Override
				public String getGenericName() {
					return thisConv.getGenericName();
				}

				@Override
				public String getType() {
					return thisConv.getClass().getSimpleName();
				}
				@Override
				public int getInnerConveyorsCount() {
					return conveyors.size();
				}
				@Override
				public boolean isRunning() {
					return thisConv.running;
				}
				@Override
				public  Conveyor conveyor() {
					return (Conveyor) thisConv;
				}
				@Override
				public void stop() {
					thisConv.stop();
				}
				@Override
				public void completeAndStop() {
					thisConv.completeAndStop();
				}
				@Override
				public void idleHeartBeatMsec(long msec) {
					if(msec > 0) {
						thisConv.setIdleHeartBeat(msec, TimeUnit.MILLISECONDS);
					}					
				}

				@Override
				public void defaultBuilderTimeoutMsec(long msec) {
					if(msec > 0) {
						thisConv.setDefaultBuilderTimeout(msec, TimeUnit.MILLISECONDS);
					}
				}

				@Override
				public void rejectUnexpireableCartsOlderThanMsec(long msec) {
					if(msec > 0) {
						thisConv.rejectUnexpireableCartsOlderThan(msec, TimeUnit.MILLISECONDS);
					}
				}

				@Override
				public void expirationPostponeTimeMsec(long msec) {
					if(msec > 0) {
						thisConv.setExpirationPostponeTime(msec, TimeUnit.MILLISECONDS);
					}					
				}
				@Override
				public boolean isSuspended() {
					return thisConv.suspended;
				}
				@Override
				public void suspend() {
					thisConv.suspend();
				}
				@Override
				public void resume() {
					thisConv.resume();
				}
			});
	}
	
	@Override
	public long getCartCounter() {
		long counter = 0;
		for(Conveyor c: conveyors) {
			counter += c.getCartCounter();
		}
		return counter;
	}

	public long getCartCounter(int idx) {
		if(idx < 0 || idx >= pf) {
			return 0;
		} else {
			return conveyors.get(idx).getCartCounter();
		}
	}

	@Override
	public void setIdleHeartBeat(Duration duration) {
		this.setIdleHeartBeat(duration.toMillis(),TimeUnit.MILLISECONDS);
	}

	@Override
	public void rejectUnexpireableCartsOlderThan(Duration duration) {
		this.rejectUnexpireableCartsOlderThan(duration.toMillis(),TimeUnit.MILLISECONDS);
	}

	@Override
	public void setExpirationPostponeTime(Duration duration) {
		this.setExpirationPostponeTime(duration.toMillis(),TimeUnit.MILLISECONDS);
	}
	
	@Override
	public ResultConsumerLoader resultConsumer() {
		return new ResultConsumerLoader<>(rcl->{
			Cart cart;
			if(rcl.key != null) {
				cart = new ResultConsumerCart<>(rcl.key, rcl.consumer, rcl.creationTime, rcl.expirationTime, rcl.priority);
			} else {
				cart = new MultiKeyCart<>(rcl.filter, rcl.consumer, null, rcl.creationTime, rcl.expirationTime,  LoadType.RESULT_CONSUMER,rcl.priority);
			}
			
			CompletableFuture f = new CompletableFuture<>();
			f.complete(true);
			
			for(Conveyor conv: conveyors) {
				f = f.thenCombine( conv.place(cart.copy()), (a,b) -> a && b );
			}

			return f;
		}, 
		rc->{
			this.resultConsumer = rc;
			for(Conveyor conv: conveyors) {
				conv.resultConsumer().first(this.resultConsumer).set();
			}
			if(rc instanceof ForwardingConsumer) {
				this.forwardingResults = true;
			}
		}, 
		this.resultConsumer);
	}
	
	@Override
	public ResultConsumerLoader resultConsumer(ResultConsumer consumer) {
		return this.resultConsumer().first(consumer);
	}
	
	public ScrapConsumerLoader scrapConsumer() {
		return new ScrapConsumerLoader<>(sc -> {
            this.scrapConsumer = sc;
            for (Conveyor conv : conveyors) {
                conv.scrapConsumer().first(this.scrapConsumer).set();
            }

        }, scrapConsumer);
	}

	@Override
	public ScrapConsumerLoader scrapConsumer(ScrapConsumer scrapConsumer) {
		return scrapConsumer().first(scrapConsumer);
	}

	@Override
	public void setAutoAcknowledge(boolean auto) {
		for(Conveyor conv: conveyors) {
			conv.setAutoAcknowledge(auto);
		}
	}

	@Override
	public void autoAcknowledgeOnStatus(Status first, Status... other) {
		for(Conveyor conv: conveyors) {
			conv.autoAcknowledgeOnStatus(first,other);
		}
	}


	@Override
	public ResultConsumer getResultConsumer() {
		return resultConsumer;
	}


	@Override
	public void interrupt(final String conveyorName) {
		conveyors.forEach(c->c.interrupt(conveyorName));
	}

	@Override
	public void interrupt(String conveyorName, K key) {
		conveyors.forEach(c->c.interrupt(conveyorName,key));
	}


	@Override
	public void setCartPayloadAccessor(Function, Object> payloadFunction) {
		conveyors.forEach(c->c.setCartPayloadAccessor(payloadFunction));
	}


	@Override
	public void suspend() {
		this.suspended = true;
		conveyors.forEach(Conveyor::suspend);
	}


	@Override
	public void resume() {
		this.suspended = false;
		conveyors.forEach(Conveyor::resume);
	}


	@Override
	public boolean isSuspended() {
		return suspended;
	}

	@Override
	public Class mBeanInterface() {
		return ParallelConveyorMBean.class;
	}

	@Override
	public ConveyorMetaInfo getMetaInfo() {
		return conveyors.get(0).getMetaInfo();
	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy