Please wait. This can take some minutes ...
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.
de.tsl2.nano.vnet.Net Maven / Gradle / Ivy
Go to download
TSL2 Framework VirtualNetwork (Generic parallelized Network-Mechanism providing implementations for: NeuronalNetwork, Routing, Workflow)
/*
* File: $HeadURL$
* Id : $Id$
*
* created by: ts
* created on: 09.11.2012
*
* Copyright: (c) Thomas Schneider 2012, all rights reserved
*/
package de.tsl2.nano.vnet;
import java.io.Serializable;
import java.sql.Time;
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedList;
import java.util.Map;
import java.util.TreeMap;
import java.util.function.Function;
import de.tsl2.nano.bean.BeanFileUtil;
import de.tsl2.nano.core.ENV;
import de.tsl2.nano.core.IPredicate;
import de.tsl2.nano.core.ITransformer;
import de.tsl2.nano.core.ManagedException;
import de.tsl2.nano.core.log.LogFactory;
import de.tsl2.nano.core.messaging.EventController;
import de.tsl2.nano.core.messaging.IListener;
import de.tsl2.nano.core.util.FileUtil;
import de.tsl2.nano.core.util.ListSet;
import de.tsl2.nano.structure.IConnection;
import de.tsl2.nano.structure.INode;
import de.tsl2.nano.util.ActivityGraph;
import de.tsl2.nano.util.GraphLog;
/**
* provides a virtual net with {@link Node}s having {@link Connection}s to several other {@link Node}s. Each
* {@link Node} must be {@link ILocatable} and contains a core object - the element you define by yourself. If you want
* the net to work, you {@link #notify(Notification)} some nodes, where the {@link Notification} is a message with a
* path-expression to address the handlers and a response map to store each response. All connected nodes will be
* informed through the messaging system {@link ThreadingEventController}. All connected nodes must implement the
* {@link IListener} interface to react on notifications and changes.
* the goal is a fast parallel working net without using javas thread synchronization.
*
* the elements/nodes of that net are hold in a {@link TreeMap} - always having the same order.
*
* it is possible to handle each response by adding your implementation to the {@link Notification} object that you give
* to {@link #notify(Notification)}.
*
* the goal of this vnet package is to encapsulate networking and parallelism method techniques from your special
* implementation.
*
* to enable to work on numbers, each node implements the {@link Comparable} interface, delegating to its core (your
* implementation). each connection has a {@link Connection#length()} as number representation (e.g. weight) of itself.
* the default implementation uses the hashCode() of this descriptor object.
*
* @param type of {@link Node} content (=core)
* @param connection descriptor. on simple weighted connections, it would be Float. on complex connections you may
* need connection-properties for both ends.
* @author Thomas Schneider, Thomas Schneider
* @version $Revision$
*/
public class Net & ILocatable & Serializable & Comparable super T>, D extends Comparable super D>> implements Serializable {
private static final long serialVersionUID = -8224858182720810370L;
String name;
/** all net nodes */
Map> elements;
/** using {@link #notifyIdles(Collection, IListener, long, long)}, all waiting sleep times are added */
long waitingCycles = 0;
/** optional function to do styling on textual graph output */
Function graphStyler;
/** normally true, but if you want to work without threading, you can set it to false */
static boolean workParallel = true;
public Net() {
this("vnet-" + Net.class.getSimpleName().toLowerCase());
}
/**
* constructor
*/
public Net(String name) {
this.name = name;
elements = new TreeMap>();
ENV.removeService(NotificationController.class);
}
@SuppressWarnings("rawtypes")
public static Net create(String vnetFile) {
try {
return (Net) FileUtil.loadXml(FileUtil.getFile(vnetFile));
} catch (Exception e) {
ManagedException.forward(e);
return null;
}
}
public void save() {
FileUtil.saveXml(this, ENV.getConfigPath() + FileUtil.getUniqueFileName(getName()) + ".xml");
BeanFileUtil.toFile(elements.values(), ENV.getConfigPath() + FileUtil.getUniqueFileName(getName()) + ".net", BeanFileUtil.FileType.TABSHEET);
}
/**
* addAll
*
* @param cores node cores to add
*/
public void addAll(Collection cores) {
for (T c : cores) {
add(c);
}
}
/**
* add new node
*
* @param newNodeCore new node core to be wrapped into node instance to be added to the net.
* @return new created node
*/
public Node add(T newNodeCore) {
Node node = new Node(newNodeCore, null);
elements.put(node.getPath(), node);
return node;
}
/**
* add the given node and add it to the given source node
*
* @param newNodeCore
* @param connection source node to be connected to
* @param connectionDescriptor connection description
* @return new created node
*/
@SuppressWarnings("unchecked")
public Node addAndConnect(T newNodeCore, T connection, D connectionDescriptor) {
Node node = new Node(newNodeCore,
new ListSet>(new Connection(getNode(connection, true),
connectionDescriptor)));
elements.put(node.getPath(), node);
return node;
}
/**
* TODO: implement and test
*
* crawler starting from root - adding all connections that satisfy addingCondition. crawls the network of nodes
* through all of its connections. you can use the standard {@link Node} implementation or implement your own
* {@link INode} defining its special connections.
*
* @param root INode implementation holding the root object.
* @param addingCondition
* @return all items found through crawling the nodes net.
*/
public ListSet> crawl(INode root, IPredicate> addingCondition) {
ListSet> result = new ListSet>();
if (addingCondition.eval(root))
result.add(root);
for (IConnection c : root.getConnections()) {
result.addAll(crawl(c.getDestination(), addingCondition));
}
return result;
}
/**
* TODO: implement and test
*
* crawler starting from root - transforming all connections through the given transformer. crawls the network of nodes
* through all of its connections. you can use the standard {@link Node} implementation or implement your own
* {@link INode} defining its special connections.
*
* @param root INode implementation holding the root object.
* @param transformer node transformer
* @return transformed root node
*/
public INode crawl(INode root, ITransformer, INode> transformer) {
INode transformed = transformer.transform(root);
for (IConnection c : root.getConnections()) {
crawl(c.getDestination(), transformer);
}
return transformed;
}
/**
* remove
*
* @param nodeCore node to be removed
* @return removed node or null
*/
public Node remove(T nodeCore) {
return elements.remove(getNode(nodeCore).getPath());
}
/**
* notify all nodes that fulfill the path of the given notification.
*
* @param notification notification to be sent to its defined path.
*/
public void notify(Notification notification) {
Collection> nodes = elements.values();
for (Node n : nodes) {
//check, if node n has right path to be notified
if (notification.notify(n)) {
n.notify(notification);
}
}
}
/**
* does the whole job. sends a notification to all dependent net nodes, waits until all nodes are ready, collects all results of all
* notifications and returns a result list.
*
* @param result type
* @param notifications notifications to send
* @param resultType type of notification results
* @return notification results
*/
public Collection notifyAndCollect(Notification notification,
Class resultType
) {
log("==> starting notify and collect on " + notification);
long start = System.currentTimeMillis();
notify(notification);
waitForIdle(-1);
log("<== notifications ended in " + (System.currentTimeMillis() - start)
+ " msecs (waiting cycles: "
+ waitingCycles
+ " msecs)");
return (Collection) (notification.getResponse() != null ? notification.getResponse().values()
: new LinkedList());
}
/**
* notifies only nodes that are idle. this method blocks the current thread until an idle node was found. the given
* responseObserver will be informed, if the working node puts its result to the notification object.
*
* this method should provide the kanban flow pattern.
*
* @param notification notification to send
* @param responseObserver response observer to be informed on any response
* @param timeout timeout for blocking the current thread - searching for the next idle node
* @param waitTime thread sleeping time between iteration
*/
public void notifyFirstIdle(Notification notification,
IListener responseObserver,
int timeout,
int waitTime) {
Collection> nodes = elements.values();
long start = System.currentTimeMillis();
int count = 0;
while (timeout != -1 || System.currentTimeMillis() - start > timeout) {
log_("\ndelegating notification " + notification + " to idle nodes (count: " + count++ + ")...");
for (Node n : nodes) {
if (notification.notify(n) && n.isIdle()) {
n.notify(notification);
return;
}
}
try {
waitingCycles += waitTime;
Thread.sleep(waitTime);
} catch (InterruptedException e) {
ManagedException.forward(e);
}
}
log(this.getClass(), "timeout of " + timeout + " exceeded");
}
/**
* notifyIdles
*
* @param notifications
* @param responseObserver
* @param timeout
* @param waitTime
*/
public void notifyIdles(Collection notifications,
IListener responseObserver,
int timeout,
int waitTime) {
for (Notification notification : notifications) {
notifyFirstIdle(notification, responseObserver, timeout, waitTime);
}
}
/*
* Utility block
*/
/**
* waits until all nodes are idle
*
* @param timeout timeout for blocking the current thread - searching for the next idle node
*/
public void waitForIdle(long timeout) {
Collection> nodes = elements.values();
long start = System.currentTimeMillis();
log_("waiting");
boolean wait;
while (timeout == -1 || System.currentTimeMillis() - start < timeout) {
log_(".");
wait = false;
for (Node n : nodes) {
if (!n.isIdle()) {
wait = true;
break;
}
}
if (!wait) {
log(this.getClass(), "no work left - all nodes are idle");
return;
}
try {
Thread.sleep(200);
} catch (InterruptedException e) {
ManagedException.forward(e);
}
}
log(this.getClass(), "wait timeout of " + timeout + " msecs exceeded");
}
/**
* does the whole job. sends all notifications, waits until all nodes are ready, collects all results of all
* notifications and returns a result list.
*
* @param result type
* @param notifications notifications to send
* @param resultType type of notification results
* @return notification results
*/
@SuppressWarnings("unchecked")
public Collection notifyIdlesAndCollect(Collection notifications,
Class resultType
) {
log("==> starting notify and collect on " + notifications.size() + " notifications");
long start = System.currentTimeMillis();
Collection results = new ArrayList();
notifyIdles(notifications, null, -1, 500);
waitForIdle(-1);
log("<== notifications ended in " + (System.currentTimeMillis() - start)
+ " msecs (waiting cycles: "
+ waitingCycles
+ " msecs)");
log_("collecting results...");
for (Notification n : notifications) {
if (n.getResponse() != null) {
results.addAll((Collection extends R>) n.getResponse().values());
}
}
log_("done\n");
return results;
}
/**
* setWorkParallel
*
* @param workParallel {@link #workParallel}
*/
public void setWorkParallel(boolean workParallel) {
this.workParallel = workParallel;
}
/**
* resetStatistics
*/
public void resetStatistics() {
waitingCycles = 0;
for (Node n : elements.values()) {
n.statistics.reset();
}
}
/**
* log
*
* @param logger logger
* @param text message
*/
static final void log(Class> logger, String text) {
log(logger.getSimpleName() + ": " + text);
}
static void log_(String msg) {
if (LogFactory.isDebugLevel())
System.out.print(msg);
}
static void log(String msg) {
log_(msg + "\n");
}
/**
* searches for the given node inside this net
*
* @param nodeCore nodes core to be searched
* @return found node or null
*/
public Node getNode(T nodeCore) {
return getNode(nodeCore, false);
}
public Node getNode(T nodeCore, boolean createIfNotExists) {
Node d = elements.get(nodeCore.getPath());
if (d == null && createIfNotExists)
return add(nodeCore);
return d;
}
/**
* factory to create event controller. all items of this net have to use this factory method.
*
* @return new event controller
*/
public static EventController createEventController() {
if (workParallel) {
return new NotificationController();
} else {
return new EventController();
}
}
/**
* dumps the properties and working times of all nodes - usable for statistics.
*
* @return net and node descriptions
*/
public String dump() {
graph(graphStyler);
StringBuilder buf = new StringBuilder(30 + 30 * elements.size());
buf.append(toString() + "\n");
buf.append("graph (graphviz) stored in: " + new GraphLog(name).getFileName() + "\n");
Collection> nodes = elements.values();
long totalWorkingTime = 0;
int notifications = 0;
for (Node node : nodes) {
buf.append(node.dump() + "\n");
totalWorkingTime += node.getStatistics().workingTime;
notifications += node.getStatistics().notifications;
}
buf.append("total working time: " + DateFormat.getTimeInstance().format(new Time(totalWorkingTime))
+ " on "
+ notifications
+ " notifications\n");
return buf.toString();
}
public void setGraphStyler(Function graphStyler) {
this.graphStyler = graphStyler;
}
public void graph(Function graphStyler) {
GraphLog.createGraphFile(name, elements.values(), graphStyler);
new ActivityGraph(name).create(elements.values()).write();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return /*new BeanClass(this.getClass()).getGenericType() + */" Net with " + elements.size() + " elements";
}
}