From 756d4cec2e4aa688128e0b18168ac2e92331aa8c Mon Sep 17 00:00:00 2001 From: Tobias Becker Date: Sat, 31 Oct 2015 22:26:04 +0100 Subject: [PATCH 001/161] added a small chat example --- Dream2/src/test/java/javareact/chat/Chat.java | 60 +++++++++++++++++++ .../test/java/javareact/chat/ChatServer.java | 59 ++++++++++++++++++ .../java/javareact/chat/package-info.java | 9 +++ 3 files changed, 128 insertions(+) create mode 100644 Dream2/src/test/java/javareact/chat/Chat.java create mode 100644 Dream2/src/test/java/javareact/chat/ChatServer.java create mode 100644 Dream2/src/test/java/javareact/chat/package-info.java diff --git a/Dream2/src/test/java/javareact/chat/Chat.java b/Dream2/src/test/java/javareact/chat/Chat.java new file mode 100644 index 0000000..2fc2ca2 --- /dev/null +++ b/Dream2/src/test/java/javareact/chat/Chat.java @@ -0,0 +1,60 @@ +package javareact.chat; + +import java.io.BufferedReader; +import java.io.InputStreamReader; + +import javareact.common.Consts; +import javareact.common.types.ReactiveChangeListener; +import javareact.common.types.RemoteVar; +import javareact.common.types.Var; + +public class Chat implements ReactiveChangeListener { + + private RemoteVar remoteMessages; + private Var messages; + private String userName; + + public Chat(String username) throws Exception { + Consts.hostName = username; + messages = new Var("message", ""); + remoteMessages = new RemoteVar("message@*"); + remoteMessages.addReactiveChangeListener(this); + this.userName = username; + } + + protected void writeMessage(String text) { + messages.set(userName + ":" + text); + } + + @Override + public void notifyReactiveChanged(String newValue) { + String[] msg = newValue.split(":", 2); + if (!msg[0].equals(userName)) + System.out.println(newValue); + } + + public static void main(String[] args) { + try { + if (args.length < 1) + System.out.println("username missing"); + + // args[0]=topicName; args[1]=username + Chat chat = new Chat(args[0]); + + // read from command line + BufferedReader commandLine = new java.io.BufferedReader( + new InputStreamReader(System.in)); + + // loop until the word "exit" is typed + while (true) { + String s = commandLine.readLine(); + if (s.equalsIgnoreCase("exit")) { + System.exit(0);// exit program + } else + chat.writeMessage(s); + } + } catch (Exception e) { + e.printStackTrace(); + } + } +} \ No newline at end of file diff --git a/Dream2/src/test/java/javareact/chat/ChatServer.java b/Dream2/src/test/java/javareact/chat/ChatServer.java new file mode 100644 index 0000000..5e17ea4 --- /dev/null +++ b/Dream2/src/test/java/javareact/chat/ChatServer.java @@ -0,0 +1,59 @@ +package javareact.chat; + +import java.util.HashSet; +import java.util.Set; + +import javareact.common.Consts; +import javareact.server.ServerLauncher; +import javareact.token_service.TokenServiceLauncher; + +public class ChatServer { + private boolean serverStarted = false; + private boolean tokenServiceStarted = false; + + public static void main(String[] args) { + new ChatServer().start(); + } + + public void start() { + startServerIfNeeded(); + startTokenServiceIfNeeded(); + + Consts.hostName = "ChatServer"; + + while (true) { + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + + private final void startServerIfNeeded() { + if (!serverStarted) { + ServerLauncher.start(); + serverStarted = true; + } + try { + Thread.sleep(500); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + private final void startTokenServiceIfNeeded() { + if (!tokenServiceStarted) { + String serverAddress = "reds-tcp:localhost:9000"; + Set addresses = new HashSet(); + addresses.add(serverAddress); + TokenServiceLauncher.start(addresses); + tokenServiceStarted = true; + } + try { + Thread.sleep(500); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } +} diff --git a/Dream2/src/test/java/javareact/chat/package-info.java b/Dream2/src/test/java/javareact/chat/package-info.java new file mode 100644 index 0000000..f604716 --- /dev/null +++ b/Dream2/src/test/java/javareact/chat/package-info.java @@ -0,0 +1,9 @@ +/** + * A small example for a Chat Application + * + * Usage: Start {@link javareact.chat.ChatServer ChatServer} first. + * Then start as many instances of {@link javareact.chat.Chat Chat} as you need + * and supply a user name as the first parameter. + * @author Tobias Becker + */ +package javareact.chat; \ No newline at end of file From 027577eeb9122e1a60d9dc6f7f2ad90ab4963fff Mon Sep 17 00:00:00 2001 From: Tobias Becker Date: Tue, 10 Nov 2015 11:50:02 +0100 Subject: [PATCH 002/161] added small swing gui --- Dream2/src/test/java/javareact/chat/Chat.java | 117 ++++++++++++++---- 1 file changed, 96 insertions(+), 21 deletions(-) diff --git a/Dream2/src/test/java/javareact/chat/Chat.java b/Dream2/src/test/java/javareact/chat/Chat.java index 2fc2ca2..cf1e7fa 100644 --- a/Dream2/src/test/java/javareact/chat/Chat.java +++ b/Dream2/src/test/java/javareact/chat/Chat.java @@ -1,36 +1,102 @@ package javareact.chat; +import java.awt.Container; +import java.awt.EventQueue; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.KeyEvent; +import java.awt.event.KeyListener; import java.io.BufferedReader; import java.io.InputStreamReader; +import javax.swing.GroupLayout; +import javax.swing.GroupLayout.SequentialGroup; +import javax.swing.JButton; +import javax.swing.JComponent; +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.JTextArea; +import javax.swing.JTextField; + import javareact.common.Consts; import javareact.common.types.ReactiveChangeListener; import javareact.common.types.RemoteVar; import javareact.common.types.Var; -public class Chat implements ReactiveChangeListener { +public class Chat extends JFrame implements ReactiveChangeListener { private RemoteVar remoteMessages; private Var messages; private String userName; - + private JTextArea msgs; + private JTextField sendText; public Chat(String username) throws Exception { Consts.hostName = username; messages = new Var("message", ""); remoteMessages = new RemoteVar("message@*"); remoteMessages.addReactiveChangeListener(this); this.userName = username; - } + initUI(); + } + + private void initUI() { + sendText = new JTextField(20); + sendText.addKeyListener(new KeyListener() { + + @Override + public void keyTyped(KeyEvent e) { + } + + @Override + public void keyReleased(KeyEvent e) { + } + + @Override + public void keyPressed(KeyEvent e) { + if (e.getKeyCode() == KeyEvent.VK_ENTER) + sendMessage(); + } + }); + JButton sendButton = new JButton("Send"); + sendButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent event) { + sendMessage(); + } + }); + msgs = new JTextArea(5, 20); + msgs.setEditable(false); + JPanel panel = new JPanel(); + panel.add(msgs); + panel.add(sendText); + panel.add(sendButton); + getContentPane().add(panel); + + setTitle("Chat - " + userName); + setSize(300, 200); + setLocationRelativeTo(null); + setDefaultCloseOperation(EXIT_ON_CLOSE); + } + - protected void writeMessage(String text) { - messages.set(userName + ":" + text); + protected void sendMessage() { + messages.set(userName + ":" + sendText.getText()); + displayMessage("You: " + sendText.getText()); + sendText.setText(""); + } + + protected void displayMessage(String text) { + if (msgs.getText().isEmpty()) + msgs.append(text); + else + msgs.append(System.lineSeparator() + text); } @Override public void notifyReactiveChanged(String newValue) { String[] msg = newValue.split(":", 2); if (!msg[0].equals(userName)) - System.out.println(newValue); + displayMessage(msg[0] + ": " +msg[1]); } public static void main(String[] args) { @@ -38,21 +104,30 @@ public static void main(String[] args) { if (args.length < 1) System.out.println("username missing"); - // args[0]=topicName; args[1]=username - Chat chat = new Chat(args[0]); - - // read from command line - BufferedReader commandLine = new java.io.BufferedReader( - new InputStreamReader(System.in)); - - // loop until the word "exit" is typed - while (true) { - String s = commandLine.readLine(); - if (s.equalsIgnoreCase("exit")) { - System.exit(0);// exit program - } else - chat.writeMessage(s); - } + EventQueue.invokeLater(new Runnable() { + + @Override + public void run() { + try { + Chat chat = new Chat(args[0]); + chat.setVisible(true); + } catch (Exception e) { + } + + } + }); +// // read from command line +// BufferedReader commandLine = new java.io.BufferedReader( +// new InputStreamReader(System.in)); +// +// // loop until the word "exit" is typed +// while (true) { +// String s = commandLine.readLine(); +// if (s.equalsIgnoreCase("exit")) { +// System.exit(0);// exit program +// } else +// chat.writeMessage(s); +// } } catch (Exception e) { e.printStackTrace(); } From 517cb876f86ee5d364f2ca63980ba27d97503802 Mon Sep 17 00:00:00 2001 From: Tobias Becker Date: Tue, 10 Nov 2015 14:52:42 +0100 Subject: [PATCH 003/161] added resources with license --- Dream2/.classpath | 1 + Dream2/src/resources/javareact/chat/License.txt | 2 ++ .../src/resources/javareact/chat/status-busy.png | Bin 0 -> 439 bytes .../resources/javareact/chat/status-offline.png | Bin 0 -> 448 bytes .../resources/javareact/chat/status-online.png | Bin 0 -> 433 bytes 5 files changed, 3 insertions(+) create mode 100644 Dream2/src/resources/javareact/chat/License.txt create mode 100644 Dream2/src/resources/javareact/chat/status-busy.png create mode 100644 Dream2/src/resources/javareact/chat/status-offline.png create mode 100644 Dream2/src/resources/javareact/chat/status-online.png diff --git a/Dream2/.classpath b/Dream2/.classpath index 3b42b26..e482da2 100644 --- a/Dream2/.classpath +++ b/Dream2/.classpath @@ -12,6 +12,7 @@ + diff --git a/Dream2/src/resources/javareact/chat/License.txt b/Dream2/src/resources/javareact/chat/License.txt new file mode 100644 index 0000000..741a409 --- /dev/null +++ b/Dream2/src/resources/javareact/chat/License.txt @@ -0,0 +1,2 @@ +Owner: http://p.yusukekamiyamane.com/ +License: http://creativecommons.org/licenses/by/3.0/ \ No newline at end of file diff --git a/Dream2/src/resources/javareact/chat/status-busy.png b/Dream2/src/resources/javareact/chat/status-busy.png new file mode 100644 index 0000000000000000000000000000000000000000..a9d5f4db20d9c8d0b1819a5085889e9f619a9278 GIT binary patch literal 439 zcmV;o0Z9IdP)H(>1n>M=ZqdoN(m;DQ?%H%%AX4Iwqbl!TUoEe?gjk3`cDVZ+KvUdZP? z9{>HBA#of-nN@+3tF%FNw|F2VdC@@EA(kpaM!X|51~3W%+?!0~YR0gE!n?CxPd)GV zC0-QyPtT+Jcs$T2lL7L#2sbwZemp!?6!k<=+P3QorfF6do+li4yKTACQCZqqeg^@z z-E6fa!!Up`29(loBs*0XMJY&Q-4_|iK@{;T+XipBgmAn45J h$Tj$%m+4D@0RRT`l(u2}p>qHL002ovPDHLkV1jjlxO@Nr literal 0 HcmV?d00001 diff --git a/Dream2/src/resources/javareact/chat/status-offline.png b/Dream2/src/resources/javareact/chat/status-offline.png new file mode 100644 index 0000000000000000000000000000000000000000..f148af49853dc39cf36be1e7492f7f72d8d67288 GIT binary patch literal 448 zcmV;x0YCnUP)BbKoq|INE@htgTcfw ziE%WrLN^&CVW@wA{*9Obp^1bPx|qP?;G%J|i3JS=+k=+2_}zdZ#&%)yC12is_wK&D zckdmy*=)!@#*;m5Zvk!sbtyY|_>+j$Xu>$M$ z`n+5&*KFJ7S$BNjXL|%$@x@}XhIKDd02gwoKkqmWaU6<_f}fZqNqH7ed_>Rl+{xr~ zx?F}Qf*=!ytx@VuSv;|gIPh#3#vql-M6#@qEhI^a#^dpT#S=5IENj$i-L)z8MOD32 zv)SCyVln?14tr+5-)+xkv(aw>Zg-SXza$94nW88q_yYI=;tsm=hIT%|zc8L8Aiy26 qdH7@K*o)3_-j5adpP%VlfB^u}=ePcNmHmDI00006PEXtjc^v`i`UeB(mVNH?r}$(PCW_wuEa zH^UGS7O4a*SPKi5XE*G!Kwnsnv~}7VM2YATCJ1Oigf5;w&lgbTK;U_Gr()>adT}MC zDkJXm=2NTI?%vfXKO@}vM-W)AY#kVSp;8W9Uv*xb#Mlc|T{|rAYh^>`u}6RqjlK2V zqGeeigaFR@CvsBR)l5<5)O8Uf5G9^F1naBSD7rnmQ=Qi3ZWPUb| zqcN9x{1`c!-Ny$jsU-n6;F~;vKlCh_#|uo}aNCaQ-8vn?rU7I@Od$&V)?0_)k4%~W z7O>f?%os~So>k#EaRMcFMEXsdzYRUSfopLEuh^vi_c*~4Eo+4G$EAyiA@zrI1^@Fg beF!iBtoNm!wZ Date: Tue, 10 Nov 2015 14:53:07 +0100 Subject: [PATCH 004/161] added indicators for offline/online status --- Dream2/src/test/java/javareact/chat/Chat.java | 107 +++++++++++++++--- 1 file changed, 91 insertions(+), 16 deletions(-) diff --git a/Dream2/src/test/java/javareact/chat/Chat.java b/Dream2/src/test/java/javareact/chat/Chat.java index cf1e7fa..40728b8 100644 --- a/Dream2/src/test/java/javareact/chat/Chat.java +++ b/Dream2/src/test/java/javareact/chat/Chat.java @@ -1,23 +1,21 @@ package javareact.chat; -import java.awt.Container; +import java.awt.Color; import java.awt.EventQueue; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; -import java.io.BufferedReader; -import java.io.InputStreamReader; - -import javax.swing.GroupLayout; -import javax.swing.GroupLayout.SequentialGroup; +import javax.swing.DefaultListCellRenderer; +import javax.swing.DefaultListModel; +import javax.swing.ImageIcon; import javax.swing.JButton; -import javax.swing.JComponent; import javax.swing.JFrame; -import javax.swing.JPanel; +import javax.swing.JLabel; +import javax.swing.JList; import javax.swing.JTextArea; import javax.swing.JTextField; - +import javax.swing.SpringLayout; import javareact.common.Consts; import javareact.common.types.ReactiveChangeListener; import javareact.common.types.RemoteVar; @@ -25,16 +23,20 @@ public class Chat extends JFrame implements ReactiveChangeListener { + private static final long serialVersionUID = 390641070042167681L; private RemoteVar remoteMessages; private Var messages; private String userName; private JTextArea msgs; private JTextField sendText; + private JList statusList; + public Chat(String username) throws Exception { Consts.hostName = username; messages = new Var("message", ""); remoteMessages = new RemoteVar("message@*"); remoteMessages.addReactiveChangeListener(this); + this.userName = username; initUI(); } @@ -64,20 +66,93 @@ public void actionPerformed(ActionEvent event) { sendMessage(); } }); - msgs = new JTextArea(5, 20); + msgs = new JTextArea(5, 27); msgs.setEditable(false); - JPanel panel = new JPanel(); - panel.add(msgs); - panel.add(sendText); - panel.add(sendButton); - getContentPane().add(panel); + msgs.setMaximumSize(null); + + DefaultListModel listModel = new DefaultListModel(); + listModel.addElement("Alice"); + listModel.addElement("Bob"); + statusList = new JList(listModel); + statusList.setEnabled(false); + statusList.setLayoutOrientation(JList.HORIZONTAL_WRAP); + statusList.setVisibleRowCount(-1); + + statusList.setCellRenderer(new DefaultListCellRenderer() { + + private static final long serialVersionUID = 9019815674349211344L; + private JLabel label = new JLabel(); + private Color textSelectionColor = Color.BLACK; + private Color backgroundSelectionColor = Color.CYAN; + private Color textNonSelectionColor = Color.BLACK; + private Color backgroundNonSelectionColor = Color.WHITE; + + @Override + public java.awt.Component getListCellRendererComponent(javax.swing.JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { + + String name = (String)value; + label.setIcon(createImageIcon("status-offline.png", "Offline")); + label.setText(name); + + if (isSelected) { + label.setBackground(backgroundSelectionColor); + label.setForeground(textSelectionColor); + } else { + label.setBackground(backgroundNonSelectionColor); + label.setForeground(textNonSelectionColor); + } + + return label; + }; + + }); + SpringLayout layout = new SpringLayout(); + + // put messages on (5,5) + layout.putConstraint(SpringLayout.WEST, msgs, 5, SpringLayout.WEST, getContentPane()); + layout.putConstraint(SpringLayout.NORTH, msgs, 5, SpringLayout.NORTH, getContentPane()); + + // put textfield below messages + layout.putConstraint(SpringLayout.NORTH, sendText, 5, SpringLayout.SOUTH, msgs); + layout.putConstraint(SpringLayout.WEST, sendText, 5, SpringLayout.WEST, getContentPane()); + + // put button next to the textfield + layout.putConstraint(SpringLayout.NORTH, sendButton, 5, SpringLayout.SOUTH, msgs); + layout.putConstraint(SpringLayout.WEST, sendButton, 5, SpringLayout.EAST, sendText); + + // make the frame big enough to fit all in + layout.putConstraint(SpringLayout.EAST, getContentPane(), 10, SpringLayout.EAST, statusList); + layout.putConstraint(SpringLayout.SOUTH, getContentPane(), 10, SpringLayout.SOUTH, sendText); + + layout.putConstraint(SpringLayout.NORTH, statusList, 5, SpringLayout.NORTH, getContentPane()); + layout.putConstraint(SpringLayout.WEST, statusList, 15, SpringLayout.EAST, sendButton); + + getContentPane().setLayout(layout); + + getContentPane().add(msgs); + getContentPane().add(sendText); + getContentPane().add(sendButton); + getContentPane().add(statusList); setTitle("Chat - " + userName); - setSize(300, 200); +// setSize(300, 200); setLocationRelativeTo(null); setDefaultCloseOperation(EXIT_ON_CLOSE); + + pack(); } + /** Returns an ImageIcon, or null if the path was invalid. */ + protected ImageIcon createImageIcon(String path, + String description) { + java.net.URL imgURL = getClass().getResource(path); + if (imgURL != null) { + return new ImageIcon(imgURL, description); + } else { + System.err.println("Couldn't find file: " + path); + return null; + } + } protected void sendMessage() { messages.set(userName + ":" + sendText.getText()); From 4e7c2fd06f8ae58aeeb012fff8038a4a655ccea7 Mon Sep 17 00:00:00 2001 From: Tobias Becker Date: Thu, 12 Nov 2015 13:07:13 +0100 Subject: [PATCH 005/161] added online status as signal --- Dream2/src/test/java/javareact/chat/Chat.java | 266 ++++++++++-------- 1 file changed, 142 insertions(+), 124 deletions(-) diff --git a/Dream2/src/test/java/javareact/chat/Chat.java b/Dream2/src/test/java/javareact/chat/Chat.java index 40728b8..443d179 100644 --- a/Dream2/src/test/java/javareact/chat/Chat.java +++ b/Dream2/src/test/java/javareact/chat/Chat.java @@ -19,6 +19,7 @@ import javareact.common.Consts; import javareact.common.types.ReactiveChangeListener; import javareact.common.types.RemoteVar; +import javareact.common.types.Signal; import javareact.common.types.Var; public class Chat extends JFrame implements ReactiveChangeListener { @@ -26,140 +27,157 @@ public class Chat extends JFrame implements ReactiveChangeListener { private static final long serialVersionUID = 390641070042167681L; private RemoteVar remoteMessages; private Var messages; + private Var status; private String userName; private JTextArea msgs; private JTextField sendText; private JList statusList; - + public Chat(String username) throws Exception { Consts.hostName = username; messages = new Var("message", ""); remoteMessages = new RemoteVar("message@*"); remoteMessages.addReactiveChangeListener(this); - + + status = new Var("status", true); + RemoteVar remoteStatus = new RemoteVar<>("status@*"); + Signal s = new Signal("s", () -> { + if (remoteStatus.get() == null) + return false; + else + return remoteStatus.get(); + } , remoteStatus); + s.addReactiveChangeListener(new ReactiveChangeListener() { + + @Override + public void notifyReactiveChanged(Boolean newValue) { + System.out.println(newValue); + + } + }); this.userName = username; - initUI(); - } + initUI(); + } + + private void initUI() { + sendText = new JTextField(20); + sendText.addKeyListener(new KeyListener() { - private void initUI() { - sendText = new JTextField(20); - sendText.addKeyListener(new KeyListener() { - @Override public void keyTyped(KeyEvent e) { } - + @Override public void keyReleased(KeyEvent e) { } - + @Override public void keyPressed(KeyEvent e) { if (e.getKeyCode() == KeyEvent.VK_ENTER) - sendMessage(); + sendMessage(); } }); - JButton sendButton = new JButton("Send"); - sendButton.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent event) { - sendMessage(); - } - }); - msgs = new JTextArea(5, 27); - msgs.setEditable(false); - msgs.setMaximumSize(null); - - DefaultListModel listModel = new DefaultListModel(); - listModel.addElement("Alice"); - listModel.addElement("Bob"); - statusList = new JList(listModel); - statusList.setEnabled(false); - statusList.setLayoutOrientation(JList.HORIZONTAL_WRAP); - statusList.setVisibleRowCount(-1); - - statusList.setCellRenderer(new DefaultListCellRenderer() { - + JButton sendButton = new JButton("Send"); + sendButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent event) { + sendMessage(); + } + }); + msgs = new JTextArea(5, 27); + msgs.setEditable(false); + msgs.setMaximumSize(null); + + DefaultListModel listModel = new DefaultListModel(); + listModel.addElement("Alice"); + listModel.addElement("Bob"); + statusList = new JList(listModel); + statusList.setEnabled(false); + statusList.setLayoutOrientation(JList.HORIZONTAL_WRAP); + statusList.setVisibleRowCount(-1); + + statusList.setCellRenderer(new DefaultListCellRenderer() { + private static final long serialVersionUID = 9019815674349211344L; private JLabel label = new JLabel(); - private Color textSelectionColor = Color.BLACK; - private Color backgroundSelectionColor = Color.CYAN; - private Color textNonSelectionColor = Color.BLACK; - private Color backgroundNonSelectionColor = Color.WHITE; - - @Override - public java.awt.Component getListCellRendererComponent(javax.swing.JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { - - String name = (String)value; - label.setIcon(createImageIcon("status-offline.png", "Offline")); - label.setText(name); - - if (isSelected) { - label.setBackground(backgroundSelectionColor); - label.setForeground(textSelectionColor); - } else { - label.setBackground(backgroundNonSelectionColor); - label.setForeground(textNonSelectionColor); - } - - return label; - }; - - }); - SpringLayout layout = new SpringLayout(); - - // put messages on (5,5) - layout.putConstraint(SpringLayout.WEST, msgs, 5, SpringLayout.WEST, getContentPane()); - layout.putConstraint(SpringLayout.NORTH, msgs, 5, SpringLayout.NORTH, getContentPane()); - - // put textfield below messages - layout.putConstraint(SpringLayout.NORTH, sendText, 5, SpringLayout.SOUTH, msgs); - layout.putConstraint(SpringLayout.WEST, sendText, 5, SpringLayout.WEST, getContentPane()); - - // put button next to the textfield - layout.putConstraint(SpringLayout.NORTH, sendButton, 5, SpringLayout.SOUTH, msgs); - layout.putConstraint(SpringLayout.WEST, sendButton, 5, SpringLayout.EAST, sendText); - - // make the frame big enough to fit all in - layout.putConstraint(SpringLayout.EAST, getContentPane(), 10, SpringLayout.EAST, statusList); - layout.putConstraint(SpringLayout.SOUTH, getContentPane(), 10, SpringLayout.SOUTH, sendText); - - layout.putConstraint(SpringLayout.NORTH, statusList, 5, SpringLayout.NORTH, getContentPane()); - layout.putConstraint(SpringLayout.WEST, statusList, 15, SpringLayout.EAST, sendButton); - - getContentPane().setLayout(layout); - - getContentPane().add(msgs); - getContentPane().add(sendText); - getContentPane().add(sendButton); - getContentPane().add(statusList); - - setTitle("Chat - " + userName); -// setSize(300, 200); - setLocationRelativeTo(null); - setDefaultCloseOperation(EXIT_ON_CLOSE); - - pack(); - } - - /** Returns an ImageIcon, or null if the path was invalid. */ - protected ImageIcon createImageIcon(String path, - String description) { - java.net.URL imgURL = getClass().getResource(path); - if (imgURL != null) { - return new ImageIcon(imgURL, description); - } else { - System.err.println("Couldn't find file: " + path); - return null; - } - } + private Color textSelectionColor = Color.BLACK; + private Color backgroundSelectionColor = Color.CYAN; + private Color textNonSelectionColor = Color.BLACK; + private Color backgroundNonSelectionColor = Color.WHITE; + + @Override + public java.awt.Component getListCellRendererComponent(javax.swing.JList list, Object value, int index, + boolean isSelected, boolean cellHasFocus) { + + String name = (String) value; + label.setIcon(createImageIcon("status-offline.png", "Offline")); + label.setText(name); + + if (isSelected) { + label.setBackground(backgroundSelectionColor); + label.setForeground(textSelectionColor); + } else { + label.setBackground(backgroundNonSelectionColor); + label.setForeground(textNonSelectionColor); + } + + return label; + }; + + }); + SpringLayout layout = new SpringLayout(); + + // put messages on (5,5) + layout.putConstraint(SpringLayout.WEST, msgs, 5, SpringLayout.WEST, getContentPane()); + layout.putConstraint(SpringLayout.NORTH, msgs, 5, SpringLayout.NORTH, getContentPane()); + + // put textfield below messages + layout.putConstraint(SpringLayout.NORTH, sendText, 5, SpringLayout.SOUTH, msgs); + layout.putConstraint(SpringLayout.WEST, sendText, 5, SpringLayout.WEST, getContentPane()); + + // put button next to the textfield + layout.putConstraint(SpringLayout.NORTH, sendButton, 5, SpringLayout.SOUTH, msgs); + layout.putConstraint(SpringLayout.WEST, sendButton, 5, SpringLayout.EAST, sendText); + + // make the frame big enough to fit all in + layout.putConstraint(SpringLayout.EAST, getContentPane(), 10, SpringLayout.EAST, statusList); + layout.putConstraint(SpringLayout.SOUTH, getContentPane(), 10, SpringLayout.SOUTH, sendText); + + layout.putConstraint(SpringLayout.NORTH, statusList, 5, SpringLayout.NORTH, getContentPane()); + layout.putConstraint(SpringLayout.WEST, statusList, 15, SpringLayout.EAST, sendButton); + + getContentPane().setLayout(layout); + + getContentPane().add(msgs); + getContentPane().add(sendText); + getContentPane().add(sendButton); + getContentPane().add(statusList); + + setTitle("Chat - " + userName); + // setSize(300, 200); + setLocationRelativeTo(null); + setDefaultCloseOperation(EXIT_ON_CLOSE); + + pack(); + } + + /** Returns an ImageIcon, or null if the path was invalid. */ + protected ImageIcon createImageIcon(String path, String description) { + java.net.URL imgURL = getClass().getResource(path); + if (imgURL != null) { + return new ImageIcon(imgURL, description); + } else { + System.err.println("Couldn't find file: " + path); + return null; + } + } protected void sendMessage() { messages.set(userName + ":" + sendText.getText()); displayMessage("You: " + sendText.getText()); sendText.setText(""); } - + protected void displayMessage(String text) { if (msgs.getText().isEmpty()) msgs.append(text); @@ -171,7 +189,7 @@ protected void displayMessage(String text) { public void notifyReactiveChanged(String newValue) { String[] msg = newValue.split(":", 2); if (!msg[0].equals(userName)) - displayMessage(msg[0] + ": " +msg[1]); + displayMessage(msg[0] + ": " + msg[1]); } public static void main(String[] args) { @@ -179,30 +197,30 @@ public static void main(String[] args) { if (args.length < 1) System.out.println("username missing"); - EventQueue.invokeLater(new Runnable() { - - @Override - public void run() { + EventQueue.invokeLater(new Runnable() { + + @Override + public void run() { try { Chat chat = new Chat(args[0]); chat.setVisible(true); } catch (Exception e) { } - - } - }); -// // read from command line -// BufferedReader commandLine = new java.io.BufferedReader( -// new InputStreamReader(System.in)); -// -// // loop until the word "exit" is typed -// while (true) { -// String s = commandLine.readLine(); -// if (s.equalsIgnoreCase("exit")) { -// System.exit(0);// exit program -// } else -// chat.writeMessage(s); -// } + + } + }); + // // read from command line + // BufferedReader commandLine = new java.io.BufferedReader( + // new InputStreamReader(System.in)); + // + // // loop until the word "exit" is typed + // while (true) { + // String s = commandLine.readLine(); + // if (s.equalsIgnoreCase("exit")) { + // System.exit(0);// exit program + // } else + // chat.writeMessage(s); + // } } catch (Exception e) { e.printStackTrace(); } From 0d68fe90f865dffd5fccfb62329b848859c33cfe Mon Sep 17 00:00:00 2001 From: Tobias Becker Date: Mon, 23 Nov 2015 15:15:00 +0100 Subject: [PATCH 006/161] added more information passed to ReactiveChangeListeners --- .../java/javareact/common/types/AbstractReactive.java | 7 ++++--- .../common/types/ReactiveChangeListener.java | 4 ++-- .../main/java/javareact/common/types/RemoteVar.java | 8 +++++--- .../java/javareact/examples/local/LocalExample2.java | 2 +- .../java/javareact/examples/local/LocalExample3.java | 2 +- Dream2/src/test/java/javareact/RemoteTest.java | 2 +- Dream2/src/test/java/javareact/chat/Chat.java | 11 +++++------ .../test/java/javareact/financial/FinancialApp.java | 2 +- Dream2/src/test/java/javareact/financial/Model1.java | 2 +- Dream2/src/test/java/javareact/financial/Model2.java | 2 +- Dream2/src/test/java/javareact/financial/Model3.java | 2 +- 11 files changed, 23 insertions(+), 21 deletions(-) diff --git a/Dream2/src/main/java/javareact/common/types/AbstractReactive.java b/Dream2/src/main/java/javareact/common/types/AbstractReactive.java index 849347f..bbd3648 100755 --- a/Dream2/src/main/java/javareact/common/types/AbstractReactive.java +++ b/Dream2/src/main/java/javareact/common/types/AbstractReactive.java @@ -48,6 +48,7 @@ public void update(EventProxyPair eventProxyPair) { if (!pairs.isEmpty()) { System.out.println(name + ": actual update"); // Compute the new value + T oldVal = val; val = evaluate(); logger.finest("New value computed for the reactive object: " + val); @@ -69,7 +70,7 @@ public void update(EventProxyPair eventProxyPair) { // Notify listeners logger.finest("Notifying registered listeners of the change."); - notifyListeners(); + notifyListeners(oldVal, eventProxyPair.getEventPacket().getEvent().getHostId()); // Acknowledge the proxy for (EventProxyPair pair : pairs) { @@ -92,9 +93,9 @@ public void removeReactiveChangeListener(ReactiveChangeListener listener) { listeners.remove(listener); } - private final void notifyListeners() { + private final void notifyListeners(T oldVal, String host) { for (ReactiveChangeListener listener : listeners) { - listener.notifyReactiveChanged(val); + listener.notifyReactiveChanged(oldVal, val, host); } } diff --git a/Dream2/src/main/java/javareact/common/types/ReactiveChangeListener.java b/Dream2/src/main/java/javareact/common/types/ReactiveChangeListener.java index 8e38c22..8a0c09f 100755 --- a/Dream2/src/main/java/javareact/common/types/ReactiveChangeListener.java +++ b/Dream2/src/main/java/javareact/common/types/ReactiveChangeListener.java @@ -5,6 +5,6 @@ */ public interface ReactiveChangeListener { - public void notifyReactiveChanged(T newValue); + public void notifyReactiveChanged(T oldValue, T newValue, String host); -} +} \ No newline at end of file diff --git a/Dream2/src/main/java/javareact/common/types/RemoteVar.java b/Dream2/src/main/java/javareact/common/types/RemoteVar.java index 326efff..d407ea8 100755 --- a/Dream2/src/main/java/javareact/common/types/RemoteVar.java +++ b/Dream2/src/main/java/javareact/common/types/RemoteVar.java @@ -11,6 +11,7 @@ public class RemoteVar extends Proxy implements Reactive { private final Set> listeners = new HashSet>(); private T val; + private T oldVal; public RemoteVar(String host, String object) { super(host, object); @@ -53,6 +54,7 @@ protected void processEvent(Event ev) { protected final void processEvent(Event ev) { if (ev.hasAttribute(method)) { Attribute attr = ev.getAttributeFor(method); + oldVal = val; val = (T)attr.getValue(); } } @@ -72,16 +74,16 @@ public void removeReactiveChangeListener(ReactiveChangeListener listener) { listeners.remove(listener); } - private final void notifyListeners() { + private final void notifyListeners(String host) { for (ReactiveChangeListener listener : listeners) { - listener.notifyReactiveChanged(val); + listener.notifyReactiveChanged(oldVal, val, host); } } @Override public final void notifyValueChanged(EventPacket evPkt) { super.notifyValueChanged(evPkt); - notifyListeners(); + notifyListeners(evPkt.getEvent().getHostId()); } } diff --git a/Dream2/src/main/java/javareact/examples/local/LocalExample2.java b/Dream2/src/main/java/javareact/examples/local/LocalExample2.java index e668290..518b343 100755 --- a/Dream2/src/main/java/javareact/examples/local/LocalExample2.java +++ b/Dream2/src/main/java/javareact/examples/local/LocalExample2.java @@ -51,7 +51,7 @@ public void launch() { } @Override - public void notifyReactiveChanged(Double changedReactive) { + public void notifyReactiveChanged(Double old, Double changedReactive, String host) { System.out.println(changedReactive); } diff --git a/Dream2/src/main/java/javareact/examples/local/LocalExample3.java b/Dream2/src/main/java/javareact/examples/local/LocalExample3.java index 98205bb..edb35f3 100755 --- a/Dream2/src/main/java/javareact/examples/local/LocalExample3.java +++ b/Dream2/src/main/java/javareact/examples/local/LocalExample3.java @@ -23,7 +23,7 @@ public static void main(String args[]) { () -> 1000 + obListProxy.get().size(), obListProxy); - reactInt.addReactiveChangeListener(newValue -> System.out.println(newValue)); + reactInt.addReactiveChangeListener((oldValue, newValue, host) -> System.out.println(newValue)); obList.modify(self -> self.add(10)); obList.modify(self -> self.add(20)); diff --git a/Dream2/src/test/java/javareact/RemoteTest.java b/Dream2/src/test/java/javareact/RemoteTest.java index ea71c65..182ee78 100644 --- a/Dream2/src/test/java/javareact/RemoteTest.java +++ b/Dream2/src/test/java/javareact/RemoteTest.java @@ -48,7 +48,7 @@ public void remoteTest() { } @Override - public void notifyReactiveChanged(Integer newValue) { + public void notifyReactiveChanged(Integer oldValue, Integer newValue, String host) { assertTrue( signal.get().equals(a.get() * 2)); } diff --git a/Dream2/src/test/java/javareact/chat/Chat.java b/Dream2/src/test/java/javareact/chat/Chat.java index 443d179..a33c906 100644 --- a/Dream2/src/test/java/javareact/chat/Chat.java +++ b/Dream2/src/test/java/javareact/chat/Chat.java @@ -50,7 +50,7 @@ public Chat(String username) throws Exception { s.addReactiveChangeListener(new ReactiveChangeListener() { @Override - public void notifyReactiveChanged(Boolean newValue) { + public void notifyReactiveChanged(Boolean oldValue, Boolean newValue, String host) { System.out.println(newValue); } @@ -173,7 +173,7 @@ protected ImageIcon createImageIcon(String path, String description) { } protected void sendMessage() { - messages.set(userName + ":" + sendText.getText()); + messages.set(sendText.getText()); displayMessage("You: " + sendText.getText()); sendText.setText(""); } @@ -186,10 +186,9 @@ protected void displayMessage(String text) { } @Override - public void notifyReactiveChanged(String newValue) { - String[] msg = newValue.split(":", 2); - if (!msg[0].equals(userName)) - displayMessage(msg[0] + ": " + msg[1]); + public void notifyReactiveChanged(String oldValue, String newValue, String host) { + if (!host.equals(userName)) + displayMessage(host + ": " + newValue); } public static void main(String[] args) { diff --git a/Dream2/src/test/java/javareact/financial/FinancialApp.java b/Dream2/src/test/java/javareact/financial/FinancialApp.java index 83c76cf..5d74de5 100644 --- a/Dream2/src/test/java/javareact/financial/FinancialApp.java +++ b/Dream2/src/test/java/javareact/financial/FinancialApp.java @@ -59,7 +59,7 @@ public void start() { } @Override - public void notifyReactiveChanged(Integer newValue) { + public void notifyReactiveChanged(Integer oldValue, Integer newValue, String host) { System.out.println("Value changed"); // if (f1Signal.get() != null && f2Signal.get() != null && f3Signal.get() != null) { // if ((f1Signal.get() + f2Signal.get() + f3Signal.get()) / 3.0 > 150) { diff --git a/Dream2/src/test/java/javareact/financial/Model1.java b/Dream2/src/test/java/javareact/financial/Model1.java index 4459303..72f88de 100644 --- a/Dream2/src/test/java/javareact/financial/Model1.java +++ b/Dream2/src/test/java/javareact/financial/Model1.java @@ -24,7 +24,7 @@ public void start() { } @Override - public void notifyReactiveChanged(Integer newValue) { + public void notifyReactiveChanged(Integer oldValue, Integer newValue, String host) { System.out.println("New value for f1: " + newValue); } diff --git a/Dream2/src/test/java/javareact/financial/Model2.java b/Dream2/src/test/java/javareact/financial/Model2.java index d42215f..456825c 100644 --- a/Dream2/src/test/java/javareact/financial/Model2.java +++ b/Dream2/src/test/java/javareact/financial/Model2.java @@ -21,7 +21,7 @@ public void start() { } @Override - public void notifyReactiveChanged(Integer newValue) { + public void notifyReactiveChanged(Integer oldValue, Integer newValue, String host) { System.out.println("New value for f2: " + newValue); } diff --git a/Dream2/src/test/java/javareact/financial/Model3.java b/Dream2/src/test/java/javareact/financial/Model3.java index e06369f..1e34281 100644 --- a/Dream2/src/test/java/javareact/financial/Model3.java +++ b/Dream2/src/test/java/javareact/financial/Model3.java @@ -21,7 +21,7 @@ public void start() { } @Override - public void notifyReactiveChanged(Integer newValue) { + public void notifyReactiveChanged(Integer oldValue, Integer newValue, String host) { System.out.println("New value for f3: " + newValue); } From 5991a6130fd32fc73858e86449823eb5b09c5afe Mon Sep 17 00:00:00 2001 From: Tobias Becker Date: Thu, 3 Dec 2015 18:04:19 +0100 Subject: [PATCH 007/161] added a ChangeEvent --- .../common/types/AbstractReactive.java | 21 +-------- .../javareact/common/types/ChangeEvent.java | 44 +++++++++++++++++++ .../common/types/ChangeEventHandler.java | 6 +++ .../java/javareact/common/types/Reactive.java | 16 ------- .../javareact/common/types/RemoteVar.java | 22 ---------- .../examples/local/LocalExample2.java | 12 ++--- .../examples/local/LocalExample3.java | 8 +--- .../src/test/java/javareact/RemoteTest.java | 11 +---- .../javareact/financial/FinancialApp.java | 18 ++++---- .../test/java/javareact/financial/Model1.java | 11 +---- .../test/java/javareact/financial/Model2.java | 10 +---- .../test/java/javareact/financial/Model3.java | 10 +---- 12 files changed, 73 insertions(+), 116 deletions(-) create mode 100644 Dream2/src/main/java/javareact/common/types/ChangeEvent.java create mode 100644 Dream2/src/main/java/javareact/common/types/ChangeEventHandler.java diff --git a/Dream2/src/main/java/javareact/common/types/AbstractReactive.java b/Dream2/src/main/java/javareact/common/types/AbstractReactive.java index 849347f..93b171a 100755 --- a/Dream2/src/main/java/javareact/common/types/AbstractReactive.java +++ b/Dream2/src/main/java/javareact/common/types/AbstractReactive.java @@ -18,7 +18,6 @@ import javareact.common.packets.content.Subscription; abstract class AbstractReactive implements Reactive, ProxyChangeListener { - private final Set> listeners = new HashSet>(); private final ClientEventForwarder clientEventForwarder; private final QueueManager queueManager = new QueueManager(); protected final String name; @@ -67,10 +66,6 @@ public void update(EventProxyPair eventProxyPair) { logger.finest("Sending event to dependent reactive objects."); clientEventForwarder.sendEvent(id, ev, computedFrom, finalExpressions, true); - // Notify listeners - logger.finest("Notifying registered listeners of the change."); - notifyListeners(); - // Acknowledge the proxy for (EventProxyPair pair : pairs) { EventPacket evPkt = pair.getEventPacket(); @@ -82,20 +77,8 @@ public void update(EventProxyPair eventProxyPair) { } } - @Override - public void addReactiveChangeListener(ReactiveChangeListener listener) { - listeners.add(listener); - } - - @Override - public void removeReactiveChangeListener(ReactiveChangeListener listener) { - listeners.remove(listener); - } - - private final void notifyListeners() { - for (ReactiveChangeListener listener : listeners) { - listener.notifyReactiveChanged(val); - } + public ChangeEvent change() { + return new ChangeEvent(this); } private final void sentAdvertisement() { diff --git a/Dream2/src/main/java/javareact/common/types/ChangeEvent.java b/Dream2/src/main/java/javareact/common/types/ChangeEvent.java new file mode 100644 index 0000000..abfd586 --- /dev/null +++ b/Dream2/src/main/java/javareact/common/types/ChangeEvent.java @@ -0,0 +1,44 @@ +package javareact.common.types; + +import java.util.HashSet; +import java.util.Set; + +import javareact.common.packets.content.Attribute; + +public class ChangeEvent implements ProxyChangeListener { + + private T latest; + + private Set> handlers = new HashSet>(); + + public ChangeEvent(ProxyGenerator p) { + p.getProxy().addProxyChangeListener(this); + } + + public void addHandler(ChangeEventHandler handler) { + handlers.add(handler); + } + + public void removeHandler(ChangeEventHandler handler) { + handlers.remove(handler); + } + + private void notifyHandler(T oldValue) { + handlers.forEach(h -> h.handle(oldValue, latest)); + } + + @Override + public void update(EventProxyPair pair) { + String method = pair.getProxy().method; + Attribute temp = pair.getEventPacket().getEvent() + .getAttributeFor(method); + System.out.println("update called"); + if (latest == null || !latest.equals(temp.getValue())) { + T old = latest; + latest = temp.getValue(); + notifyHandler(old); + } + pair.getProxy().notifyEventProcessed(this, pair.getEventPacket()); + } + +} \ No newline at end of file diff --git a/Dream2/src/main/java/javareact/common/types/ChangeEventHandler.java b/Dream2/src/main/java/javareact/common/types/ChangeEventHandler.java new file mode 100644 index 0000000..7e1105b --- /dev/null +++ b/Dream2/src/main/java/javareact/common/types/ChangeEventHandler.java @@ -0,0 +1,6 @@ +package javareact.common.types; + +@FunctionalInterface +public interface ChangeEventHandler { + public void handle(T oldVal, T newVal); +} diff --git a/Dream2/src/main/java/javareact/common/types/Reactive.java b/Dream2/src/main/java/javareact/common/types/Reactive.java index 64108ee..2a9d8aa 100755 --- a/Dream2/src/main/java/javareact/common/types/Reactive.java +++ b/Dream2/src/main/java/javareact/common/types/Reactive.java @@ -6,8 +6,6 @@ * In particular, it exposes an evaluate() method which is automatically invoked whenever one of the observable methods * it depends on changes. * - * It also exposes methods to register and unregister ReactiveChangeListener, which are automatically notified when the - * value of the reactive object changes. */ public interface Reactive extends ProxyGenerator { @@ -16,18 +14,4 @@ public interface Reactive extends ProxyGenerator { */ public T evaluate(); - /** - * Register a new ReactiveChangeListener. - * - * @param listener the listener to add. - */ - public void addReactiveChangeListener(ReactiveChangeListener listener); - - /** - * Unregister a ReactiveChangeListener. - * - * @param listener the listener to remove. - */ - public void removeReactiveChangeListener(ReactiveChangeListener listener); - } diff --git a/Dream2/src/main/java/javareact/common/types/RemoteVar.java b/Dream2/src/main/java/javareact/common/types/RemoteVar.java index 326efff..9ccac79 100755 --- a/Dream2/src/main/java/javareact/common/types/RemoteVar.java +++ b/Dream2/src/main/java/javareact/common/types/RemoteVar.java @@ -62,26 +62,4 @@ public T evaluate() { return this.get(); } - @Override - public void addReactiveChangeListener(ReactiveChangeListener listener) { - listeners.add(listener); - } - - @Override - public void removeReactiveChangeListener(ReactiveChangeListener listener) { - listeners.remove(listener); - } - - private final void notifyListeners() { - for (ReactiveChangeListener listener : listeners) { - listener.notifyReactiveChanged(val); - } - } - - @Override - public final void notifyValueChanged(EventPacket evPkt) { - super.notifyValueChanged(evPkt); - notifyListeners(); - } - } diff --git a/Dream2/src/main/java/javareact/examples/local/LocalExample2.java b/Dream2/src/main/java/javareact/examples/local/LocalExample2.java index e668290..1d6ca61 100755 --- a/Dream2/src/main/java/javareact/examples/local/LocalExample2.java +++ b/Dream2/src/main/java/javareact/examples/local/LocalExample2.java @@ -2,10 +2,9 @@ import javareact.common.types.RemoteVar; import javareact.common.types.Var; -import javareact.common.types.ReactiveChangeListener; import javareact.common.types.Signal; -public class LocalExample2 implements ReactiveChangeListener { +public class LocalExample2 { public static void main(String args[]) { LocalExample2 example = new LocalExample2(); @@ -32,7 +31,8 @@ public void launch() { new Signal("sub", () -> reactDouble1Proxy.get() - reactDouble2Proxy.get(), - reactDouble1Proxy, reactDouble2Proxy).addReactiveChangeListener(this); + reactDouble1Proxy, reactDouble2Proxy).change().addHandler( + (oldValue, newValue) -> System.out.println(newValue)); try { Thread.sleep(500); @@ -49,10 +49,4 @@ public void launch() { obDouble1.set(40.0); obDouble2.set(50.0); } - - @Override - public void notifyReactiveChanged(Double changedReactive) { - System.out.println(changedReactive); - } - } diff --git a/Dream2/src/main/java/javareact/examples/local/LocalExample3.java b/Dream2/src/main/java/javareact/examples/local/LocalExample3.java index 98205bb..8cb9134 100755 --- a/Dream2/src/main/java/javareact/examples/local/LocalExample3.java +++ b/Dream2/src/main/java/javareact/examples/local/LocalExample3.java @@ -1,12 +1,6 @@ package javareact.examples.local; import java.util.ArrayList; - -import javareact.common.types.ListProxy; -import javareact.common.types.ObservableList; -import javareact.common.types.ReactiveChangeListener; -import javareact.common.types.ReactiveInteger; - import java.util.List; import javareact.common.types.Var; @@ -23,7 +17,7 @@ public static void main(String args[]) { () -> 1000 + obListProxy.get().size(), obListProxy); - reactInt.addReactiveChangeListener(newValue -> System.out.println(newValue)); + reactInt.change().addHandler((oldValue, newValue) -> System.out.println(newValue)); obList.modify(self -> self.add(10)); obList.modify(self -> self.add(20)); diff --git a/Dream2/src/test/java/javareact/RemoteTest.java b/Dream2/src/test/java/javareact/RemoteTest.java index ea71c65..399fe4e 100644 --- a/Dream2/src/test/java/javareact/RemoteTest.java +++ b/Dream2/src/test/java/javareact/RemoteTest.java @@ -7,7 +7,6 @@ import java.util.Set; import javareact.common.Consts; -import javareact.common.types.ReactiveChangeListener; import javareact.common.types.RemoteVar; import javareact.common.types.Signal; import javareact.common.types.Var; @@ -16,7 +15,7 @@ import org.junit.Test; -public class RemoteTest implements ReactiveChangeListener { +public class RemoteTest { private boolean serverStarted = false; private boolean tokenServiceStarted = false; @@ -35,7 +34,7 @@ public void remoteTest() { return a.get() * 2; }, a); - signal.addReactiveChangeListener(this); + signal.change().addHandler((oldValue, newValue) -> assertTrue( signal.get().equals(a.get() * 2))); for (int i = 0; i < 10; i++) { try { @@ -46,10 +45,4 @@ public void remoteTest() { } } } - - @Override - public void notifyReactiveChanged(Integer newValue) { - assertTrue( signal.get().equals(a.get() * 2)); - } - } diff --git a/Dream2/src/test/java/javareact/financial/FinancialApp.java b/Dream2/src/test/java/javareact/financial/FinancialApp.java index 83c76cf..64b898f 100644 --- a/Dream2/src/test/java/javareact/financial/FinancialApp.java +++ b/Dream2/src/test/java/javareact/financial/FinancialApp.java @@ -1,11 +1,11 @@ package javareact.financial; import javareact.common.Consts; -import javareact.common.types.ReactiveChangeListener; +import javareact.common.types.ChangeEventHandler; import javareact.common.types.RemoteVar; import javareact.common.types.Signal; -public class FinancialApp implements ReactiveChangeListener { +public class FinancialApp implements ChangeEventHandler { //private Signal f1Signal; //private Signal f2Signal; //private Signal f3Signal; @@ -33,13 +33,13 @@ public void start() { RemoteVar model1 = new RemoteVar<>("Model1", "model1"); - //f1Signal = new Signal<>("f1Signal", () -> f1.get(), f1); - //f2Signal = new Signal<>("f2Signal", () -> f2.get(), f2); - //f3Signal = new Signal<>("f3Signal", () -> f3.get(), f3); + Signal f1Signal = new Signal<>("f1Signal", () -> f1.get(), f1); + Signal f2Signal = new Signal<>("f2Signal", () -> f2.get(), f2); + Signal f3Signal = new Signal<>("f3Signal", () -> f3.get(), f3); - f1.addReactiveChangeListener(this); - f2.addReactiveChangeListener(this); - f3.addReactiveChangeListener(this); + f1Signal.change().addHandler(this); + f2Signal.change().addHandler(this); + f3Signal.change().addHandler(this); try { Thread.sleep(2000); @@ -59,7 +59,7 @@ public void start() { } @Override - public void notifyReactiveChanged(Integer newValue) { + public void handle(Integer oldValue, Integer newValue) { System.out.println("Value changed"); // if (f1Signal.get() != null && f2Signal.get() != null && f3Signal.get() != null) { // if ((f1Signal.get() + f2Signal.get() + f3Signal.get()) / 3.0 > 150) { diff --git a/Dream2/src/test/java/javareact/financial/Model1.java b/Dream2/src/test/java/javareact/financial/Model1.java index 4459303..37c515b 100644 --- a/Dream2/src/test/java/javareact/financial/Model1.java +++ b/Dream2/src/test/java/javareact/financial/Model1.java @@ -1,12 +1,10 @@ package javareact.financial; import javareact.common.Consts; -import javareact.common.types.ReactiveChangeListener; import javareact.common.types.RemoteVar; import javareact.common.types.Signal; -import javareact.common.types.Var; -public class Model1 implements ReactiveChangeListener { +public class Model1 { public void start() { Consts.hostName = "Model1"; @@ -20,12 +18,7 @@ public void start() { else { return marketIndex.get() * 2 + stockOpts.get(); } }, marketIndex, stockOpts); - f1.addReactiveChangeListener(this); - } - - @Override - public void notifyReactiveChanged(Integer newValue) { - System.out.println("New value for f1: " + newValue); + f1.change().addHandler((oldValue, newValue) -> System.out.println("New value for f1: " + newValue)); } public static void main(String[] args) { diff --git a/Dream2/src/test/java/javareact/financial/Model2.java b/Dream2/src/test/java/javareact/financial/Model2.java index d42215f..527bf13 100644 --- a/Dream2/src/test/java/javareact/financial/Model2.java +++ b/Dream2/src/test/java/javareact/financial/Model2.java @@ -1,11 +1,10 @@ package javareact.financial; import javareact.common.Consts; -import javareact.common.types.ReactiveChangeListener; import javareact.common.types.RemoteVar; import javareact.common.types.Signal; -public class Model2 implements ReactiveChangeListener { +public class Model2 { public void start() { Consts.hostName = "Model2"; @@ -17,12 +16,7 @@ public void start() { else { return marketIndex.get() + stockOpts.get() * 2; } }, marketIndex, stockOpts); - f2.addReactiveChangeListener(this); - } - - @Override - public void notifyReactiveChanged(Integer newValue) { - System.out.println("New value for f2: " + newValue); + f2.change().addHandler((oldValue, newValue) -> System.out.println("New value for f2: " + newValue)); } public static void main(String[] args) { diff --git a/Dream2/src/test/java/javareact/financial/Model3.java b/Dream2/src/test/java/javareact/financial/Model3.java index e06369f..796a467 100644 --- a/Dream2/src/test/java/javareact/financial/Model3.java +++ b/Dream2/src/test/java/javareact/financial/Model3.java @@ -1,11 +1,10 @@ package javareact.financial; import javareact.common.Consts; -import javareact.common.types.ReactiveChangeListener; import javareact.common.types.RemoteVar; import javareact.common.types.Signal; -public class Model3 implements ReactiveChangeListener { +public class Model3 { public void start() { Consts.hostName = "Model3"; @@ -17,14 +16,9 @@ public void start() { else { return marketIndex.get() + news.get(); } }, marketIndex, news); - f3.addReactiveChangeListener(this); + f3.change().addHandler((oldValue, newValue) -> System.out.println("New value for f3: " + newValue)); } - @Override - public void notifyReactiveChanged(Integer newValue) { - System.out.println("New value for f3: " + newValue); - } - public static void main(String[] args) { new Model3().start(); } From e0444907cb39b008f10922d47b3923c46b9ac09a Mon Sep 17 00:00:00 2001 From: Tobias Becker Date: Mon, 14 Dec 2015 17:38:25 +0100 Subject: [PATCH 008/161] refactored chat to the new ChangeEvent --- Dream2/src/test/java/javareact/chat/Chat.java | 52 ++++++++++--------- 1 file changed, 28 insertions(+), 24 deletions(-) diff --git a/Dream2/src/test/java/javareact/chat/Chat.java b/Dream2/src/test/java/javareact/chat/Chat.java index a33c906..29bc764 100644 --- a/Dream2/src/test/java/javareact/chat/Chat.java +++ b/Dream2/src/test/java/javareact/chat/Chat.java @@ -22,12 +22,11 @@ import javareact.common.types.Signal; import javareact.common.types.Var; -public class Chat extends JFrame implements ReactiveChangeListener { +public class Chat extends JFrame { private static final long serialVersionUID = 390641070042167681L; private RemoteVar remoteMessages; private Var messages; - private Var status; private String userName; private JTextArea msgs; private JTextField sendText; @@ -37,24 +36,35 @@ public Chat(String username) throws Exception { Consts.hostName = username; messages = new Var("message", ""); remoteMessages = new RemoteVar("message@*"); - remoteMessages.addReactiveChangeListener(this); - status = new Var("status", true); - RemoteVar remoteStatus = new RemoteVar<>("status@*"); - Signal s = new Signal("s", () -> { - if (remoteStatus.get() == null) - return false; + // split Sender + Message into two Signals (sHost + sMessage) + Signal sHost = new Signal("sHost", () -> { + if (remoteMessages.get() == null) + return ""; + else + return remoteMessages.get().split(":", 2)[0]; + }, remoteMessages); + + Signal sMessage = new Signal("sMessage", () -> { + if (remoteMessages.get() == null) + return ""; + else + return remoteMessages.get().split(":", 2)[1]; + }, remoteMessages); + + Signal display = new Signal("display", () -> { + if (!sHost.get().equals(userName)) + return sMessage.get(); else - return remoteStatus.get(); - } , remoteStatus); - s.addReactiveChangeListener(new ReactiveChangeListener() { - - @Override - public void notifyReactiveChanged(Boolean oldValue, Boolean newValue, String host) { - System.out.println(newValue); - - } + return ""; + }, sHost, sMessage); + + display.change().addHandler((oldValue, newValue) -> { + if (!sHost.get().equals(userName)) + displayMessage(sHost.get() + ": " + newValue); }); + + this.userName = username; initUI(); } @@ -173,7 +183,7 @@ protected ImageIcon createImageIcon(String path, String description) { } protected void sendMessage() { - messages.set(sendText.getText()); + messages.set(userName + ":" + sendText.getText()); displayMessage("You: " + sendText.getText()); sendText.setText(""); } @@ -185,12 +195,6 @@ protected void displayMessage(String text) { msgs.append(System.lineSeparator() + text); } - @Override - public void notifyReactiveChanged(String oldValue, String newValue, String host) { - if (!host.equals(userName)) - displayMessage(host + ": " + newValue); - } - public static void main(String[] args) { try { if (args.length < 1) From e2fbcdc09392f3efb253a0284f7a954c3f099a7b Mon Sep 17 00:00:00 2001 From: Tobias Becker Date: Mon, 14 Dec 2015 17:53:35 +0100 Subject: [PATCH 009/161] split gui and reactive process into two classes --- Dream2/src/test/java/javareact/chat/Chat.java | 167 +----------------- .../src/test/java/javareact/chat/ChatGUI.java | 165 +++++++++++++++++ 2 files changed, 174 insertions(+), 158 deletions(-) create mode 100644 Dream2/src/test/java/javareact/chat/ChatGUI.java diff --git a/Dream2/src/test/java/javareact/chat/Chat.java b/Dream2/src/test/java/javareact/chat/Chat.java index 29bc764..b114615 100644 --- a/Dream2/src/test/java/javareact/chat/Chat.java +++ b/Dream2/src/test/java/javareact/chat/Chat.java @@ -1,36 +1,17 @@ package javareact.chat; -import java.awt.Color; import java.awt.EventQueue; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.KeyEvent; -import java.awt.event.KeyListener; -import javax.swing.DefaultListCellRenderer; -import javax.swing.DefaultListModel; -import javax.swing.ImageIcon; -import javax.swing.JButton; -import javax.swing.JFrame; -import javax.swing.JLabel; -import javax.swing.JList; -import javax.swing.JTextArea; -import javax.swing.JTextField; -import javax.swing.SpringLayout; import javareact.common.Consts; -import javareact.common.types.ReactiveChangeListener; import javareact.common.types.RemoteVar; import javareact.common.types.Signal; import javareact.common.types.Var; -public class Chat extends JFrame { +public class Chat{ - private static final long serialVersionUID = 390641070042167681L; private RemoteVar remoteMessages; private Var messages; private String userName; - private JTextArea msgs; - private JTextField sendText; - private JList statusList; + private ChatGUI gui; public Chat(String username) throws Exception { Consts.hostName = username; @@ -61,138 +42,21 @@ public Chat(String username) throws Exception { display.change().addHandler((oldValue, newValue) -> { if (!sHost.get().equals(userName)) - displayMessage(sHost.get() + ": " + newValue); + gui.displayMessage(sHost.get() + ": " + newValue); }); this.userName = username; - initUI(); - } - - private void initUI() { - sendText = new JTextField(20); - sendText.addKeyListener(new KeyListener() { - - @Override - public void keyTyped(KeyEvent e) { - } - - @Override - public void keyReleased(KeyEvent e) { - } - - @Override - public void keyPressed(KeyEvent e) { - if (e.getKeyCode() == KeyEvent.VK_ENTER) - sendMessage(); - } - }); - JButton sendButton = new JButton("Send"); - sendButton.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent event) { - sendMessage(); - } - }); - msgs = new JTextArea(5, 27); - msgs.setEditable(false); - msgs.setMaximumSize(null); - - DefaultListModel listModel = new DefaultListModel(); - listModel.addElement("Alice"); - listModel.addElement("Bob"); - statusList = new JList(listModel); - statusList.setEnabled(false); - statusList.setLayoutOrientation(JList.HORIZONTAL_WRAP); - statusList.setVisibleRowCount(-1); - - statusList.setCellRenderer(new DefaultListCellRenderer() { - - private static final long serialVersionUID = 9019815674349211344L; - private JLabel label = new JLabel(); - private Color textSelectionColor = Color.BLACK; - private Color backgroundSelectionColor = Color.CYAN; - private Color textNonSelectionColor = Color.BLACK; - private Color backgroundNonSelectionColor = Color.WHITE; - - @Override - public java.awt.Component getListCellRendererComponent(javax.swing.JList list, Object value, int index, - boolean isSelected, boolean cellHasFocus) { - - String name = (String) value; - label.setIcon(createImageIcon("status-offline.png", "Offline")); - label.setText(name); - - if (isSelected) { - label.setBackground(backgroundSelectionColor); - label.setForeground(textSelectionColor); - } else { - label.setBackground(backgroundNonSelectionColor); - label.setForeground(textNonSelectionColor); - } - - return label; - }; - }); - SpringLayout layout = new SpringLayout(); - - // put messages on (5,5) - layout.putConstraint(SpringLayout.WEST, msgs, 5, SpringLayout.WEST, getContentPane()); - layout.putConstraint(SpringLayout.NORTH, msgs, 5, SpringLayout.NORTH, getContentPane()); - - // put textfield below messages - layout.putConstraint(SpringLayout.NORTH, sendText, 5, SpringLayout.SOUTH, msgs); - layout.putConstraint(SpringLayout.WEST, sendText, 5, SpringLayout.WEST, getContentPane()); - - // put button next to the textfield - layout.putConstraint(SpringLayout.NORTH, sendButton, 5, SpringLayout.SOUTH, msgs); - layout.putConstraint(SpringLayout.WEST, sendButton, 5, SpringLayout.EAST, sendText); - - // make the frame big enough to fit all in - layout.putConstraint(SpringLayout.EAST, getContentPane(), 10, SpringLayout.EAST, statusList); - layout.putConstraint(SpringLayout.SOUTH, getContentPane(), 10, SpringLayout.SOUTH, sendText); - - layout.putConstraint(SpringLayout.NORTH, statusList, 5, SpringLayout.NORTH, getContentPane()); - layout.putConstraint(SpringLayout.WEST, statusList, 15, SpringLayout.EAST, sendButton); - - getContentPane().setLayout(layout); - - getContentPane().add(msgs); - getContentPane().add(sendText); - getContentPane().add(sendButton); - getContentPane().add(statusList); - - setTitle("Chat - " + userName); - // setSize(300, 200); - setLocationRelativeTo(null); - setDefaultCloseOperation(EXIT_ON_CLOSE); - - pack(); + gui = new ChatGUI(userName); + gui.setListener(this); } - /** Returns an ImageIcon, or null if the path was invalid. */ - protected ImageIcon createImageIcon(String path, String description) { - java.net.URL imgURL = getClass().getResource(path); - if (imgURL != null) { - return new ImageIcon(imgURL, description); - } else { - System.err.println("Couldn't find file: " + path); - return null; - } - } protected void sendMessage() { - messages.set(userName + ":" + sendText.getText()); - displayMessage("You: " + sendText.getText()); - sendText.setText(""); - } - - protected void displayMessage(String text) { - if (msgs.getText().isEmpty()) - msgs.append(text); - else - msgs.append(System.lineSeparator() + text); + messages.set(userName + ":" + gui.getTypedText()); + gui.displayMessage("You: " + gui.getTypedText()); + gui.resetTypedText(); } public static void main(String[] args) { @@ -205,25 +69,12 @@ public static void main(String[] args) { @Override public void run() { try { - Chat chat = new Chat(args[0]); - chat.setVisible(true); + new Chat(args[0]); } catch (Exception e) { } } }); - // // read from command line - // BufferedReader commandLine = new java.io.BufferedReader( - // new InputStreamReader(System.in)); - // - // // loop until the word "exit" is typed - // while (true) { - // String s = commandLine.readLine(); - // if (s.equalsIgnoreCase("exit")) { - // System.exit(0);// exit program - // } else - // chat.writeMessage(s); - // } } catch (Exception e) { e.printStackTrace(); } diff --git a/Dream2/src/test/java/javareact/chat/ChatGUI.java b/Dream2/src/test/java/javareact/chat/ChatGUI.java new file mode 100644 index 0000000..65f1c55 --- /dev/null +++ b/Dream2/src/test/java/javareact/chat/ChatGUI.java @@ -0,0 +1,165 @@ +package javareact.chat; + +import java.awt.Color; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.KeyEvent; +import java.awt.event.KeyListener; + +import javax.swing.DefaultListCellRenderer; +import javax.swing.DefaultListModel; +import javax.swing.ImageIcon; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JList; +import javax.swing.JTextArea; +import javax.swing.JTextField; +import javax.swing.SpringLayout; + +public class ChatGUI extends JFrame { + + private static final long serialVersionUID = 4659984914364067514L; + private JTextArea msgs; + private JTextField sendText; + private JList statusList; + + private Chat listener; + + public ChatGUI(String userName) { + initUI(userName); + } + + public void setListener(Chat c) { + listener = c; + } + + public String getTypedText() { + return sendText.getText(); + } + + public void resetTypedText() { + sendText.setText(""); + } + + public void displayMessage(String text) { + if (msgs.getText().isEmpty()) + msgs.append(text); + else + msgs.append(System.lineSeparator() + text); + } + + private void initUI(String userName) { + sendText = new JTextField(20); + sendText.addKeyListener(new KeyListener() { + + @Override + public void keyTyped(KeyEvent e) { + } + + @Override + public void keyReleased(KeyEvent e) { + } + + @Override + public void keyPressed(KeyEvent e) { + if (e.getKeyCode() == KeyEvent.VK_ENTER) + listener.sendMessage(); + } + }); + JButton sendButton = new JButton("Send"); + sendButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent event) { + listener.sendMessage(); + } + }); + msgs = new JTextArea(5, 27); + msgs.setEditable(false); + msgs.setMaximumSize(null); + + DefaultListModel listModel = new DefaultListModel(); + listModel.addElement("Alice"); + listModel.addElement("Bob"); + statusList = new JList(listModel); + statusList.setEnabled(false); + statusList.setLayoutOrientation(JList.HORIZONTAL_WRAP); + statusList.setVisibleRowCount(-1); + + statusList.setCellRenderer(new DefaultListCellRenderer() { + + private static final long serialVersionUID = 9019815674349211344L; + private JLabel label = new JLabel(); + private Color textSelectionColor = Color.BLACK; + private Color backgroundSelectionColor = Color.CYAN; + private Color textNonSelectionColor = Color.BLACK; + private Color backgroundNonSelectionColor = Color.WHITE; + + @Override + public java.awt.Component getListCellRendererComponent(javax.swing.JList list, Object value, int index, + boolean isSelected, boolean cellHasFocus) { + + String name = (String) value; + label.setIcon(createImageIcon("status-offline.png", "Offline")); + label.setText(name); + + if (isSelected) { + label.setBackground(backgroundSelectionColor); + label.setForeground(textSelectionColor); + } else { + label.setBackground(backgroundNonSelectionColor); + label.setForeground(textNonSelectionColor); + } + + return label; + }; + + }); + SpringLayout layout = new SpringLayout(); + + // put messages on (5,5) + layout.putConstraint(SpringLayout.WEST, msgs, 5, SpringLayout.WEST, getContentPane()); + layout.putConstraint(SpringLayout.NORTH, msgs, 5, SpringLayout.NORTH, getContentPane()); + + // put textfield below messages + layout.putConstraint(SpringLayout.NORTH, sendText, 5, SpringLayout.SOUTH, msgs); + layout.putConstraint(SpringLayout.WEST, sendText, 5, SpringLayout.WEST, getContentPane()); + + // put button next to the textfield + layout.putConstraint(SpringLayout.NORTH, sendButton, 5, SpringLayout.SOUTH, msgs); + layout.putConstraint(SpringLayout.WEST, sendButton, 5, SpringLayout.EAST, sendText); + + // make the frame big enough to fit all in + layout.putConstraint(SpringLayout.EAST, getContentPane(), 10, SpringLayout.EAST, statusList); + layout.putConstraint(SpringLayout.SOUTH, getContentPane(), 10, SpringLayout.SOUTH, sendText); + + layout.putConstraint(SpringLayout.NORTH, statusList, 5, SpringLayout.NORTH, getContentPane()); + layout.putConstraint(SpringLayout.WEST, statusList, 15, SpringLayout.EAST, sendButton); + + getContentPane().setLayout(layout); + + getContentPane().add(msgs); + getContentPane().add(sendText); + getContentPane().add(sendButton); + getContentPane().add(statusList); + + setTitle("Chat - " + userName); + // setSize(300, 200); + setLocationRelativeTo(null); + setDefaultCloseOperation(EXIT_ON_CLOSE); + + pack(); + setVisible(true); + } + + /** Returns an ImageIcon, or null if the path was invalid. */ + protected ImageIcon createImageIcon(String path, String description) { + java.net.URL imgURL = getClass().getResource(path); + if (imgURL != null) { + return new ImageIcon(imgURL, description); + } else { + System.err.println("Couldn't find file: " + path); + return null; + } + } +} From e07c04ae2500be119543654be1e0311a6b3dd83a Mon Sep 17 00:00:00 2001 From: Tobias Becker Date: Sun, 27 Dec 2015 11:55:32 +0100 Subject: [PATCH 010/161] added a one time handler, that is executed on the first change and discarded afterwards --- .../main/java/javareact/common/types/ChangeEvent.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/Dream2/src/main/java/javareact/common/types/ChangeEvent.java b/Dream2/src/main/java/javareact/common/types/ChangeEvent.java index abfd586..f708a06 100644 --- a/Dream2/src/main/java/javareact/common/types/ChangeEvent.java +++ b/Dream2/src/main/java/javareact/common/types/ChangeEvent.java @@ -10,6 +10,7 @@ public class ChangeEvent implements ProxyChangeListener { private T latest; private Set> handlers = new HashSet>(); + private Set> onetimehandlers = new HashSet>(); public ChangeEvent(ProxyGenerator p) { p.getProxy().addProxyChangeListener(this); @@ -19,19 +20,24 @@ public void addHandler(ChangeEventHandler handler) { handlers.add(handler); } + public void addOneTimeHandler(ChangeEventHandler handler) { + onetimehandlers.add(handler); + } + public void removeHandler(ChangeEventHandler handler) { handlers.remove(handler); } private void notifyHandler(T oldValue) { handlers.forEach(h -> h.handle(oldValue, latest)); + onetimehandlers.forEach(h -> h.handle(oldValue, latest)); + onetimehandlers.clear(); } @Override public void update(EventProxyPair pair) { String method = pair.getProxy().method; - Attribute temp = pair.getEventPacket().getEvent() - .getAttributeFor(method); + Attribute temp = pair.getEventPacket().getEvent().getAttributeFor(method); System.out.println("update called"); if (latest == null || !latest.equals(temp.getValue())) { T old = latest; From 1964f19c7d406479a25a6dc08fc6dd86474334d1 Mon Sep 17 00:00:00 2001 From: Tobias Becker Date: Sun, 27 Dec 2015 11:56:08 +0100 Subject: [PATCH 011/161] refactored to server-client architecture (with unresolved issues) --- Dream2/src/test/java/javareact/chat/Chat.java | 93 +++++++---- .../test/java/javareact/chat/ChatServer.java | 146 +++++++++++++++++- 2 files changed, 204 insertions(+), 35 deletions(-) diff --git a/Dream2/src/test/java/javareact/chat/Chat.java b/Dream2/src/test/java/javareact/chat/Chat.java index b114615..b33844a 100644 --- a/Dream2/src/test/java/javareact/chat/Chat.java +++ b/Dream2/src/test/java/javareact/chat/Chat.java @@ -1,60 +1,83 @@ package javareact.chat; import java.awt.EventQueue; + import javareact.common.Consts; +import javareact.common.types.ChangeEventHandler; import javareact.common.types.RemoteVar; import javareact.common.types.Signal; import javareact.common.types.Var; -public class Chat{ +public class Chat { private RemoteVar remoteMessages; - private Var messages; + private Var myMessages; private String userName; private ChatGUI gui; public Chat(String username) throws Exception { - Consts.hostName = username; - messages = new Var("message", ""); - remoteMessages = new RemoteVar("message@*"); + this.userName = username; - // split Sender + Message into two Signals (sHost + sMessage) - Signal sHost = new Signal("sHost", () -> { - if (remoteMessages.get() == null) - return ""; - else - return remoteMessages.get().split(":", 2)[0]; - }, remoteMessages); - - Signal sMessage = new Signal("sMessage", () -> { - if (remoteMessages.get() == null) + Consts.hostName = userName; + // Establish new session with server + RemoteVar id = new RemoteVar(ChatServer.NAME, ChatServer.NEW_ID); + RemoteVar var = new RemoteVar(ChatServer.NAME, ChatServer.NEW_VAR); + Signal setup = new Signal("setup", () -> { + if (id.get() == null || var.get() == null) return ""; - else - return remoteMessages.get().split(":", 2)[1]; - }, remoteMessages); - - Signal display = new Signal("display", () -> { - if (!sHost.get().equals(userName)) - return sMessage.get(); else - return ""; - }, sHost, sMessage); - - display.change().addHandler((oldValue, newValue) -> { - if (!sHost.get().equals(userName)) - gui.displayMessage(sHost.get() + ": " + newValue); + return id.get() + "@" + var.get(); + }, id, var); + ChangeEventHandler ceh = (o, n) -> { + String[] t = n.split("@", 2); + setup(t[0], t[1]); + }; + setup.change().addOneTimeHandler((o, n) -> { + if (n.equals("")) { + setup.change().addOneTimeHandler(ceh); + } else + ceh.handle(o, n); }); - - - this.userName = username; + System.out.println("Setup: Waiting for Setup information from Server ..."); + } + + private void setup(String setup_id, String setup_var) { + System.out.println("Setup: Setup information received!"); + System.out.println("Setup: ID: " + setup_id); + System.out.println("Setup: VAR: " + setup_var); + String serverVar = ChatServer.getRandom(); + // Consts.hostName = setup_id; + Var init = new Var(setup_var, "message@" + userName + "@" + serverVar); + + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + // should have correct value now + System.out.println("init: " + init.get()); + Consts.hostName = userName; + myMessages = new Var("message", ""); + + remoteMessages = new RemoteVar(serverVar + "@" + ChatServer.NAME); + + Signal display = new Signal("display", () -> { + if (remoteMessages.get() != null) + return remoteMessages.get(); + else + return ""; + }, remoteMessages); gui = new ChatGUI(userName); gui.setListener(this); - } + display.change().addHandler((oldValue, newValue) -> { + gui.displayMessage(newValue); + }); + } protected void sendMessage() { - messages.set(userName + ":" + gui.getTypedText()); + myMessages.set(userName + ":" + gui.getTypedText()); gui.displayMessage("You: " + gui.getTypedText()); gui.resetTypedText(); } @@ -63,14 +86,16 @@ public static void main(String[] args) { try { if (args.length < 1) System.out.println("username missing"); - + // Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).setLevel(Level.ALL); EventQueue.invokeLater(new Runnable() { @Override public void run() { try { + new Chat(args[0]); } catch (Exception e) { + e.printStackTrace(); } } diff --git a/Dream2/src/test/java/javareact/chat/ChatServer.java b/Dream2/src/test/java/javareact/chat/ChatServer.java index 5e17ea4..e2f9dd5 100644 --- a/Dream2/src/test/java/javareact/chat/ChatServer.java +++ b/Dream2/src/test/java/javareact/chat/ChatServer.java @@ -1,25 +1,169 @@ package javareact.chat; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; +import java.util.HashMap; import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Random; import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; import javareact.common.Consts; +import javareact.common.types.RemoteVar; +import javareact.common.types.Signal; +import javareact.common.types.Var; import javareact.server.ServerLauncher; import javareact.token_service.TokenServiceLauncher; public class ChatServer { + public static final String NEW_ID = "NewSessionID"; + public static final String NEW_VAR = "NewSessionVAR"; + public static final String NAME = "ChatServer"; + private boolean serverStarted = false; private boolean tokenServiceStarted = false; + private Map> clientVars; + private Var> clients; + + private Var newSessionID; + private Var newSessionVAR; + private static Random r = new Random(); + + private List processedIDs = new ArrayList(); + public static void main(String[] args) { new ChatServer().start(); } + private void initServer() { + Consts.hostName = NAME; + + clients = new Var>("RegisteredClients", new LinkedList()); + clientVars = new HashMap>(); + newSessionID = new Var(NEW_ID, ""); + newSessionVAR = new Var(NEW_VAR, ""); + changeNewSession(); + } + + /** + * Provide new Host ID and Variable name and listen to it for a new client + */ + private void changeNewSession() { + String id = getRandom(); + String var = getRandom(); + RemoteVar listener = new RemoteVar("*", var); + Signal listenerSignal = new Signal("listener", () -> { + if (listener.get() == null) + return ""; + else + return listener.get(); + }, listener); + listenerSignal.change().addOneTimeHandler((o, msg) -> { + System.out.println("new session handler"); + changeNewSession(); + processNewSession(id, var, msg); + }); + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + newSessionID.set(id); + newSessionVAR.set(var); + + try { + Thread.sleep(10000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + processedIDs.add(id); + changeNewSession(); + } + + private void processNewSession(String id, String var, String msg) { + if (!processedIDs.contains(id)) { + processedIDs.add(id); + // variable@name@serverVar + String[] temp = msg.split("@", 3); + registerClient(temp[1], temp[0], temp[2]); + } else + System.out.println("New message(\"" + msg + "\") on already discarded channel " + id + "@" + var); + } + + /** + * Registers a new chat client with its name and the name of the variable it + * is sending messages on + * + * @param clientName + * the name of the client + * @param clientVar + * the name of the variable, must be of type String + * @param serverVar + * @return the name of the variable on which the server will provide + * messages to the client + */ + public void registerClient(String clientName, String clientVar, String serverVar) { + System.out.println("Register: " + clientName + "(" + clientVar + ") -> " + serverVar); + RemoteVar remote = new RemoteVar(clientName, clientVar); + Signal listen = new Signal(serverVar + "listen", () -> { + if (remote.get() == null) + return ""; + else + return remote.get(); + }, remote); + + // handler for "client writes something" + listen.change().addHandler((oldValue, newValue) -> clientWrote(clientName, newValue)); + + // create new var for sending messages to this client + clientVars.put(clientName, new Var(serverVar, "")); + + // add client as registered + clients.modify((old) -> old.add(clientName)); + } + + /** + * called when the server receives a new message from a client + * + * @param name + * @param text + */ + private void clientWrote(String name, String text) { + System.out.println("Server: " + name + " -> " + text); + // TODO propagate to correct clients (chat rooms etc.) + clientVars.forEach((client, var) -> { + if (client != name) + var.set(name + ": " + text); + }); + } + + /** + * @return random String hashed with SHA-256 + */ + public static String getRandom() { + try { + MessageDigest messageDigest = MessageDigest.getInstance("SHA-256"); + messageDigest.update(String.valueOf(r.nextLong()).getBytes()); + String encryptedString = new String(messageDigest.digest()); + return encryptedString.replace("@", ""); + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + } + return ""; + } + public void start() { startServerIfNeeded(); startTokenServiceIfNeeded(); - Consts.hostName = "ChatServer"; + Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).setLevel(Level.ALL); + initServer(); while (true) { try { From 3ae40d6ae908d9669ee81bc726da73e075fe053f Mon Sep 17 00:00:00 2001 From: Tobias Becker Date: Fri, 1 Jan 2016 17:18:01 +0100 Subject: [PATCH 012/161] changed random string to be human readable --- .../test/java/javareact/chat/ChatServer.java | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/Dream2/src/test/java/javareact/chat/ChatServer.java b/Dream2/src/test/java/javareact/chat/ChatServer.java index e2f9dd5..fb5294a 100644 --- a/Dream2/src/test/java/javareact/chat/ChatServer.java +++ b/Dream2/src/test/java/javareact/chat/ChatServer.java @@ -1,14 +1,13 @@ package javareact.chat; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; +import java.math.BigInteger; +import java.security.SecureRandom; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; -import java.util.Random; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; @@ -33,7 +32,7 @@ public class ChatServer { private Var newSessionID; private Var newSessionVAR; - private static Random r = new Random(); + private static SecureRandom r = new SecureRandom(); private List processedIDs = new ArrayList(); @@ -147,15 +146,7 @@ private void clientWrote(String name, String text) { * @return random String hashed with SHA-256 */ public static String getRandom() { - try { - MessageDigest messageDigest = MessageDigest.getInstance("SHA-256"); - messageDigest.update(String.valueOf(r.nextLong()).getBytes()); - String encryptedString = new String(messageDigest.digest()); - return encryptedString.replace("@", ""); - } catch (NoSuchAlgorithmException e) { - e.printStackTrace(); - } - return ""; + return new BigInteger(130, r).toString(32); } public void start() { From f8648d2966b75c33c65e3f8d31fae1d36ea1eb97 Mon Sep 17 00:00:00 2001 From: Tobias Becker Date: Fri, 1 Jan 2016 17:39:27 +0100 Subject: [PATCH 013/161] applied some code formatting --- Dream2/.settings/org.eclipse.jdt.core.prefs | 284 +++ Dream2/.settings/org.eclipse.jdt.ui.prefs | 66 + .../org.eclipse.ltk.core.refactoring.prefs | 2 + .../client/ClientEventForwarder.java | 276 +-- .../client/ClientSubscriptionTable.java | 148 +- .../javareact/client/ConnectionManager.java | 160 +- .../java/javareact/client/QueueManager.java | 138 +- .../java/javareact/client/Subscriber.java | 16 +- .../javareact/common/ConsistencyType.java | 36 +- .../main/java/javareact/common/Consts.java | 88 +- .../common/packets/AdvertisementPacket.java | 162 +- .../javareact/common/packets/EventPacket.java | 184 +- .../common/packets/SubscriptionPacket.java | 103 +- .../common/packets/content/AdvType.java | 2 +- .../common/packets/content/Advertisement.java | 124 +- .../common/packets/content/Attribute.java | 114 +- .../common/packets/content/Constraint.java | 293 +-- .../common/packets/content/ConstraintOp.java | 96 +- .../common/packets/content/Event.java | 245 +-- .../common/packets/content/SubType.java | 2 +- .../common/packets/content/Subscription.java | 262 +-- .../common/packets/content/Value.java | 304 +-- .../common/packets/content/ValueType.java | 2 +- .../registry/RegistryAdvertisePacket.java | 86 +- .../packets/token_service/TokenAckPacket.java | 33 +- .../TokenServiceAdvertisePacket.java | 88 +- .../common/types/AbstractReactive.java | 189 +- .../javareact/common/types/BooleanProxy.java | 14 +- .../javareact/common/types/DoubleProxy.java | 14 +- .../common/types/EventProxyPair.java | 34 +- .../javareact/common/types/ImpactsOn.java | 2 +- .../javareact/common/types/IntegerProxy.java | 12 +- .../javareact/common/types/ListProxy.java | 60 +- .../javareact/common/types/Observable.java | 52 +- .../common/types/ObservableBool.java | 22 +- .../common/types/ObservableDouble.java | 22 +- .../common/types/ObservableInteger.java | 22 +- .../common/types/ObservableList.java | 190 +- .../common/types/ObservableString.java | 330 ++-- .../java/javareact/common/types/Proxy.java | 239 +-- .../common/types/ProxyChangeListener.java | 12 +- .../common/types/ProxyGenerator.java | 15 +- .../java/javareact/common/types/Reactive.java | 13 +- .../common/types/ReactiveBoolean.java | 18 +- .../common/types/ReactiveChangeListener.java | 5 +- .../common/types/ReactiveDouble.java | 18 +- .../common/types/ReactiveInteger.java | 18 +- .../javareact/common/types/ReactiveList.java | 64 +- .../common/types/ReactiveString.java | 18 +- .../javareact/common/types/RemoteVar.java | 4 +- .../java/javareact/common/types/Signal.java | 10 +- .../javareact/common/types/StringProxy.java | 306 +-- .../main/java/javareact/common/types/Var.java | 89 +- .../javareact/examples/StartRegistry.java | 6 +- .../java/javareact/examples/StartServer.java | 6 +- .../javareact/examples/StartTokenService.java | 40 +- .../examples/local/LocalExample.java | 101 +- .../examples/local/LocalExample2.java | 62 +- .../examples/local/LocalExample3.java | 24 +- .../examples/local/LocalExample4.java | 101 +- .../examples/remote/RemoteObservable.java | 38 +- .../examples/remote/RemoteReactive.java | 100 +- .../java/javareact/registry/Registry.java | 142 +- .../javareact/registry/RegistryLauncher.java | 99 +- .../javareact/server/AdvertisementTable.java | 73 +- .../javareact/server/DependencyDetector.java | 443 ++--- .../server/ServerEventForwarder.java | 423 ++--- .../java/javareact/server/ServerLauncher.java | 66 +- .../javareact/server/SubscriptionTable.java | 89 +- .../javareact/server/WaitRecommendations.java | 59 +- .../FinalExpressionsDetector.java | 188 +- .../javareact/token_service/TokenService.java | 216 +-- .../token_service/TokenServiceLauncher.java | 106 +- Dream2/src/test/java/javareact/LocalTest.java | 85 +- .../src/test/java/javareact/LocalTest2.java | 177 +- .../src/test/java/javareact/LocalTestOld.java | 18 +- .../test/java/javareact/RegressionTests.java | 4 +- .../test/java/javareact/RemoteObservable.java | 10 +- .../src/test/java/javareact/RemoteTest.java | 5 +- .../src/test/java/javareact/chat/ChatGUI.java | 14 +- .../javareact/financial/FinancialApp.java | 37 +- .../java/javareact/financial/InputModel.java | 42 +- .../test/java/javareact/financial/Model1.java | 19 +- .../test/java/javareact/financial/Model2.java | 11 +- .../test/java/javareact/financial/Model3.java | 11 +- .../server/DependencyDetectorTest.java | 1634 ++++++++--------- .../FinalExpressionsDetectorTest.java | 445 ++--- 87 files changed, 5204 insertions(+), 4796 deletions(-) create mode 100644 Dream2/.settings/org.eclipse.jdt.ui.prefs create mode 100644 Dream2/.settings/org.eclipse.ltk.core.refactoring.prefs diff --git a/Dream2/.settings/org.eclipse.jdt.core.prefs b/Dream2/.settings/org.eclipse.jdt.core.prefs index 714351a..3924d2b 100644 --- a/Dream2/.settings/org.eclipse.jdt.core.prefs +++ b/Dream2/.settings/org.eclipse.jdt.core.prefs @@ -3,3 +3,287 @@ org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 org.eclipse.jdt.core.compiler.compliance=1.8 org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning org.eclipse.jdt.core.compiler.source=1.8 +org.eclipse.jdt.core.formatter.align_type_members_on_columns=false +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=0 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16 +org.eclipse.jdt.core.formatter.alignment_for_assignment=0 +org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16 +org.eclipse.jdt.core.formatter.alignment_for_compact_if=16 +org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80 +org.eclipse.jdt.core.formatter.alignment_for_enum_constants=0 +org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16 +org.eclipse.jdt.core.formatter.alignment_for_method_declaration=0 +org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16 +org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_resources_in_try=80 +org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16 +org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch=16 +org.eclipse.jdt.core.formatter.blank_lines_after_imports=1 +org.eclipse.jdt.core.formatter.blank_lines_after_package=1 +org.eclipse.jdt.core.formatter.blank_lines_before_field=0 +org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0 +org.eclipse.jdt.core.formatter.blank_lines_before_imports=1 +org.eclipse.jdt.core.formatter.blank_lines_before_member_type=1 +org.eclipse.jdt.core.formatter.blank_lines_before_method=1 +org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1 +org.eclipse.jdt.core.formatter.blank_lines_before_package=0 +org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1 +org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=1 +org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_lambda_body=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line +org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false +org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false +org.eclipse.jdt.core.formatter.comment.format_block_comments=true +org.eclipse.jdt.core.formatter.comment.format_header=false +org.eclipse.jdt.core.formatter.comment.format_html=true +org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true +org.eclipse.jdt.core.formatter.comment.format_line_comments=true +org.eclipse.jdt.core.formatter.comment.format_source_code=true +org.eclipse.jdt.core.formatter.comment.indent_parameter_description=true +org.eclipse.jdt.core.formatter.comment.indent_root_tags=true +org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert +org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=insert +org.eclipse.jdt.core.formatter.comment.line_length=80 +org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries=true +org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries=true +org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments=false +org.eclipse.jdt.core.formatter.compact_else_if=true +org.eclipse.jdt.core.formatter.continuation_indentation=2 +org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2 +org.eclipse.jdt.core.formatter.disabling_tag=@formatter\:off +org.eclipse.jdt.core.formatter.enabling_tag=@formatter\:on +org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false +org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true +org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true +org.eclipse.jdt.core.formatter.indent_empty_lines=false +org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true +org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true +org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true +org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=false +org.eclipse.jdt.core.formatter.indentation.size=4 +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_label=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_after_type_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert +org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert +org.eclipse.jdt.core.formatter.insert_space_after_lambda_arrow=insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources=insert +org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert +org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_lambda_arrow=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert +org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert +org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert +org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.join_lines_in_comments=true +org.eclipse.jdt.core.formatter.join_wrapped_lines=true +org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false +org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false +org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false +org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false +org.eclipse.jdt.core.formatter.lineSplit=120 +org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false +org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false +org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0 +org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1 +org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=true +org.eclipse.jdt.core.formatter.tabulation.char=tab +org.eclipse.jdt.core.formatter.tabulation.size=4 +org.eclipse.jdt.core.formatter.use_on_off_tags=false +org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false +org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true +org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch=true +org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested=true diff --git a/Dream2/.settings/org.eclipse.jdt.ui.prefs b/Dream2/.settings/org.eclipse.jdt.ui.prefs new file mode 100644 index 0000000..a430aa1 --- /dev/null +++ b/Dream2/.settings/org.eclipse.jdt.ui.prefs @@ -0,0 +1,66 @@ +cleanup.add_default_serial_version_id=true +cleanup.add_generated_serial_version_id=false +cleanup.add_missing_annotations=true +cleanup.add_missing_deprecated_annotations=true +cleanup.add_missing_methods=false +cleanup.add_missing_nls_tags=false +cleanup.add_missing_override_annotations=true +cleanup.add_missing_override_annotations_interface_methods=true +cleanup.add_serial_version_id=false +cleanup.always_use_blocks=true +cleanup.always_use_parentheses_in_expressions=false +cleanup.always_use_this_for_non_static_field_access=false +cleanup.always_use_this_for_non_static_method_access=false +cleanup.convert_functional_interfaces=false +cleanup.convert_to_enhanced_for_loop=false +cleanup.correct_indentation=false +cleanup.format_source_code=false +cleanup.format_source_code_changes_only=false +cleanup.insert_inferred_type_arguments=false +cleanup.make_local_variable_final=true +cleanup.make_parameters_final=false +cleanup.make_private_fields_final=true +cleanup.make_type_abstract_if_missing_method=false +cleanup.make_variable_declarations_final=false +cleanup.never_use_blocks=false +cleanup.never_use_parentheses_in_expressions=true +cleanup.organize_imports=false +cleanup.qualify_static_field_accesses_with_declaring_class=false +cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true +cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true +cleanup.qualify_static_member_accesses_with_declaring_class=true +cleanup.qualify_static_method_accesses_with_declaring_class=false +cleanup.remove_private_constructors=true +cleanup.remove_redundant_type_arguments=true +cleanup.remove_trailing_whitespaces=false +cleanup.remove_trailing_whitespaces_all=true +cleanup.remove_trailing_whitespaces_ignore_empty=false +cleanup.remove_unnecessary_casts=true +cleanup.remove_unnecessary_nls_tags=true +cleanup.remove_unused_imports=true +cleanup.remove_unused_local_variables=false +cleanup.remove_unused_private_fields=true +cleanup.remove_unused_private_members=false +cleanup.remove_unused_private_methods=true +cleanup.remove_unused_private_types=true +cleanup.sort_members=false +cleanup.sort_members_all=false +cleanup.use_anonymous_class_creation=false +cleanup.use_blocks=false +cleanup.use_blocks_only_for_return_and_throw=false +cleanup.use_lambda=true +cleanup.use_parentheses_in_expressions=false +cleanup.use_this_for_non_static_field_access=false +cleanup.use_this_for_non_static_field_access_only_if_necessary=true +cleanup.use_this_for_non_static_method_access=false +cleanup.use_this_for_non_static_method_access_only_if_necessary=true +cleanup.use_type_arguments=false +cleanup_profile=org.eclipse.jdt.ui.default.eclipse_clean_up_profile +cleanup_settings_version=2 +eclipse.preferences.version=1 +formatter_profile=_Dream +formatter_settings_version=12 +org.eclipse.jdt.ui.ignorelowercasenames=true +org.eclipse.jdt.ui.importorder=java;javax;org;com; +org.eclipse.jdt.ui.ondemandthreshold=99 +org.eclipse.jdt.ui.staticondemandthreshold=99 diff --git a/Dream2/.settings/org.eclipse.ltk.core.refactoring.prefs b/Dream2/.settings/org.eclipse.ltk.core.refactoring.prefs new file mode 100644 index 0000000..b196c64 --- /dev/null +++ b/Dream2/.settings/org.eclipse.ltk.core.refactoring.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +org.eclipse.ltk.core.refactoring.enable.project.refactoring.history=false diff --git a/Dream2/src/main/java/javareact/client/ClientEventForwarder.java b/Dream2/src/main/java/javareact/client/ClientEventForwarder.java index 4144cfa..790d1f2 100755 --- a/Dream2/src/main/java/javareact/client/ClientEventForwarder.java +++ b/Dream2/src/main/java/javareact/client/ClientEventForwarder.java @@ -20,142 +20,144 @@ import polimi.reds.broker.routing.PacketForwarder; public class ClientEventForwarder implements PacketForwarder { - private static ClientEventForwarder self = null; - - private final ConnectionManager connectionManager; - private final ClientSubscriptionTable subTable; - private final Logger logger = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME); - - public static final ClientEventForwarder get() { - if (self == null) { - self = new ClientEventForwarder(); - } - return self; - } - - public static final void stop() { - if (self != null) { - self.stopClient(); - self = null; - } - } - - private final void stopClient() { - connectionManager.stop(); - } - - private ClientEventForwarder() { - connectionManager = new ConnectionManager(); - subTable = new ClientSubscriptionTable(); - connectionManager.registerForwarder(this, EventPacket.subject); - connectionManager.registerForwarder(this, SubscriptionPacket.subject); - } - - @Override - public Collection forwardPacket(String subject, NodeDescriptor sender, Serializable packet, Collection neighbors, Outbox outbox) { - Collection result = new ArrayList(); - if (subject.equals(EventPacket.subject)) { - assert (packet instanceof EventPacket); - logger.finer("Received an event packet " + packet); - processEventFromServer((EventPacket) packet); - } else if (subject.equals(SubscriptionPacket.subject)) { - assert (packet instanceof SubscriptionPacket); - logger.fine("Received a subscription packet " + packet); - processSubscriptionFromServer((SubscriptionPacket) packet); - } else { - assert false : subject; - } - return result; - } - - public final void sendEvent(UUID id, Event ev, Set computedFrom, boolean approvedByTokenService) { - sendEvent(id, ev, computedFrom, new HashSet(), approvedByTokenService); - } - - public final void sendEvent(UUID id, Event ev, Set computedFrom, Set finalExpressions, boolean approvedByTokenService) { - logger.finer("Sending an event " + ev); - // Local forward occurs only if glitch freedom is not guaranteed. - // Indeed, to ensure glitch freedom, all events, including local ones, - // need to be pass through the server before being delivered - if (Consts.consistencyType != ConsistencyType.GLITCH_FREE && Consts.consistencyType != ConsistencyType.ATOMIC) { - for (Subscriber sub : subTable.getMatchingSubscribers(ev)) { - sub.notifyValueChanged(new EventPacket(ev, id, computedFrom, approvedByTokenService)); - } - } - if (subTable.needsToDeliverToServer(ev) || ev.isPersistent()) { - connectionManager.sendEvent(id, ev, computedFrom, finalExpressions, approvedByTokenService); - } - } - - public final void advertise(Advertisement adv, boolean isPublic) { - logger.fine("Sending advertisement " + adv); - connectionManager.sendAdvertisement(adv, isPublic); - } - - public final void unadvertise(Advertisement adv, boolean isPublic) { - logger.fine("Sending unadvertisement " + adv); - connectionManager.sendUnadvertisement(adv, isPublic); - } - - public final void advertise(Advertisement adv, Set subs, boolean isPublic) { - logger.fine("Sending advertisement " + adv + " with subscriptions " + subs); - connectionManager.sendAdvertisement(adv, subs, isPublic); - } - - public final void unadvertise(Advertisement adv, Set subs, boolean isPublic) { - logger.fine("Sending unadvertisement " + adv + " with subscriptions " + subs); - connectionManager.sendUnadvertisement(adv, isPublic); - } - - public final void addSubscription(Subscriber subscriber, Subscription subscription) { - logger.fine("Adding subscription " + subscription); - subTable.addSubscription(subscriber, subscription); - if (needToSendToServer(subscription)) { - connectionManager.sendSubscription(subscription); - } - } - - public final void removeSubscription(Subscriber subscriber, Subscription subscription) { - logger.fine("Adding subscription " + subscription); - subTable.addSubscription(subscriber, subscription); - if (needToSendToServer(subscription)) { - connectionManager.sendSubscription(subscription); - } - } - - private final boolean needToSendToServer(Subscription sub) { - return // - !isLocal(sub) || // - Consts.consistencyType == ConsistencyType.GLITCH_FREE || // - Consts.consistencyType == ConsistencyType.ATOMIC; - } - - private final boolean isLocal(Subscription sub) { - return sub.getHostId().equals(Consts.hostName); - } - - private final void processEventFromServer(EventPacket evPkt) { - for (Subscriber sub : subTable.getMatchingSubscribers(evPkt.getEvent())) { - sub.notifyValueChanged(evPkt); - } - if (Consts.consistencyType == ConsistencyType.GLITCH_FREE || Consts.consistencyType == ConsistencyType.ATOMIC) { - for (Subscriber sub : subTable.getSignatureOnlyMatchingSubscribers(evPkt.getEvent())) { - sub.notifyValueChanged(evPkt); - } - } - } - - private final void processSubscriptionFromServer(SubscriptionPacket subPkt) { - switch (subPkt.getSubType()) { - case SUB: - subTable.addServerSubscription(subPkt.getSubscription()); - break; - case UNSUB: - subTable.removeServerSubscription(subPkt.getSubscription()); - break; - default: - assert false : subPkt.getSubType(); - } - } + private static ClientEventForwarder self = null; + + private final ConnectionManager connectionManager; + private final ClientSubscriptionTable subTable; + private final Logger logger = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME); + + public static final ClientEventForwarder get() { + if (self == null) { + self = new ClientEventForwarder(); + } + return self; + } + + public static final void stop() { + if (self != null) { + self.stopClient(); + self = null; + } + } + + private final void stopClient() { + connectionManager.stop(); + } + + private ClientEventForwarder() { + connectionManager = new ConnectionManager(); + subTable = new ClientSubscriptionTable(); + connectionManager.registerForwarder(this, EventPacket.subject); + connectionManager.registerForwarder(this, SubscriptionPacket.subject); + } + + @Override + public Collection forwardPacket(String subject, NodeDescriptor sender, Serializable packet, + Collection neighbors, Outbox outbox) { + Collection result = new ArrayList(); + if (subject.equals(EventPacket.subject)) { + assert (packet instanceof EventPacket); + logger.finer("Received an event packet " + packet); + processEventFromServer((EventPacket) packet); + } else if (subject.equals(SubscriptionPacket.subject)) { + assert (packet instanceof SubscriptionPacket); + logger.fine("Received a subscription packet " + packet); + processSubscriptionFromServer((SubscriptionPacket) packet); + } else { + assert false : subject; + } + return result; + } + + public final void sendEvent(UUID id, Event ev, Set computedFrom, boolean approvedByTokenService) { + sendEvent(id, ev, computedFrom, new HashSet(), approvedByTokenService); + } + + public final void sendEvent(UUID id, Event ev, Set computedFrom, Set finalExpressions, + boolean approvedByTokenService) { + logger.finer("Sending an event " + ev); + // Local forward occurs only if glitch freedom is not guaranteed. + // Indeed, to ensure glitch freedom, all events, including local ones, + // need to be pass through the server before being delivered + if (Consts.consistencyType != ConsistencyType.GLITCH_FREE && Consts.consistencyType != ConsistencyType.ATOMIC) { + for (Subscriber sub : subTable.getMatchingSubscribers(ev)) { + sub.notifyValueChanged(new EventPacket(ev, id, computedFrom, approvedByTokenService)); + } + } + if (subTable.needsToDeliverToServer(ev) || ev.isPersistent()) { + connectionManager.sendEvent(id, ev, computedFrom, finalExpressions, approvedByTokenService); + } + } + + public final void advertise(Advertisement adv, boolean isPublic) { + logger.fine("Sending advertisement " + adv); + connectionManager.sendAdvertisement(adv, isPublic); + } + + public final void unadvertise(Advertisement adv, boolean isPublic) { + logger.fine("Sending unadvertisement " + adv); + connectionManager.sendUnadvertisement(adv, isPublic); + } + + public final void advertise(Advertisement adv, Set subs, boolean isPublic) { + logger.fine("Sending advertisement " + adv + " with subscriptions " + subs); + connectionManager.sendAdvertisement(adv, subs, isPublic); + } + + public final void unadvertise(Advertisement adv, Set subs, boolean isPublic) { + logger.fine("Sending unadvertisement " + adv + " with subscriptions " + subs); + connectionManager.sendUnadvertisement(adv, isPublic); + } + + public final void addSubscription(Subscriber subscriber, Subscription subscription) { + logger.fine("Adding subscription " + subscription); + subTable.addSubscription(subscriber, subscription); + if (needToSendToServer(subscription)) { + connectionManager.sendSubscription(subscription); + } + } + + public final void removeSubscription(Subscriber subscriber, Subscription subscription) { + logger.fine("Adding subscription " + subscription); + subTable.addSubscription(subscriber, subscription); + if (needToSendToServer(subscription)) { + connectionManager.sendSubscription(subscription); + } + } + + private final boolean needToSendToServer(Subscription sub) { + return // + !isLocal(sub) || // + Consts.consistencyType == ConsistencyType.GLITCH_FREE || // + Consts.consistencyType == ConsistencyType.ATOMIC; + } + + private final boolean isLocal(Subscription sub) { + return sub.getHostId().equals(Consts.hostName); + } + + private final void processEventFromServer(EventPacket evPkt) { + for (Subscriber sub : subTable.getMatchingSubscribers(evPkt.getEvent())) { + sub.notifyValueChanged(evPkt); + } + if (Consts.consistencyType == ConsistencyType.GLITCH_FREE || Consts.consistencyType == ConsistencyType.ATOMIC) { + for (Subscriber sub : subTable.getSignatureOnlyMatchingSubscribers(evPkt.getEvent())) { + sub.notifyValueChanged(evPkt); + } + } + } + + private final void processSubscriptionFromServer(SubscriptionPacket subPkt) { + switch (subPkt.getSubType()) { + case SUB: + subTable.addServerSubscription(subPkt.getSubscription()); + break; + case UNSUB: + subTable.removeServerSubscription(subPkt.getSubscription()); + break; + default: + assert false : subPkt.getSubType(); + } + } } diff --git a/Dream2/src/main/java/javareact/client/ClientSubscriptionTable.java b/Dream2/src/main/java/javareact/client/ClientSubscriptionTable.java index 92ebe08..8b33325 100755 --- a/Dream2/src/main/java/javareact/client/ClientSubscriptionTable.java +++ b/Dream2/src/main/java/javareact/client/ClientSubscriptionTable.java @@ -11,88 +11,90 @@ import javareact.common.packets.content.Subscription; final class ClientSubscriptionTable { - private final Map> subs = new HashMap>(); - private final Collection serverSubscriptions = new ArrayList(); + private final Map> subs = new HashMap>(); + private final Collection serverSubscriptions = new ArrayList(); - final void addSubscription(Subscriber subscriber, Subscription sub) { - Collection subsList = subs.get(subscriber); - if (subsList == null) { - subsList = new ArrayList(); - subs.put(subscriber, subsList); - } - subsList.add(sub); - } + final void addSubscription(Subscriber subscriber, Subscription sub) { + Collection subsList = subs.get(subscriber); + if (subsList == null) { + subsList = new ArrayList(); + subs.put(subscriber, subsList); + } + subsList.add(sub); + } - final void addSubscriptions(Subscriber subscriber, Collection subs) { - for (Subscription sub : subs) { - addSubscription(subscriber, sub); - } - } + final void addSubscriptions(Subscriber subscriber, Collection subs) { + for (Subscription sub : subs) { + addSubscription(subscriber, sub); + } + } - final void addServerSubscription(Subscription sub) { - serverSubscriptions.add(sub); - } + final void addServerSubscription(Subscription sub) { + serverSubscriptions.add(sub); + } - final void removeSubscription(Subscriber subscriber, Subscription sub) { - Collection subscriptions = subs.get(subscriber); - if (subscriptions == null) return; - subscriptions.remove(sub); - if (subscriptions.isEmpty()) { - subs.remove(subscriber); - } - } + final void removeSubscription(Subscriber subscriber, Subscription sub) { + Collection subscriptions = subs.get(subscriber); + if (subscriptions == null) + return; + subscriptions.remove(sub); + if (subscriptions.isEmpty()) { + subs.remove(subscriber); + } + } - final void removeSubscriptions(Subscriber subscriber, Collection subs) { - for (Subscription sub : subs) { - removeSubscription(subscriber, sub); - } - } + final void removeSubscriptions(Subscriber subscriber, Collection subs) { + for (Subscription sub : subs) { + removeSubscription(subscriber, sub); + } + } - final void removeServerSubscription(Subscription sub) { - serverSubscriptions.remove(sub); - } + final void removeServerSubscription(Subscription sub) { + serverSubscriptions.remove(sub); + } - final Set getMatchingSubscribers(Event ev) { - Set subscribers = new HashSet(); - subscribersLoop: for (Subscriber subscriber : subs.keySet()) { - for (Subscription sub : subs.get(subscriber)) { - if (sub.isSatisfiedBy(ev)) { - subscribers.add(subscriber); - continue subscribersLoop; - } - } - } - return subscribers; - } + final Set getMatchingSubscribers(Event ev) { + Set subscribers = new HashSet(); + subscribersLoop: for (Subscriber subscriber : subs.keySet()) { + for (Subscription sub : subs.get(subscriber)) { + if (sub.isSatisfiedBy(ev)) { + subscribers.add(subscriber); + continue subscribersLoop; + } + } + } + return subscribers; + } - final Set getSignatureOnlyMatchingSubscribers(Event ev) { - Set subscribers = new HashSet(); - subscribersLoop: for (Subscriber subscriber : subs.keySet()) { - boolean matchesSignature = false; - for (Subscription sub : subs.get(subscriber)) { - if (sub.isSatisfiedBy(ev)) { - continue subscribersLoop; - } else if (sub.matchesOnlySignatureOf(ev)) { - matchesSignature = true; - } - } - if (matchesSignature) { - subscribers.add(subscriber); - } - } - return subscribers; - } + final Set getSignatureOnlyMatchingSubscribers(Event ev) { + Set subscribers = new HashSet(); + subscribersLoop: for (Subscriber subscriber : subs.keySet()) { + boolean matchesSignature = false; + for (Subscription sub : subs.get(subscriber)) { + if (sub.isSatisfiedBy(ev)) { + continue subscribersLoop; + } else if (sub.matchesOnlySignatureOf(ev)) { + matchesSignature = true; + } + } + if (matchesSignature) { + subscribers.add(subscriber); + } + } + return subscribers; + } - final boolean needsToDeliverToServer(Event ev) { - for (Subscription sub : serverSubscriptions) { - if (sub.isSatisfiedBy(ev)) return true; - } - return false; - } + final boolean needsToDeliverToServer(Event ev) { + for (Subscription sub : serverSubscriptions) { + if (sub.isSatisfiedBy(ev)) + return true; + } + return false; + } - @Override - public String toString() { - return "ClientSubscriptionTable [\n subs=" + subs + "\n serverSubscriptions=" + serverSubscriptions + "\n]"; - } + @Override + public String toString() { + return "ClientSubscriptionTable [\n subs=" + subs + "\n serverSubscriptions=" + serverSubscriptions + "\n]"; + } } diff --git a/Dream2/src/main/java/javareact/client/ConnectionManager.java b/Dream2/src/main/java/javareact/client/ConnectionManager.java index 4e72d1f..6b82514 100755 --- a/Dream2/src/main/java/javareact/client/ConnectionManager.java +++ b/Dream2/src/main/java/javareact/client/ConnectionManager.java @@ -28,84 +28,86 @@ import polimi.reds.broker.routing.PacketForwarder; class ConnectionManager { - private final Overlay overlay; - private final GenericRouter router; - - ConnectionManager() { - Transport tr = null; - try { - tr = new TCPTransport(); - } catch (IOException e) { - e.printStackTrace(); - } - TopologyManager tm = new SimpleTopologyManager(); - overlay = new GenericOverlay(tm, tr, false); - router = new GenericRouter(overlay); - overlay.start(); - try { - overlay.addNeighbor(Consts.serverAddr); - } catch (ConnectException | MalformedURLException | NotRunningException e) { - e.printStackTrace(); - } - } - - final void sendEvent(UUID id, Event event, Set computedFrom, Set finalExpressions, boolean approvedByTokenService) { - EventPacket pkt = new EventPacket(event, id, computedFrom, approvedByTokenService); - for (String finalExpression : finalExpressions) { - pkt.addFinalExpression(finalExpression); - } - send(EventPacket.subject, pkt); - } - - final void sendSubscription(Subscription sub) { - SubscriptionPacket pkt = new SubscriptionPacket(sub, SubType.SUB); - send(SubscriptionPacket.subject, pkt); - } - - final void sendUnsubscription(Subscription sub) { - SubscriptionPacket pkt = new SubscriptionPacket(sub, SubType.UNSUB); - send(SubscriptionPacket.subject, pkt); - } - - final void sendAdvertisement(Advertisement adv, boolean isPublic) { - sendAdvertisement(adv, AdvType.ADV, null, isPublic); - } - - final void sendAdvertisement(Advertisement adv, Set subs, boolean isPublic) { - sendAdvertisement(adv, AdvType.ADV, subs, isPublic); - } - - final void sendUnadvertisement(Advertisement adv, boolean isPublic) { - sendAdvertisement(adv, AdvType.UNADV, null, isPublic); - } - - final void sendUnadvertisement(Advertisement adv, Set subs, boolean isPublic) { - sendAdvertisement(adv, AdvType.UNADV, subs, isPublic); - } - - private final void sendAdvertisement(Advertisement adv, AdvType advType, Set subs, boolean isPublic) { - AdvertisementPacket pkt = (subs != null) ? new AdvertisementPacket(adv, advType, subs, isPublic) : new AdvertisementPacket(adv, advType, isPublic); - send(AdvertisementPacket.subject, pkt); - } - - final void registerForwarder(PacketForwarder forwarder, String subject) { - router.setPacketForwarder(subject, forwarder); - } - - final void stop() { - overlay.stop(); - } - - private final void send(String subject, Serializable packet) { - assert overlay.getNumberOfBrokers() == 1; - assert overlay.getNumberOfClients() == 0; - for (NodeDescriptor node : overlay.getNeighbors()) { - try { - overlay.send(subject, packet, node); - } catch (IOException | NotRunningException e) { - e.printStackTrace(); - } - } - } + private final Overlay overlay; + private final GenericRouter router; + + ConnectionManager() { + Transport tr = null; + try { + tr = new TCPTransport(); + } catch (IOException e) { + e.printStackTrace(); + } + TopologyManager tm = new SimpleTopologyManager(); + overlay = new GenericOverlay(tm, tr, false); + router = new GenericRouter(overlay); + overlay.start(); + try { + overlay.addNeighbor(Consts.serverAddr); + } catch (ConnectException | MalformedURLException | NotRunningException e) { + e.printStackTrace(); + } + } + + final void sendEvent(UUID id, Event event, Set computedFrom, Set finalExpressions, + boolean approvedByTokenService) { + EventPacket pkt = new EventPacket(event, id, computedFrom, approvedByTokenService); + for (String finalExpression : finalExpressions) { + pkt.addFinalExpression(finalExpression); + } + send(EventPacket.subject, pkt); + } + + final void sendSubscription(Subscription sub) { + SubscriptionPacket pkt = new SubscriptionPacket(sub, SubType.SUB); + send(SubscriptionPacket.subject, pkt); + } + + final void sendUnsubscription(Subscription sub) { + SubscriptionPacket pkt = new SubscriptionPacket(sub, SubType.UNSUB); + send(SubscriptionPacket.subject, pkt); + } + + final void sendAdvertisement(Advertisement adv, boolean isPublic) { + sendAdvertisement(adv, AdvType.ADV, null, isPublic); + } + + final void sendAdvertisement(Advertisement adv, Set subs, boolean isPublic) { + sendAdvertisement(adv, AdvType.ADV, subs, isPublic); + } + + final void sendUnadvertisement(Advertisement adv, boolean isPublic) { + sendAdvertisement(adv, AdvType.UNADV, null, isPublic); + } + + final void sendUnadvertisement(Advertisement adv, Set subs, boolean isPublic) { + sendAdvertisement(adv, AdvType.UNADV, subs, isPublic); + } + + private final void sendAdvertisement(Advertisement adv, AdvType advType, Set subs, boolean isPublic) { + AdvertisementPacket pkt = (subs != null) ? new AdvertisementPacket(adv, advType, subs, isPublic) + : new AdvertisementPacket(adv, advType, isPublic); + send(AdvertisementPacket.subject, pkt); + } + + final void registerForwarder(PacketForwarder forwarder, String subject) { + router.setPacketForwarder(subject, forwarder); + } + + final void stop() { + overlay.stop(); + } + + private final void send(String subject, Serializable packet) { + assert overlay.getNumberOfBrokers() == 1; + assert overlay.getNumberOfClients() == 0; + for (NodeDescriptor node : overlay.getNeighbors()) { + try { + overlay.send(subject, packet, node); + } catch (IOException | NotRunningException e) { + e.printStackTrace(); + } + } + } } diff --git a/Dream2/src/main/java/javareact/client/QueueManager.java b/Dream2/src/main/java/javareact/client/QueueManager.java index 5a30a85..91aec72 100755 --- a/Dream2/src/main/java/javareact/client/QueueManager.java +++ b/Dream2/src/main/java/javareact/client/QueueManager.java @@ -15,83 +15,87 @@ import javareact.server.WaitRecommendations; /** - * This class is responsible for temporarily accumulating events before delivery to make sure that no glitch can occur. + * This class is responsible for temporarily accumulating events before delivery + * to make sure that no glitch can occur. */ public class QueueManager { - // WaitingElements, partitioned by id - private final Map waitingElements = new HashMap(); - // Candidate results to deliver (they can be effectively delivered only there are no waiting elements). - private final Set pendingResults = new HashSet(); + // WaitingElements, partitioned by id + private final Map waitingElements = new HashMap(); + // Candidate results to deliver (they can be effectively delivered only + // there are no waiting elements). + private final Set pendingResults = new HashSet(); - public final List processEventPacket(EventProxyPair eventProxyPair, String expression) { - EventPacket evPkt = eventProxyPair.getEventPacket(); - Proxy proxy = eventProxyPair.getProxy(); + public final List processEventPacket(EventProxyPair eventProxyPair, String expression) { + EventPacket evPkt = eventProxyPair.getEventPacket(); + Proxy proxy = eventProxyPair.getProxy(); - UUID id = evPkt.getId(); - Set waitingRecommendations = evPkt.hasRecommendationsFor(expression) ? evPkt.getRecommendationsFor(expression) : new HashSet(); + UUID id = evPkt.getId(); + Set waitingRecommendations = evPkt.hasRecommendationsFor(expression) ? evPkt + .getRecommendationsFor(expression) : new HashSet(); - if (waitingElements.containsKey(id)) { - WaitingElement elem = waitingElements.get(id); - elem.processEvent(evPkt, proxy); - if (elem.hasFinishedWaiting()) { - Map eventProxyMap = elem.getReceivedEvents(); - for (EventPacket eventToDeliver : eventProxyMap.keySet()) { - Proxy proxyToDeliver = eventProxyMap.get(eventToDeliver); - pendingResults.add(new EventProxyPair(eventToDeliver, proxyToDeliver)); - } - waitingElements.remove(id); - } - } else { - Set expressionsToWaitFrom = getExpressionsToWaitFrom(waitingRecommendations); - if (!expressionsToWaitFrom.isEmpty()) { - WaitingElement elem = new WaitingElement(expressionsToWaitFrom, evPkt, proxy); - waitingElements.put(id, elem); - } else { - pendingResults.add(new EventProxyPair(evPkt, proxy)); - } - } + if (waitingElements.containsKey(id)) { + WaitingElement elem = waitingElements.get(id); + elem.processEvent(evPkt, proxy); + if (elem.hasFinishedWaiting()) { + Map eventProxyMap = elem.getReceivedEvents(); + for (EventPacket eventToDeliver : eventProxyMap.keySet()) { + Proxy proxyToDeliver = eventProxyMap.get(eventToDeliver); + pendingResults.add(new EventProxyPair(eventToDeliver, proxyToDeliver)); + } + waitingElements.remove(id); + } + } else { + Set expressionsToWaitFrom = getExpressionsToWaitFrom(waitingRecommendations); + if (!expressionsToWaitFrom.isEmpty()) { + WaitingElement elem = new WaitingElement(expressionsToWaitFrom, evPkt, proxy); + waitingElements.put(id, elem); + } else { + pendingResults.add(new EventProxyPair(evPkt, proxy)); + } + } - List result = new ArrayList(); - if (waitingElements.isEmpty()) { - result.addAll(pendingResults); - pendingResults.clear(); - } - return result; - } + List result = new ArrayList(); + if (waitingElements.isEmpty()) { + result.addAll(pendingResults); + pendingResults.clear(); + } + return result; + } - private final Set getExpressionsToWaitFrom(Set recommendations) { - Set result = new HashSet(); - for (WaitRecommendations wr : recommendations) { - result.addAll(wr.getRecommendations()); - } - return result; - } + private final Set getExpressionsToWaitFrom(Set recommendations) { + Set result = new HashSet(); + for (WaitRecommendations wr : recommendations) { + result.addAll(wr.getRecommendations()); + } + return result; + } - private class WaitingElement { - // Set of expressions we are waiting for before delivering the events with the given id - private final Set waitingFor = new HashSet(); - // Set of events received with the given id - private final Map receivedEvents = new HashMap(); + private class WaitingElement { + // Set of expressions we are waiting for before delivering the events + // with the given id + private final Set waitingFor = new HashSet(); + // Set of events received with the given id + private final Map receivedEvents = new HashMap(); - WaitingElement(Set waitingFor, EventPacket evPkt, Proxy proxy) { - this.waitingFor.addAll(waitingFor); - receivedEvents.put(evPkt, proxy); - } + WaitingElement(Set waitingFor, EventPacket evPkt, Proxy proxy) { + this.waitingFor.addAll(waitingFor); + receivedEvents.put(evPkt, proxy); + } - final void processEvent(EventPacket evPkt, Proxy proxy) { - Event ev = evPkt.getEvent(); - String signature = ev.getSignature(); - assert (waitingFor.contains(signature)); - waitingFor.remove(signature); - receivedEvents.put(evPkt, proxy); - } + final void processEvent(EventPacket evPkt, Proxy proxy) { + Event ev = evPkt.getEvent(); + String signature = ev.getSignature(); + assert (waitingFor.contains(signature)); + waitingFor.remove(signature); + receivedEvents.put(evPkt, proxy); + } - final boolean hasFinishedWaiting() { - return waitingFor.isEmpty(); - } + final boolean hasFinishedWaiting() { + return waitingFor.isEmpty(); + } - final Map getReceivedEvents() { - return receivedEvents; - } - } + final Map getReceivedEvents() { + return receivedEvents; + } + } } diff --git a/Dream2/src/main/java/javareact/client/Subscriber.java b/Dream2/src/main/java/javareact/client/Subscriber.java index 1d0f10b..d852fcd 100755 --- a/Dream2/src/main/java/javareact/client/Subscriber.java +++ b/Dream2/src/main/java/javareact/client/Subscriber.java @@ -3,14 +3,16 @@ import javareact.common.packets.EventPacket; /** - * A subscriber receives events that express some change in the value of an observable variable. + * A subscriber receives events that express some change in the value of an + * observable variable. */ public interface Subscriber { - /** - * Notifies the subscriber that the given event has occurred. - * - * @param event the occurred event. - */ - public void notifyValueChanged(EventPacket event); + /** + * Notifies the subscriber that the given event has occurred. + * + * @param event + * the occurred event. + */ + public void notifyValueChanged(EventPacket event); } diff --git a/Dream2/src/main/java/javareact/common/ConsistencyType.java b/Dream2/src/main/java/javareact/common/ConsistencyType.java index 8150cd4..5a43f73 100755 --- a/Dream2/src/main/java/javareact/common/ConsistencyType.java +++ b/Dream2/src/main/java/javareact/common/ConsistencyType.java @@ -1,22 +1,22 @@ package javareact.common; public enum ConsistencyType { - CAUSAL { - @Override - public final String toString() { - return "Causal"; - } - }, - GLITCH_FREE { - @Override - public final String toString() { - return "GlitchFree"; - } - }, - ATOMIC { - @Override - public final String toString() { - return "Atomic"; - } - } + CAUSAL { + @Override + public final String toString() { + return "Causal"; + } + }, + GLITCH_FREE { + @Override + public final String toString() { + return "GlitchFree"; + } + }, + ATOMIC { + @Override + public final String toString() { + return "Atomic"; + } + } } diff --git a/Dream2/src/main/java/javareact/common/Consts.java b/Dream2/src/main/java/javareact/common/Consts.java index f9eb230..e237b73 100755 --- a/Dream2/src/main/java/javareact/common/Consts.java +++ b/Dream2/src/main/java/javareact/common/Consts.java @@ -9,56 +9,56 @@ import java.util.logging.Logger; public final class Consts { - private static final Properties properties = new Properties(); - private static final String LOGGING_PROPERTIES_FILE_NAME = "logging.properties"; + private static final Properties properties = new Properties(); + private static final String LOGGING_PROPERTIES_FILE_NAME = "logging.properties"; - public static final int serverPort; - public static final String serverAddr; + public static final int serverPort; + public static final String serverAddr; - public static ConsistencyType consistencyType; - public static String hostName; + public static ConsistencyType consistencyType; + public static String hostName; - static { - /** - * Load properties - */ - try { - FileInputStream input = new FileInputStream("jr.properties"); - properties.load(input); - input.close(); - } catch (IOException e) { - e.printStackTrace(); - } + static { + /** + * Load properties + */ + try { + FileInputStream input = new FileInputStream("jr.properties"); + properties.load(input); + input.close(); + } catch (IOException e) { + e.printStackTrace(); + } - String serverPortProperty = properties.getProperty("serverPort", "9000"); - serverPort = Integer.parseInt(serverPortProperty); + String serverPortProperty = properties.getProperty("serverPort", "9000"); + serverPort = Integer.parseInt(serverPortProperty); - String serverAddrProperty = properties.getProperty("serverAddr", "localhost"); - serverAddr = "reds-tcp:" + serverAddrProperty + ":" + serverPort; + String serverAddrProperty = properties.getProperty("serverAddr", "localhost"); + serverAddr = "reds-tcp:" + serverAddrProperty + ":" + serverPort; - String hostNameProperty = properties.getProperty("hostName", "local"); - hostName = hostNameProperty; + String hostNameProperty = properties.getProperty("hostName", "local"); + hostName = hostNameProperty; - String consistencyTypeProperty = properties.getProperty("consistencyType", "glitch_free").toLowerCase(); - if (consistencyTypeProperty.equals("causal")) { - consistencyType = ConsistencyType.CAUSAL; - } else if (consistencyTypeProperty.equals("glitch_free")) { - consistencyType = ConsistencyType.GLITCH_FREE; - } else { - consistencyType = ConsistencyType.ATOMIC; - } + String consistencyTypeProperty = properties.getProperty("consistencyType", "glitch_free").toLowerCase(); + if (consistencyTypeProperty.equals("causal")) { + consistencyType = ConsistencyType.CAUSAL; + } else if (consistencyTypeProperty.equals("glitch_free")) { + consistencyType = ConsistencyType.GLITCH_FREE; + } else { + consistencyType = ConsistencyType.ATOMIC; + } - /** - * Read logging properties - */ - LogManager manager = LogManager.getLogManager(); - try { - manager.readConfiguration(new FileInputStream(new File(LOGGING_PROPERTIES_FILE_NAME))); - } catch (SecurityException | IOException e) { - e.printStackTrace(); - } - Logger logger = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME); - ConsoleHandler consoleHandler = new ConsoleHandler(); - logger.addHandler(consoleHandler); - } + /** + * Read logging properties + */ + LogManager manager = LogManager.getLogManager(); + try { + manager.readConfiguration(new FileInputStream(new File(LOGGING_PROPERTIES_FILE_NAME))); + } catch (SecurityException | IOException e) { + e.printStackTrace(); + } + Logger logger = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME); + ConsoleHandler consoleHandler = new ConsoleHandler(); + logger.addHandler(consoleHandler); + } } diff --git a/Dream2/src/main/java/javareact/common/packets/AdvertisementPacket.java b/Dream2/src/main/java/javareact/common/packets/AdvertisementPacket.java index 1105356..04f287c 100755 --- a/Dream2/src/main/java/javareact/common/packets/AdvertisementPacket.java +++ b/Dream2/src/main/java/javareact/common/packets/AdvertisementPacket.java @@ -10,98 +10,100 @@ /** * Packets used to advertise the presence of a given observer. * - * If the packet is advertising a reactive observable object, it also includes the set of subscriptions required to - * define all the observable methods the reactive object depends from. + * If the packet is advertising a reactive observable object, it also includes + * the set of subscriptions required to define all the observable methods the + * reactive object depends from. */ public class AdvertisementPacket implements Serializable { - private static final long serialVersionUID = 5219175796450319466L; - public static final String subject = "__JAVA_REACT_ADVERTISEMENT_PACKET_SUBJECT"; + private static final long serialVersionUID = 5219175796450319466L; + public static final String subject = "__JAVA_REACT_ADVERTISEMENT_PACKET_SUBJECT"; - private final Advertisement advertisement; - private final AdvType advType; - private final Set subscriptions; - private final boolean isPublic; + private final Advertisement advertisement; + private final AdvType advType; + private final Set subscriptions; + private final boolean isPublic; - public AdvertisementPacket(Advertisement advertisement, AdvType advType, Set subscriptions, boolean isPublic) { - this.advertisement = advertisement; - this.advType = advType; - this.subscriptions = subscriptions; - this.isPublic = isPublic; - } + public AdvertisementPacket(Advertisement advertisement, AdvType advType, Set subscriptions, + boolean isPublic) { + this.advertisement = advertisement; + this.advType = advType; + this.subscriptions = subscriptions; + this.isPublic = isPublic; + } - public AdvertisementPacket(Advertisement advertisement, AdvType advType, boolean isPublic) { - this(advertisement, advType, null, isPublic); - } + public AdvertisementPacket(Advertisement advertisement, AdvType advType, boolean isPublic) { + this(advertisement, advType, null, isPublic); + } - public Advertisement getAdvertisement() { - return advertisement; - } + public Advertisement getAdvertisement() { + return advertisement; + } - public AdvType getAdvType() { - return advType; - } + public AdvType getAdvType() { + return advType; + } - public final boolean containtsSubscriptions() { - return subscriptions != null; - } + public final boolean containtsSubscriptions() { + return subscriptions != null; + } - public final Set getSubscriptions() { - return subscriptions; - } + public final Set getSubscriptions() { + return subscriptions; + } - public final boolean isPublic() { - return isPublic; - } + public final boolean isPublic() { + return isPublic; + } - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((advType == null) ? 0 : advType.hashCode()); - result = prime * result + ((advertisement == null) ? 0 : advertisement.hashCode()); - result = prime * result + ((subscriptions == null) ? 0 : subscriptions.hashCode()); - return result; - } + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((advType == null) ? 0 : advType.hashCode()); + result = prime * result + ((advertisement == null) ? 0 : advertisement.hashCode()); + result = prime * result + ((subscriptions == null) ? 0 : subscriptions.hashCode()); + return result; + } - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (!(obj instanceof AdvertisementPacket)) { - return false; - } - AdvertisementPacket other = (AdvertisementPacket) obj; - if (advType != other.advType) { - return false; - } - if (advertisement == null) { - if (other.advertisement != null) { - return false; - } - } else if (!advertisement.equals(other.advertisement)) { - return false; - } - if (subscriptions == null) { - if (other.subscriptions != null) { - return false; - } - } else if (!subscriptions.equals(other.subscriptions)) { - return false; - } - return true; - } + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof AdvertisementPacket)) { + return false; + } + AdvertisementPacket other = (AdvertisementPacket) obj; + if (advType != other.advType) { + return false; + } + if (advertisement == null) { + if (other.advertisement != null) { + return false; + } + } else if (!advertisement.equals(other.advertisement)) { + return false; + } + if (subscriptions == null) { + if (other.subscriptions != null) { + return false; + } + } else if (!subscriptions.equals(other.subscriptions)) { + return false; + } + return true; + } - @Override - public String toString() { - if (subscriptions != null) { - return advType + ": " + advertisement.toString() + " - Depending from: " + subscriptions; - } else { - return advType + ": " + advertisement.toString(); - } - } + @Override + public String toString() { + if (subscriptions != null) { + return advType + ": " + advertisement.toString() + " - Depending from: " + subscriptions; + } else { + return advType + ": " + advertisement.toString(); + } + } } diff --git a/Dream2/src/main/java/javareact/common/packets/EventPacket.java b/Dream2/src/main/java/javareact/common/packets/EventPacket.java index bd79d48..a2c0580 100755 --- a/Dream2/src/main/java/javareact/common/packets/EventPacket.java +++ b/Dream2/src/main/java/javareact/common/packets/EventPacket.java @@ -14,95 +14,99 @@ * Packet used to deliver events, which notify about some state change. */ public class EventPacket implements Serializable { - private static final long serialVersionUID = 8208653909787190211L; - public static final String subject = "__JAVA_REACT_PUBLICATION_PACKET_SUBJECT"; - - private final Event event; - private final UUID id; - private final Set computedFrom = new HashSet(); - private boolean approvedByTokenService = false; - - // Wait recommendations are used in glitch free protocols to tell the client to wait before processing an event - private final Map> waitRecommendations = new HashMap>(); - - // Final expressions are used in the atomic protocol to determine when the token can be released - // Upon receiving a final expression, the server acknowledges the token manager - private final Set finalExpressions = new HashSet(); - - public EventPacket(Event event, UUID id, Set computedFrom, boolean approvedByTokenService) { - this.event = event; - this.id = id; - this.computedFrom.addAll(computedFrom); - this.approvedByTokenService = approvedByTokenService; - } - - public EventPacket(Event event, UUID id, boolean approvedByTokenService) { - this.event = event; - this.id = id; - this.approvedByTokenService = approvedByTokenService; - } - - public final Event getEvent() { - return event; - } - - public final UUID getId() { - return id; - } - - public final Set getComputedFrom() { - return computedFrom; - } - - public final void addWaitRecommendations(WaitRecommendations recommendations) { - String expression = recommendations.getExpression(); - Set innerSet = waitRecommendations.get(expression); - if (innerSet == null) { - innerSet = new HashSet(); - waitRecommendations.put(expression, innerSet); - } - innerSet.add(recommendations); - } - - public final boolean hasRecommendationsFor(String expression) { - return waitRecommendations.containsKey(expression); - } - - public final Set getRecommendationsFor(String expression) { - return waitRecommendations.get(expression); - } - - public final void addFinalExpression(String expression) { - finalExpressions.add(expression); - } - - public final Set getFinalExpressions() { - return finalExpressions; - } - - public final boolean isFinal() { - return finalExpressions.contains(event.getSignature()); - } - - public final EventPacket dup() { - EventPacket result = new EventPacket(event, id, approvedByTokenService); - result.computedFrom.addAll(computedFrom); - result.finalExpressions.addAll(finalExpressions); - result.waitRecommendations.putAll(waitRecommendations); - return result; - } - - public final void tokenServiceApproves() { - approvedByTokenService = true; - } - - public final boolean isApprovedByTokenService() { - return approvedByTokenService; - } - - @Override - public String toString() { - return "EventPacket [event=" + event + ", id=" + id + ", computedFrom=" + computedFrom + ", waitRecommendations=" + waitRecommendations + ", finalExpressions=" + finalExpressions + "]"; - } + private static final long serialVersionUID = 8208653909787190211L; + public static final String subject = "__JAVA_REACT_PUBLICATION_PACKET_SUBJECT"; + + private final Event event; + private final UUID id; + private final Set computedFrom = new HashSet(); + private boolean approvedByTokenService = false; + + // Wait recommendations are used in glitch free protocols to tell the client + // to wait before processing an event + private final Map> waitRecommendations = new HashMap>(); + + // Final expressions are used in the atomic protocol to determine when the + // token can be released + // Upon receiving a final expression, the server acknowledges the token + // manager + private final Set finalExpressions = new HashSet(); + + public EventPacket(Event event, UUID id, Set computedFrom, boolean approvedByTokenService) { + this.event = event; + this.id = id; + this.computedFrom.addAll(computedFrom); + this.approvedByTokenService = approvedByTokenService; + } + + public EventPacket(Event event, UUID id, boolean approvedByTokenService) { + this.event = event; + this.id = id; + this.approvedByTokenService = approvedByTokenService; + } + + public final Event getEvent() { + return event; + } + + public final UUID getId() { + return id; + } + + public final Set getComputedFrom() { + return computedFrom; + } + + public final void addWaitRecommendations(WaitRecommendations recommendations) { + String expression = recommendations.getExpression(); + Set innerSet = waitRecommendations.get(expression); + if (innerSet == null) { + innerSet = new HashSet(); + waitRecommendations.put(expression, innerSet); + } + innerSet.add(recommendations); + } + + public final boolean hasRecommendationsFor(String expression) { + return waitRecommendations.containsKey(expression); + } + + public final Set getRecommendationsFor(String expression) { + return waitRecommendations.get(expression); + } + + public final void addFinalExpression(String expression) { + finalExpressions.add(expression); + } + + public final Set getFinalExpressions() { + return finalExpressions; + } + + public final boolean isFinal() { + return finalExpressions.contains(event.getSignature()); + } + + public final EventPacket dup() { + EventPacket result = new EventPacket(event, id, approvedByTokenService); + result.computedFrom.addAll(computedFrom); + result.finalExpressions.addAll(finalExpressions); + result.waitRecommendations.putAll(waitRecommendations); + return result; + } + + public final void tokenServiceApproves() { + approvedByTokenService = true; + } + + public final boolean isApprovedByTokenService() { + return approvedByTokenService; + } + + @Override + public String toString() { + return "EventPacket [event=" + event + ", id=" + id + ", computedFrom=" + computedFrom + + ", waitRecommendations=" + waitRecommendations + ", finalExpressions=" + finalExpressions + "]"; + } } diff --git a/Dream2/src/main/java/javareact/common/packets/SubscriptionPacket.java b/Dream2/src/main/java/javareact/common/packets/SubscriptionPacket.java index 2699615..0fb0996 100755 --- a/Dream2/src/main/java/javareact/common/packets/SubscriptionPacket.java +++ b/Dream2/src/main/java/javareact/common/packets/SubscriptionPacket.java @@ -6,65 +6,66 @@ import javareact.common.packets.content.Subscription; /** - * Packets used to deliver a subscription, which expresses an interest in some specific events. + * Packets used to deliver a subscription, which expresses an interest in some + * specific events. */ public class SubscriptionPacket implements Serializable { - private static final long serialVersionUID = -9026500933220636540L; - public static final String subject = "__JAVA_REACT_SUBSCRIPTION_PACKET_SUBJECT"; + private static final long serialVersionUID = -9026500933220636540L; + public static final String subject = "__JAVA_REACT_SUBSCRIPTION_PACKET_SUBJECT"; - private final Subscription subscription; - private final SubType subType; + private final Subscription subscription; + private final SubType subType; - public SubscriptionPacket(Subscription subscription, SubType subType) { - this.subscription = subscription; - this.subType = subType; - } + public SubscriptionPacket(Subscription subscription, SubType subType) { + this.subscription = subscription; + this.subType = subType; + } - public final SubType getSubType() { - return subType; - } + public final SubType getSubType() { + return subType; + } - public final Subscription getSubscription() { - return subscription; - } + public final Subscription getSubscription() { + return subscription; + } - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((subType == null) ? 0 : subType.hashCode()); - result = prime * result + ((subscription == null) ? 0 : subscription.hashCode()); - return result; - } + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((subType == null) ? 0 : subType.hashCode()); + result = prime * result + ((subscription == null) ? 0 : subscription.hashCode()); + return result; + } - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (!(obj instanceof SubscriptionPacket)) { - return false; - } - SubscriptionPacket other = (SubscriptionPacket) obj; - if (subType != other.subType) { - return false; - } - if (subscription == null) { - if (other.subscription != null) { - return false; - } - } else if (!subscription.equals(other.subscription)) { - return false; - } - return true; - } + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof SubscriptionPacket)) { + return false; + } + SubscriptionPacket other = (SubscriptionPacket) obj; + if (subType != other.subType) { + return false; + } + if (subscription == null) { + if (other.subscription != null) { + return false; + } + } else if (!subscription.equals(other.subscription)) { + return false; + } + return true; + } - @Override - public String toString() { - return subType + " " + subscription.toString(); - } + @Override + public String toString() { + return subType + " " + subscription.toString(); + } } diff --git a/Dream2/src/main/java/javareact/common/packets/content/AdvType.java b/Dream2/src/main/java/javareact/common/packets/content/AdvType.java index c6fab0b..a6e9abf 100755 --- a/Dream2/src/main/java/javareact/common/packets/content/AdvType.java +++ b/Dream2/src/main/java/javareact/common/packets/content/AdvType.java @@ -1,5 +1,5 @@ package javareact.common.packets.content; public enum AdvType { - ADV, UNADV + ADV, UNADV } diff --git a/Dream2/src/main/java/javareact/common/packets/content/Advertisement.java b/Dream2/src/main/java/javareact/common/packets/content/Advertisement.java index e686f7b..b3ee3b7 100755 --- a/Dream2/src/main/java/javareact/common/packets/content/Advertisement.java +++ b/Dream2/src/main/java/javareact/common/packets/content/Advertisement.java @@ -3,75 +3,77 @@ import java.io.Serializable; public class Advertisement implements Serializable { - private static final long serialVersionUID = -6636280874981657399L; + private static final long serialVersionUID = -6636280874981657399L; - private final String hostId; - private final String observableId; + private final String hostId; + private final String observableId; - public Advertisement(String hostId, String observableId) { - this.hostId = hostId; - this.observableId = observableId; - } + public Advertisement(String hostId, String observableId) { + this.hostId = hostId; + this.observableId = observableId; + } - public boolean isSatisfiedBy(Subscription sub) { - if (!sub.isBroadcast() && !hostId.equals(sub.getHostId())) return false; - if (!observableId.equals(sub.getObservableId())) return false; - return true; - } + public boolean isSatisfiedBy(Subscription sub) { + if (!sub.isBroadcast() && !hostId.equals(sub.getHostId())) + return false; + if (!observableId.equals(sub.getObservableId())) + return false; + return true; + } - public final String getObservableId() { - return observableId; - } + public final String getObservableId() { + return observableId; + } - public final String getHostId() { - return hostId; - } + public final String getHostId() { + return hostId; + } - public final String getSignature() { - return hostId + "." + observableId; - } + public final String getSignature() { + return hostId + "." + observableId; + } - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((hostId == null) ? 0 : hostId.hashCode()); - result = prime * result + ((observableId == null) ? 0 : observableId.hashCode()); - return result; - } + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((hostId == null) ? 0 : hostId.hashCode()); + result = prime * result + ((observableId == null) ? 0 : observableId.hashCode()); + return result; + } - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (!(obj instanceof Advertisement)) { - return false; - } - Advertisement other = (Advertisement) obj; - if (hostId == null) { - if (other.hostId != null) { - return false; - } - } else if (!hostId.equals(other.hostId)) { - return false; - } - if (observableId == null) { - if (other.observableId != null) { - return false; - } - } else if (!observableId.equals(other.observableId)) { - return false; - } - return true; - } + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof Advertisement)) { + return false; + } + Advertisement other = (Advertisement) obj; + if (hostId == null) { + if (other.hostId != null) { + return false; + } + } else if (!hostId.equals(other.hostId)) { + return false; + } + if (observableId == null) { + if (other.observableId != null) { + return false; + } + } else if (!observableId.equals(other.observableId)) { + return false; + } + return true; + } - @Override - public String toString() { - return "Advertisement [" + hostId + "." + observableId + "]"; - } + @Override + public String toString() { + return "Advertisement [" + hostId + "." + observableId + "]"; + } } diff --git a/Dream2/src/main/java/javareact/common/packets/content/Attribute.java b/Dream2/src/main/java/javareact/common/packets/content/Attribute.java index 8ede6b9..ddcffa7 100755 --- a/Dream2/src/main/java/javareact/common/packets/content/Attribute.java +++ b/Dream2/src/main/java/javareact/common/packets/content/Attribute.java @@ -3,69 +3,69 @@ import java.io.Serializable; public class Attribute implements Serializable { - private static final long serialVersionUID = -1970460317346993023L; + private static final long serialVersionUID = -1970460317346993023L; - private final String name; - private final T value; + private final String name; + private final T value; - public Attribute(String name, T value) { - this.name = name; - this.value = value; - } + public Attribute(String name, T value) { + this.name = name; + this.value = value; + } - public final String getName() { - return name; - } + public final String getName() { + return name; + } - public final T getValue() { - return value; - } + public final T getValue() { + return value; + } - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((name == null) ? 0 : name.hashCode()); - result = prime * result + ((value == null) ? 0 : value.hashCode()); - return result; - } + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((name == null) ? 0 : name.hashCode()); + result = prime * result + ((value == null) ? 0 : value.hashCode()); + return result; + } - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (!(obj instanceof Attribute)) { - return false; - } - Attribute other = (Attribute) obj; - if (name == null) { - if (other.name != null) { - return false; - } - } else if (!name.equals(other.name)) { - return false; - } - if (value == null) { - if (other.value != null) { - return false; - } - } else if (!value.equals(other.value)) { - return false; - } - return true; - } + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof Attribute)) { + return false; + } + Attribute other = (Attribute) obj; + if (name == null) { + if (other.name != null) { + return false; + } + } else if (!name.equals(other.name)) { + return false; + } + if (value == null) { + if (other.value != null) { + return false; + } + } else if (!value.equals(other.value)) { + return false; + } + return true; + } - @Override - public String toString() { - return "<" + name + "=" + value + ">"; - } - - public static Attribute of(String name, T value) { - return new Attribute(name, value); - } + @Override + public String toString() { + return "<" + name + "=" + value + ">"; + } + + public static Attribute of(String name, T value) { + return new Attribute(name, value); + } } diff --git a/Dream2/src/main/java/javareact/common/packets/content/Constraint.java b/Dream2/src/main/java/javareact/common/packets/content/Constraint.java index f159f4e..da69837 100755 --- a/Dream2/src/main/java/javareact/common/packets/content/Constraint.java +++ b/Dream2/src/main/java/javareact/common/packets/content/Constraint.java @@ -3,151 +3,152 @@ import java.io.Serializable; public class Constraint implements Serializable { - private static final long serialVersionUID = 2361519551421250914L; - - private final String name; - private final ConstraintOp op; - private final T val; - - public Constraint(String name, ConstraintOp op, T val) { - this.name = name; - this.op = op; - this.val = val; - } - - public Constraint(String name) { - this.name = name; - op = ConstraintOp.ANY; - val = null; - } - - public final boolean isSatisfiedBy(Event ev) { - if (!ev.hasAttribute(name)) return false; - Attribute attr = ev.getAttributeFor(name); - - if (val == null) { - // ANY - return true; - } - - if (attr.getValue() instanceof Integer && val instanceof Integer) { - Integer intConstrVal = (Integer)val; - Integer intEvVal = (Integer)attr.getValue(); - switch (op) { - case EQ: - return intEvVal.equals(intConstrVal); - case DF: - return !intEvVal.equals(intConstrVal); - case GT: - return intEvVal.compareTo(intConstrVal) > 0; - case LT: - return intEvVal.compareTo(intConstrVal) < 0; - default: - assert false : op; - return false; - } - } - - if (attr.getValue() instanceof Double && val instanceof Double) { - Double doubleConstrVal = (Double)val; - Double doubleEvVal = (Double)attr.getValue(); - switch (op) { - case EQ: - return doubleEvVal.equals(doubleConstrVal); - case DF: - return doubleEvVal.equals(doubleConstrVal); - case GT: - return doubleEvVal.compareTo(doubleConstrVal) > 0; - case LT: - return doubleEvVal.compareTo(doubleConstrVal) < 0; - default: - assert false : op; - return false; - } - } - - if (attr.getValue() instanceof String && val instanceof String) { - String stringConstrVal = (String)val; - String stringEvVal = (String)attr.getValue(); - switch (op) { - case EQ: - return stringEvVal.equals(stringConstrVal); - case DF: - return !stringEvVal.equals(stringConstrVal); - case IN: - return stringEvVal.contains(stringConstrVal); - case SW: - return stringEvVal.startsWith(stringConstrVal); - case EW: - return stringEvVal.endsWith(stringConstrVal); - default: - assert false : op; - return false; - } - } - - if (attr.getValue() instanceof Boolean && val instanceof Boolean) { - Boolean boolConstrVal = (Boolean)val; - Boolean boolEvVal = (Boolean)attr.getValue(); - switch (op) { - case EQ: - return boolEvVal.equals(boolConstrVal); - case DF: - return !boolEvVal.equals(boolConstrVal); - default: - assert false : op; - return false; - } - } - - return false; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((name == null) ? 0 : name.hashCode()); - result = prime * result + ((op == null) ? 0 : op.hashCode()); - result = prime * result + ((val == null) ? 0 : val.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (!(obj instanceof Constraint)) { - return false; - } - Constraint other = (Constraint) obj; - if (name == null) { - if (other.name != null) { - return false; - } - } else if (!name.equals(other.name)) { - return false; - } - if (op != other.op) { - return false; - } - if (val == null) { - if (other.val != null) { - return false; - } - } else if (!val.equals(other.val)) { - return false; - } - return true; - } - - @Override - public String toString() { - return "<" + name + op + val + ">"; - } + private static final long serialVersionUID = 2361519551421250914L; + + private final String name; + private final ConstraintOp op; + private final T val; + + public Constraint(String name, ConstraintOp op, T val) { + this.name = name; + this.op = op; + this.val = val; + } + + public Constraint(String name) { + this.name = name; + op = ConstraintOp.ANY; + val = null; + } + + public final boolean isSatisfiedBy(Event ev) { + if (!ev.hasAttribute(name)) + return false; + Attribute attr = ev.getAttributeFor(name); + + if (val == null) { + // ANY + return true; + } + + if (attr.getValue() instanceof Integer && val instanceof Integer) { + Integer intConstrVal = (Integer) val; + Integer intEvVal = (Integer) attr.getValue(); + switch (op) { + case EQ: + return intEvVal.equals(intConstrVal); + case DF: + return !intEvVal.equals(intConstrVal); + case GT: + return intEvVal.compareTo(intConstrVal) > 0; + case LT: + return intEvVal.compareTo(intConstrVal) < 0; + default: + assert false : op; + return false; + } + } + + if (attr.getValue() instanceof Double && val instanceof Double) { + Double doubleConstrVal = (Double) val; + Double doubleEvVal = (Double) attr.getValue(); + switch (op) { + case EQ: + return doubleEvVal.equals(doubleConstrVal); + case DF: + return doubleEvVal.equals(doubleConstrVal); + case GT: + return doubleEvVal.compareTo(doubleConstrVal) > 0; + case LT: + return doubleEvVal.compareTo(doubleConstrVal) < 0; + default: + assert false : op; + return false; + } + } + + if (attr.getValue() instanceof String && val instanceof String) { + String stringConstrVal = (String) val; + String stringEvVal = (String) attr.getValue(); + switch (op) { + case EQ: + return stringEvVal.equals(stringConstrVal); + case DF: + return !stringEvVal.equals(stringConstrVal); + case IN: + return stringEvVal.contains(stringConstrVal); + case SW: + return stringEvVal.startsWith(stringConstrVal); + case EW: + return stringEvVal.endsWith(stringConstrVal); + default: + assert false : op; + return false; + } + } + + if (attr.getValue() instanceof Boolean && val instanceof Boolean) { + Boolean boolConstrVal = (Boolean) val; + Boolean boolEvVal = (Boolean) attr.getValue(); + switch (op) { + case EQ: + return boolEvVal.equals(boolConstrVal); + case DF: + return !boolEvVal.equals(boolConstrVal); + default: + assert false : op; + return false; + } + } + + return false; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((name == null) ? 0 : name.hashCode()); + result = prime * result + ((op == null) ? 0 : op.hashCode()); + result = prime * result + ((val == null) ? 0 : val.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof Constraint)) { + return false; + } + Constraint other = (Constraint) obj; + if (name == null) { + if (other.name != null) { + return false; + } + } else if (!name.equals(other.name)) { + return false; + } + if (op != other.op) { + return false; + } + if (val == null) { + if (other.val != null) { + return false; + } + } else if (!val.equals(other.val)) { + return false; + } + return true; + } + + @Override + public String toString() { + return "<" + name + op + val + ">"; + } } diff --git a/Dream2/src/main/java/javareact/common/packets/content/ConstraintOp.java b/Dream2/src/main/java/javareact/common/packets/content/ConstraintOp.java index edb4d01..91373d9 100755 --- a/Dream2/src/main/java/javareact/common/packets/content/ConstraintOp.java +++ b/Dream2/src/main/java/javareact/common/packets/content/ConstraintOp.java @@ -1,52 +1,52 @@ package javareact.common.packets.content; public enum ConstraintOp { - ANY { - @Override - public final String toString() { - return "ANY"; - } - }, - EQ { - @Override - public final String toString() { - return "="; - } - }, - DF { - @Override - public final String toString() { - return "!="; - } - }, - GT { - @Override - public final String toString() { - return ">"; - } - }, - LT { - @Override - public final String toString() { - return "<"; - } - }, - IN { - @Override - public final String toString() { - return "contains"; - } - }, - SW { - @Override - public final String toString() { - return "starts with"; - } - }, - EW { - @Override - public final String toString() { - return "ends with"; - } - } + ANY { + @Override + public final String toString() { + return "ANY"; + } + }, + EQ { + @Override + public final String toString() { + return "="; + } + }, + DF { + @Override + public final String toString() { + return "!="; + } + }, + GT { + @Override + public final String toString() { + return ">"; + } + }, + LT { + @Override + public final String toString() { + return "<"; + } + }, + IN { + @Override + public final String toString() { + return "contains"; + } + }, + SW { + @Override + public final String toString() { + return "starts with"; + } + }, + EW { + @Override + public final String toString() { + return "ends with"; + } + } } diff --git a/Dream2/src/main/java/javareact/common/packets/content/Event.java b/Dream2/src/main/java/javareact/common/packets/content/Event.java index d9c8bcd..9771152 100755 --- a/Dream2/src/main/java/javareact/common/packets/content/Event.java +++ b/Dream2/src/main/java/javareact/common/packets/content/Event.java @@ -7,125 +7,130 @@ import java.util.Set; public class Event implements Iterable, Serializable { - private static final long serialVersionUID = 831217881290695190L; - - private final String observableId; - private final String hostId; - private final Map attributes = new HashMap(); - private final boolean persistent; - - public Event(String hostId, String observableId, boolean persistent, Attribute... attributes) { - this.hostId = hostId; - this.observableId = observableId; - this.persistent = persistent; - for (Attribute attr : attributes) { - this.attributes.put(attr.getName(), attr); - } - } - - public Event(String hostId, String observableId, Attribute... attributes) { - this(hostId, observableId, false, attributes); - } - - public boolean hasAttribute(String name) { - return attributes.containsKey(name); - } - - public Attribute getAttributeFor(String name) { - return attributes.get(name); - } - - @Override - public Iterator iterator() { - return attributes.values().iterator(); - } - - public final String getObservableId() { - return observableId; - } - - public final String getHostId() { - return hostId; - } - - public final String getSignature() { - return hostId + "." + observableId; - } - - public final boolean isPersistent() { - return persistent; - } - - /** - * Returns true iff the event carries the same information as ev, i.e., refers to the same observable and contains the - * same attributes. - * - * @param ev the event. - * @return true iff the event carries the same information as ev. - */ - public boolean containsTheSameInformationAs(Event ev) { - if (!ev.hostId.equals(hostId)) return false; - if (!ev.observableId.equals(observableId)) return false; - Set names = attributes.keySet(); - Set evNames = attributes.keySet(); - if (names.size() != evNames.size()) return false; - if (!names.containsAll(evNames)) return false; - return true; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((attributes == null) ? 0 : attributes.hashCode()); - result = prime * result + ((hostId == null) ? 0 : hostId.hashCode()); - result = prime * result + ((observableId == null) ? 0 : observableId.hashCode()); - result = prime * result + (persistent ? 1231 : 1237); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (!(obj instanceof Event)) { - return false; - } - Event other = (Event) obj; - if (attributes == null) { - if (other.attributes != null) { - return false; - } - } else if (!attributes.equals(other.attributes)) { - return false; - } - if (hostId == null) { - if (other.hostId != null) { - return false; - } - } else if (!hostId.equals(other.hostId)) { - return false; - } - if (observableId == null) { - if (other.observableId != null) { - return false; - } - } else if (!observableId.equals(other.observableId)) { - return false; - } - if (persistent != other.persistent) { - return false; - } - return true; - } - - @Override - public String toString() { - return hostId + "." + observableId + "(" + attributes.values() + ")"; - } + private static final long serialVersionUID = 831217881290695190L; + + private final String observableId; + private final String hostId; + private final Map attributes = new HashMap(); + private final boolean persistent; + + public Event(String hostId, String observableId, boolean persistent, Attribute... attributes) { + this.hostId = hostId; + this.observableId = observableId; + this.persistent = persistent; + for (Attribute attr : attributes) { + this.attributes.put(attr.getName(), attr); + } + } + + public Event(String hostId, String observableId, Attribute... attributes) { + this(hostId, observableId, false, attributes); + } + + public boolean hasAttribute(String name) { + return attributes.containsKey(name); + } + + public Attribute getAttributeFor(String name) { + return attributes.get(name); + } + + @Override + public Iterator iterator() { + return attributes.values().iterator(); + } + + public final String getObservableId() { + return observableId; + } + + public final String getHostId() { + return hostId; + } + + public final String getSignature() { + return hostId + "." + observableId; + } + + public final boolean isPersistent() { + return persistent; + } + + /** + * Returns true iff the event carries the same information as ev, i.e., + * refers to the same observable and contains the same attributes. + * + * @param ev + * the event. + * @return true iff the event carries the same information as ev. + */ + public boolean containsTheSameInformationAs(Event ev) { + if (!ev.hostId.equals(hostId)) + return false; + if (!ev.observableId.equals(observableId)) + return false; + Set names = attributes.keySet(); + Set evNames = attributes.keySet(); + if (names.size() != evNames.size()) + return false; + if (!names.containsAll(evNames)) + return false; + return true; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((attributes == null) ? 0 : attributes.hashCode()); + result = prime * result + ((hostId == null) ? 0 : hostId.hashCode()); + result = prime * result + ((observableId == null) ? 0 : observableId.hashCode()); + result = prime * result + (persistent ? 1231 : 1237); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof Event)) { + return false; + } + Event other = (Event) obj; + if (attributes == null) { + if (other.attributes != null) { + return false; + } + } else if (!attributes.equals(other.attributes)) { + return false; + } + if (hostId == null) { + if (other.hostId != null) { + return false; + } + } else if (!hostId.equals(other.hostId)) { + return false; + } + if (observableId == null) { + if (other.observableId != null) { + return false; + } + } else if (!observableId.equals(other.observableId)) { + return false; + } + if (persistent != other.persistent) { + return false; + } + return true; + } + + @Override + public String toString() { + return hostId + "." + observableId + "(" + attributes.values() + ")"; + } } diff --git a/Dream2/src/main/java/javareact/common/packets/content/SubType.java b/Dream2/src/main/java/javareact/common/packets/content/SubType.java index d3a1a52..9f89cf8 100755 --- a/Dream2/src/main/java/javareact/common/packets/content/SubType.java +++ b/Dream2/src/main/java/javareact/common/packets/content/SubType.java @@ -1,5 +1,5 @@ package javareact.common.packets.content; public enum SubType { - SUB, UNSUB + SUB, UNSUB } diff --git a/Dream2/src/main/java/javareact/common/packets/content/Subscription.java b/Dream2/src/main/java/javareact/common/packets/content/Subscription.java index e91dfeb..38f5444 100755 --- a/Dream2/src/main/java/javareact/common/packets/content/Subscription.java +++ b/Dream2/src/main/java/javareact/common/packets/content/Subscription.java @@ -6,133 +6,139 @@ import java.util.UUID; public class Subscription implements Serializable { - private static final long serialVersionUID = -3452847781395458670L; - - public static final String wildcard = "*"; - - private final String observableId; - private final String hostId; - private final Collection constraints = new ArrayList(); - private final boolean blocking; - private final UUID proxyID; - - public Subscription(String hostId, String observableId, boolean blocking, UUID proxyID, Constraint... constraints) { - this.hostId = hostId; - this.observableId = observableId; - this.blocking = blocking; - this.proxyID = proxyID; - for (Constraint constraint : constraints) { - this.constraints.add(constraint); - } - } - - public Subscription(String hostId, String observableId, UUID proxyID, Constraint... constraints) { - this(hostId, observableId, false, proxyID, constraints); - } - - public final boolean isSatisfiedBy(Event ev) { - if (!isBroadcast() && !hostId.equals(ev.getHostId())) return false; - if (!observableId.equals(ev.getObservableId())) return false; - for (Constraint c : constraints) { - if (!c.isSatisfiedBy(ev)) return false; - } - return true; - } - - public final boolean matchesOnlySignatureOf(Event ev) { - if (!isBroadcast() && !hostId.equals(ev.getHostId())) return false; - if (!observableId.equals(ev.getObservableId())) return false; - for (Constraint c : constraints) { - if (!c.isSatisfiedBy(ev)) return true; - } - return false; - } - - public final String getObservableId() { - return observableId; - } - - public final String getHostId() { - return hostId; - } - - public final String getSignature() { - return hostId + "." + observableId; - } - - public final boolean isBroadcast() { - return hostId.equals(wildcard); - } - - public final boolean isBlocking() { - return blocking; - } - - public UUID getProxyID() { - return proxyID; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + (blocking ? 1231 : 1237); - result = prime * result + ((constraints == null) ? 0 : constraints.hashCode()); - result = prime * result + ((hostId == null) ? 0 : hostId.hashCode()); - result = prime * result + ((observableId == null) ? 0 : observableId.hashCode()); - result = prime * result + ((proxyID == null) ? 0 : proxyID.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (!(obj instanceof Subscription)) { - return false; - } - Subscription other = (Subscription) obj; - if (blocking != other.blocking) { - return false; - } - if (constraints == null) { - if (other.constraints != null) { - return false; - } - } else if (!constraints.equals(other.constraints)) { - return false; - } - if (hostId == null) { - if (other.hostId != null) { - return false; - } - } else if (!hostId.equals(other.hostId)) { - return false; - } - if (observableId == null) { - if (other.observableId != null) { - return false; - } - } else if (!observableId.equals(other.observableId)) { - return false; - } - if (proxyID == null) { - if (other.proxyID != null) { - return false; - } - } else if (!proxyID.equals(other.proxyID)) { - return false; - } - return true; - } - - @Override - public String toString() { - return hostId + "." + observableId + "(" + constraints + ")"; - } + private static final long serialVersionUID = -3452847781395458670L; + + public static final String wildcard = "*"; + + private final String observableId; + private final String hostId; + private final Collection constraints = new ArrayList(); + private final boolean blocking; + private final UUID proxyID; + + public Subscription(String hostId, String observableId, boolean blocking, UUID proxyID, Constraint... constraints) { + this.hostId = hostId; + this.observableId = observableId; + this.blocking = blocking; + this.proxyID = proxyID; + for (Constraint constraint : constraints) { + this.constraints.add(constraint); + } + } + + public Subscription(String hostId, String observableId, UUID proxyID, Constraint... constraints) { + this(hostId, observableId, false, proxyID, constraints); + } + + public final boolean isSatisfiedBy(Event ev) { + if (!isBroadcast() && !hostId.equals(ev.getHostId())) + return false; + if (!observableId.equals(ev.getObservableId())) + return false; + for (Constraint c : constraints) { + if (!c.isSatisfiedBy(ev)) + return false; + } + return true; + } + + public final boolean matchesOnlySignatureOf(Event ev) { + if (!isBroadcast() && !hostId.equals(ev.getHostId())) + return false; + if (!observableId.equals(ev.getObservableId())) + return false; + for (Constraint c : constraints) { + if (!c.isSatisfiedBy(ev)) + return true; + } + return false; + } + + public final String getObservableId() { + return observableId; + } + + public final String getHostId() { + return hostId; + } + + public final String getSignature() { + return hostId + "." + observableId; + } + + public final boolean isBroadcast() { + return hostId.equals(wildcard); + } + + public final boolean isBlocking() { + return blocking; + } + + public UUID getProxyID() { + return proxyID; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + (blocking ? 1231 : 1237); + result = prime * result + ((constraints == null) ? 0 : constraints.hashCode()); + result = prime * result + ((hostId == null) ? 0 : hostId.hashCode()); + result = prime * result + ((observableId == null) ? 0 : observableId.hashCode()); + result = prime * result + ((proxyID == null) ? 0 : proxyID.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof Subscription)) { + return false; + } + Subscription other = (Subscription) obj; + if (blocking != other.blocking) { + return false; + } + if (constraints == null) { + if (other.constraints != null) { + return false; + } + } else if (!constraints.equals(other.constraints)) { + return false; + } + if (hostId == null) { + if (other.hostId != null) { + return false; + } + } else if (!hostId.equals(other.hostId)) { + return false; + } + if (observableId == null) { + if (other.observableId != null) { + return false; + } + } else if (!observableId.equals(other.observableId)) { + return false; + } + if (proxyID == null) { + if (other.proxyID != null) { + return false; + } + } else if (!proxyID.equals(other.proxyID)) { + return false; + } + return true; + } + + @Override + public String toString() { + return hostId + "." + observableId + "(" + constraints + ")"; + } } diff --git a/Dream2/src/main/java/javareact/common/packets/content/Value.java b/Dream2/src/main/java/javareact/common/packets/content/Value.java index d11a6f8..a1523c1 100755 --- a/Dream2/src/main/java/javareact/common/packets/content/Value.java +++ b/Dream2/src/main/java/javareact/common/packets/content/Value.java @@ -4,157 +4,157 @@ import java.util.List; public class Value implements Serializable { - private static final long serialVersionUID = -4909992361317067576L; - - private final int intVal; - private final double doubleVal; - private final String stringVal; - private final boolean boolVal; - private final List listVal; - private final ValueType type; - - public Value(int val) { - intVal = val; - doubleVal = 0; - stringVal = null; - boolVal = false; - listVal = null; - type = ValueType.INT; - } - - public Value(double val) { - intVal = 0; - doubleVal = val; - stringVal = null; - boolVal = false; - listVal = null; - type = ValueType.DOUBLE; - } - - public Value(String val) { - intVal = 0; - doubleVal = 0; - stringVal = val; - boolVal = false; - listVal = null; - type = ValueType.STRING; - } - - public Value(boolean val) { - intVal = 0; - doubleVal = 0; - stringVal = null; - boolVal = val; - listVal = null; - type = ValueType.BOOL; - } - - public Value(List val) { - intVal = 0; - doubleVal = 0; - stringVal = null; - boolVal = false; - listVal = val; - type = ValueType.LIST; - } - - public final ValueType getType() { - return type; - } - - public final int intVal() { - return intVal; - } - - public final double doubleVal() { - return doubleVal; - } - - public final String stringVal() { - return stringVal; - } - - public final boolean boolVal() { - return boolVal; - } - - public final List listVal() { - return listVal; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + (boolVal ? 1231 : 1237); - long temp; - temp = Double.doubleToLongBits(doubleVal); - result = prime * result + (int) (temp ^ (temp >>> 32)); - result = prime * result + intVal; - result = prime * result + ((listVal == null) ? 0 : listVal.hashCode()); - result = prime * result + ((stringVal == null) ? 0 : stringVal.hashCode()); - result = prime * result + ((type == null) ? 0 : type.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (!(obj instanceof Value)) { - return false; - } - Value other = (Value) obj; - if (boolVal != other.boolVal) { - return false; - } - if (Double.doubleToLongBits(doubleVal) != Double.doubleToLongBits(other.doubleVal)) { - return false; - } - if (intVal != other.intVal) { - return false; - } - if (listVal == null) { - if (other.listVal != null) { - return false; - } - } else if (!listVal.equals(other.listVal)) { - return false; - } - if (stringVal == null) { - if (other.stringVal != null) { - return false; - } - } else if (!stringVal.equals(other.stringVal)) { - return false; - } - if (type != other.type) { - return false; - } - return true; - } - - @Override - public String toString() { - switch (type) { - case INT: - return String.valueOf(intVal); - case DOUBLE: - return String.valueOf(doubleVal); - case STRING: - return stringVal; - case BOOL: - return String.valueOf(boolVal); - case LIST: - return String.valueOf(listVal); - default: - assert false : type; - return "err"; - } - } + private static final long serialVersionUID = -4909992361317067576L; + + private final int intVal; + private final double doubleVal; + private final String stringVal; + private final boolean boolVal; + private final List listVal; + private final ValueType type; + + public Value(int val) { + intVal = val; + doubleVal = 0; + stringVal = null; + boolVal = false; + listVal = null; + type = ValueType.INT; + } + + public Value(double val) { + intVal = 0; + doubleVal = val; + stringVal = null; + boolVal = false; + listVal = null; + type = ValueType.DOUBLE; + } + + public Value(String val) { + intVal = 0; + doubleVal = 0; + stringVal = val; + boolVal = false; + listVal = null; + type = ValueType.STRING; + } + + public Value(boolean val) { + intVal = 0; + doubleVal = 0; + stringVal = null; + boolVal = val; + listVal = null; + type = ValueType.BOOL; + } + + public Value(List val) { + intVal = 0; + doubleVal = 0; + stringVal = null; + boolVal = false; + listVal = val; + type = ValueType.LIST; + } + + public final ValueType getType() { + return type; + } + + public final int intVal() { + return intVal; + } + + public final double doubleVal() { + return doubleVal; + } + + public final String stringVal() { + return stringVal; + } + + public final boolean boolVal() { + return boolVal; + } + + public final List listVal() { + return listVal; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + (boolVal ? 1231 : 1237); + long temp; + temp = Double.doubleToLongBits(doubleVal); + result = prime * result + (int) (temp ^ (temp >>> 32)); + result = prime * result + intVal; + result = prime * result + ((listVal == null) ? 0 : listVal.hashCode()); + result = prime * result + ((stringVal == null) ? 0 : stringVal.hashCode()); + result = prime * result + ((type == null) ? 0 : type.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof Value)) { + return false; + } + Value other = (Value) obj; + if (boolVal != other.boolVal) { + return false; + } + if (Double.doubleToLongBits(doubleVal) != Double.doubleToLongBits(other.doubleVal)) { + return false; + } + if (intVal != other.intVal) { + return false; + } + if (listVal == null) { + if (other.listVal != null) { + return false; + } + } else if (!listVal.equals(other.listVal)) { + return false; + } + if (stringVal == null) { + if (other.stringVal != null) { + return false; + } + } else if (!stringVal.equals(other.stringVal)) { + return false; + } + if (type != other.type) { + return false; + } + return true; + } + + @Override + public String toString() { + switch (type) { + case INT: + return String.valueOf(intVal); + case DOUBLE: + return String.valueOf(doubleVal); + case STRING: + return stringVal; + case BOOL: + return String.valueOf(boolVal); + case LIST: + return String.valueOf(listVal); + default: + assert false : type; + return "err"; + } + } } diff --git a/Dream2/src/main/java/javareact/common/packets/content/ValueType.java b/Dream2/src/main/java/javareact/common/packets/content/ValueType.java index a58cca0..5c7f0d3 100755 --- a/Dream2/src/main/java/javareact/common/packets/content/ValueType.java +++ b/Dream2/src/main/java/javareact/common/packets/content/ValueType.java @@ -1,5 +1,5 @@ package javareact.common.packets.content; public enum ValueType { - INT, DOUBLE, STRING, BOOL, LIST, GENERIC + INT, DOUBLE, STRING, BOOL, LIST, GENERIC } diff --git a/Dream2/src/main/java/javareact/common/packets/registry/RegistryAdvertisePacket.java b/Dream2/src/main/java/javareact/common/packets/registry/RegistryAdvertisePacket.java index 8c42e89..7164de0 100755 --- a/Dream2/src/main/java/javareact/common/packets/registry/RegistryAdvertisePacket.java +++ b/Dream2/src/main/java/javareact/common/packets/registry/RegistryAdvertisePacket.java @@ -8,48 +8,48 @@ * Packet used by a registry to advertise its existence. */ public class RegistryAdvertisePacket implements Serializable { - private static final long serialVersionUID = -2076191326020987758L; - public static final String subject = "__JAVA_REACT_REGISTRY_ADVERTISE_PACKET_SUBJECT"; - - private final AdvType type; - - public RegistryAdvertisePacket(AdvType type) { - this.type = type; - } - - public AdvType getType() { - return type; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((type == null) ? 0 : type.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (!(obj instanceof RegistryAdvertisePacket)) { - return false; - } - RegistryAdvertisePacket other = (RegistryAdvertisePacket) obj; - if (type != other.type) { - return false; - } - return true; - } - - @Override - public String toString() { - return "RegistryAdvertisePacket [type=" + type + "]"; - } + private static final long serialVersionUID = -2076191326020987758L; + public static final String subject = "__JAVA_REACT_REGISTRY_ADVERTISE_PACKET_SUBJECT"; + + private final AdvType type; + + public RegistryAdvertisePacket(AdvType type) { + this.type = type; + } + + public AdvType getType() { + return type; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((type == null) ? 0 : type.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof RegistryAdvertisePacket)) { + return false; + } + RegistryAdvertisePacket other = (RegistryAdvertisePacket) obj; + if (type != other.type) { + return false; + } + return true; + } + + @Override + public String toString() { + return "RegistryAdvertisePacket [type=" + type + "]"; + } } diff --git a/Dream2/src/main/java/javareact/common/packets/token_service/TokenAckPacket.java b/Dream2/src/main/java/javareact/common/packets/token_service/TokenAckPacket.java index 6720c45..24934bc 100755 --- a/Dream2/src/main/java/javareact/common/packets/token_service/TokenAckPacket.java +++ b/Dream2/src/main/java/javareact/common/packets/token_service/TokenAckPacket.java @@ -3,27 +3,28 @@ import java.io.Serializable; /** - * This packet is used to notify the token service about the forwarding of events. It contains a final expression a - * server has processed and the number of times it has been delivered to a client. + * This packet is used to notify the token service about the forwarding of + * events. It contains a final expression a server has processed and the number + * of times it has been delivered to a client. */ public class TokenAckPacket implements Serializable { - private static final long serialVersionUID = -755350171061415780L; - public static final String subject = "__JAVA_REACT_TOKEN_ACK_PACKET_SUBJECT"; + private static final long serialVersionUID = -755350171061415780L; + public static final String subject = "__JAVA_REACT_TOKEN_ACK_PACKET_SUBJECT"; - private final String finalExpression; - private final int count; + private final String finalExpression; + private final int count; - public TokenAckPacket(String finalExpression, int count) { - this.finalExpression = finalExpression; - this.count = count; - } + public TokenAckPacket(String finalExpression, int count) { + this.finalExpression = finalExpression; + this.count = count; + } - public final String getFinalExpression() { - return finalExpression; - } + public final String getFinalExpression() { + return finalExpression; + } - public final int getCount() { - return count; - } + public final int getCount() { + return count; + } } diff --git a/Dream2/src/main/java/javareact/common/packets/token_service/TokenServiceAdvertisePacket.java b/Dream2/src/main/java/javareact/common/packets/token_service/TokenServiceAdvertisePacket.java index ef4f00c..110acd1 100755 --- a/Dream2/src/main/java/javareact/common/packets/token_service/TokenServiceAdvertisePacket.java +++ b/Dream2/src/main/java/javareact/common/packets/token_service/TokenServiceAdvertisePacket.java @@ -9,49 +9,49 @@ * Packet used by a registry to advertise its existence. */ public class TokenServiceAdvertisePacket implements Serializable { - private static final long serialVersionUID = 527835287295833577L; - - public static final String subject = "__JAVA_REACT_TOKEN_SERVICE_ADVERTISE_PACKET_SUBJECT"; - - private final AdvType type; - - public TokenServiceAdvertisePacket(AdvType type) { - this.type = type; - } - - public AdvType getType() { - return type; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((type == null) ? 0 : type.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (!(obj instanceof RegistryAdvertisePacket)) { - return false; - } - TokenServiceAdvertisePacket other = (TokenServiceAdvertisePacket) obj; - if (type != other.type) { - return false; - } - return true; - } - - @Override - public String toString() { - return "TokenServiceAdvertisePacket [type=" + type + "]"; - } + private static final long serialVersionUID = 527835287295833577L; + + public static final String subject = "__JAVA_REACT_TOKEN_SERVICE_ADVERTISE_PACKET_SUBJECT"; + + private final AdvType type; + + public TokenServiceAdvertisePacket(AdvType type) { + this.type = type; + } + + public AdvType getType() { + return type; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((type == null) ? 0 : type.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof RegistryAdvertisePacket)) { + return false; + } + TokenServiceAdvertisePacket other = (TokenServiceAdvertisePacket) obj; + if (type != other.type) { + return false; + } + return true; + } + + @Override + public String toString() { + return "TokenServiceAdvertisePacket [type=" + type + "]"; + } } diff --git a/Dream2/src/main/java/javareact/common/types/AbstractReactive.java b/Dream2/src/main/java/javareact/common/types/AbstractReactive.java index 4a55bef..4282629 100755 --- a/Dream2/src/main/java/javareact/common/types/AbstractReactive.java +++ b/Dream2/src/main/java/javareact/common/types/AbstractReactive.java @@ -18,99 +18,100 @@ import javareact.common.packets.content.Subscription; abstract class AbstractReactive implements Reactive, ProxyChangeListener { - private final ClientEventForwarder clientEventForwarder; - private final QueueManager queueManager = new QueueManager(); - protected final String name; - private final Proxy[] dependentProxies; - - private final Logger logger = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME); - - protected T val; - - private RemoteVar proxy = null; - - public AbstractReactive(String name, Proxy... proxies) { - this.name = name; - dependentProxies = proxies; - clientEventForwarder = ClientEventForwarder.get(); - for (Proxy proxy : proxies) { - proxy.addProxyChangeListener(this); - } - sentAdvertisement(); - } - - @Override - public void update(EventProxyPair eventProxyPair) { - logger.finest("Update method invoked with " + eventProxyPair); - List pairs = queueManager.processEventPacket(eventProxyPair, Consts.hostName + "." + name); - logger.finest("The queueManager returned the following pairs " + pairs); - if (!pairs.isEmpty()) { - System.out.println(name + ": actual update"); - // Compute the new value - T oldVal = val; - val = evaluate(); - logger.finest("New value computed for the reactive object: " + val); - - // Notify depending reactive objects - EventProxyPair templatePair = pairs.get(0); - EventPacket templatePkt = templatePair.getEventPacket(); - UUID id = templatePkt.getId(); - Set computedFrom = getComputedFrom(pairs); - Set finalExpressions = templatePkt.getFinalExpressions(); - Event ev = null; - try { - // TODO consider methods other than get()!!! - ev = new Event(Consts.hostName, name, Attribute.of("get", val)); - } catch (Exception e) { - e.printStackTrace(); - } - logger.finest("Sending event to dependent reactive objects."); - clientEventForwarder.sendEvent(id, ev, computedFrom, finalExpressions, true); - - // Acknowledge the proxy - for (EventProxyPair pair : pairs) { - EventPacket evPkt = pair.getEventPacket(); - Proxy proxy = pair.getProxy(); - proxy.notifyEventProcessed(this, evPkt); - } - } else { - System.out.println(name + ": update call but waiting: " + eventProxyPair.toString()); - } - } - - public ChangeEvent change() { - return new ChangeEvent(this); - } - - private final void sentAdvertisement() { - Set subs = new HashSet(); - for (Proxy proxy : dependentProxies) { - Subscription sub = new Subscription(proxy.getHost(), proxy.getObject(), proxy.getProxyID(), new Constraint(proxy.getMethod())); - subs.add(sub); - } - Advertisement adv = new Advertisement(Consts.hostName, name); - clientEventForwarder.advertise(adv, subs, true); - } - - private final Set getComputedFrom(Collection pairs) { - Set results = new HashSet(); - for (EventProxyPair pair : pairs) { - results.addAll(pair.getEventPacket().getComputedFrom()); - } - results.add(Consts.hostName + "." + name); - return results; - } - - @Override - public synchronized RemoteVar getProxy() { - if (proxy == null) { - proxy = new RemoteVar(name); - } - return proxy; - } - - public T get() { - return val; - } + private final ClientEventForwarder clientEventForwarder; + private final QueueManager queueManager = new QueueManager(); + protected final String name; + private final Proxy[] dependentProxies; + + private final Logger logger = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME); + + protected T val; + + private RemoteVar proxy = null; + + public AbstractReactive(String name, Proxy... proxies) { + this.name = name; + dependentProxies = proxies; + clientEventForwarder = ClientEventForwarder.get(); + for (Proxy proxy : proxies) { + proxy.addProxyChangeListener(this); + } + sentAdvertisement(); + } + + @Override + public void update(EventProxyPair eventProxyPair) { + logger.finest("Update method invoked with " + eventProxyPair); + List pairs = queueManager.processEventPacket(eventProxyPair, Consts.hostName + "." + name); + logger.finest("The queueManager returned the following pairs " + pairs); + if (!pairs.isEmpty()) { + System.out.println(name + ": actual update"); + // Compute the new value + T oldVal = val; + val = evaluate(); + logger.finest("New value computed for the reactive object: " + val); + + // Notify depending reactive objects + EventProxyPair templatePair = pairs.get(0); + EventPacket templatePkt = templatePair.getEventPacket(); + UUID id = templatePkt.getId(); + Set computedFrom = getComputedFrom(pairs); + Set finalExpressions = templatePkt.getFinalExpressions(); + Event ev = null; + try { + // TODO consider methods other than get()!!! + ev = new Event(Consts.hostName, name, Attribute.of("get", val)); + } catch (Exception e) { + e.printStackTrace(); + } + logger.finest("Sending event to dependent reactive objects."); + clientEventForwarder.sendEvent(id, ev, computedFrom, finalExpressions, true); + + // Acknowledge the proxy + for (EventProxyPair pair : pairs) { + EventPacket evPkt = pair.getEventPacket(); + Proxy proxy = pair.getProxy(); + proxy.notifyEventProcessed(this, evPkt); + } + } else { + System.out.println(name + ": update call but waiting: " + eventProxyPair.toString()); + } + } + + public ChangeEvent change() { + return new ChangeEvent(this); + } + + private final void sentAdvertisement() { + Set subs = new HashSet(); + for (Proxy proxy : dependentProxies) { + Subscription sub = new Subscription(proxy.getHost(), proxy.getObject(), proxy.getProxyID(), new Constraint( + proxy.getMethod())); + subs.add(sub); + } + Advertisement adv = new Advertisement(Consts.hostName, name); + clientEventForwarder.advertise(adv, subs, true); + } + + private final Set getComputedFrom(Collection pairs) { + Set results = new HashSet(); + for (EventProxyPair pair : pairs) { + results.addAll(pair.getEventPacket().getComputedFrom()); + } + results.add(Consts.hostName + "." + name); + return results; + } + + @Override + public synchronized RemoteVar getProxy() { + if (proxy == null) { + proxy = new RemoteVar(name); + } + return proxy; + } + + public T get() { + return val; + } } diff --git a/Dream2/src/main/java/javareact/common/types/BooleanProxy.java b/Dream2/src/main/java/javareact/common/types/BooleanProxy.java index 7b27a70..614045b 100755 --- a/Dream2/src/main/java/javareact/common/types/BooleanProxy.java +++ b/Dream2/src/main/java/javareact/common/types/BooleanProxy.java @@ -5,11 +5,11 @@ import javareact.common.packets.content.ValueType; public class BooleanProxy extends RemoteVar { - public BooleanProxy(String host, String object) { - super(host, object); - } - - public BooleanProxy(String object) { - super(object); - } + public BooleanProxy(String host, String object) { + super(host, object); + } + + public BooleanProxy(String object) { + super(object); + } } diff --git a/Dream2/src/main/java/javareact/common/types/DoubleProxy.java b/Dream2/src/main/java/javareact/common/types/DoubleProxy.java index 52aff4a..cfbf71b 100755 --- a/Dream2/src/main/java/javareact/common/types/DoubleProxy.java +++ b/Dream2/src/main/java/javareact/common/types/DoubleProxy.java @@ -5,11 +5,11 @@ import javareact.common.packets.content.ValueType; public class DoubleProxy extends RemoteVar { - public DoubleProxy(String host, String object) { - super(host, object); - } - - public DoubleProxy(String object) { - super(object); - } + public DoubleProxy(String host, String object) { + super(host, object); + } + + public DoubleProxy(String object) { + super(object); + } } diff --git a/Dream2/src/main/java/javareact/common/types/EventProxyPair.java b/Dream2/src/main/java/javareact/common/types/EventProxyPair.java index 858cea0..5231808 100755 --- a/Dream2/src/main/java/javareact/common/types/EventProxyPair.java +++ b/Dream2/src/main/java/javareact/common/types/EventProxyPair.java @@ -3,26 +3,26 @@ import javareact.common.packets.EventPacket; public class EventProxyPair { - private final EventPacket eventPacket; - private final Proxy proxy; + private final EventPacket eventPacket; + private final Proxy proxy; - public EventProxyPair(EventPacket eventPacket, Proxy proxy) { - super(); - this.eventPacket = eventPacket; - this.proxy = proxy; - } + public EventProxyPair(EventPacket eventPacket, Proxy proxy) { + super(); + this.eventPacket = eventPacket; + this.proxy = proxy; + } - public final EventPacket getEventPacket() { - return eventPacket; - } + public final EventPacket getEventPacket() { + return eventPacket; + } - public final Proxy getProxy() { - return proxy; - } + public final Proxy getProxy() { + return proxy; + } - @Override - public String toString() { - return "EventProxyPair [eventPacket=" + eventPacket + ", proxy=" + proxy + "]"; - } + @Override + public String toString() { + return "EventProxyPair [eventPacket=" + eventPacket + ", proxy=" + proxy + "]"; + } } diff --git a/Dream2/src/main/java/javareact/common/types/ImpactsOn.java b/Dream2/src/main/java/javareact/common/types/ImpactsOn.java index f766ba6..b7d81be 100755 --- a/Dream2/src/main/java/javareact/common/types/ImpactsOn.java +++ b/Dream2/src/main/java/javareact/common/types/ImpactsOn.java @@ -5,5 +5,5 @@ @Retention(RetentionPolicy.RUNTIME) public @interface ImpactsOn { - String[] methods(); + String[] methods(); } diff --git a/Dream2/src/main/java/javareact/common/types/IntegerProxy.java b/Dream2/src/main/java/javareact/common/types/IntegerProxy.java index d04e695..ad9a346 100755 --- a/Dream2/src/main/java/javareact/common/types/IntegerProxy.java +++ b/Dream2/src/main/java/javareact/common/types/IntegerProxy.java @@ -5,11 +5,11 @@ import javareact.common.packets.content.ValueType; public class IntegerProxy extends RemoteVar { - public IntegerProxy(String host, String object) { - super(host, object); - } + public IntegerProxy(String host, String object) { + super(host, object); + } - public IntegerProxy(String object) { - super(object); - } + public IntegerProxy(String object) { + super(object); + } } diff --git a/Dream2/src/main/java/javareact/common/types/ListProxy.java b/Dream2/src/main/java/javareact/common/types/ListProxy.java index b513911..e4eea12 100755 --- a/Dream2/src/main/java/javareact/common/types/ListProxy.java +++ b/Dream2/src/main/java/javareact/common/types/ListProxy.java @@ -10,43 +10,43 @@ import javareact.common.packets.content.ValueType; public class ListProxy extends RemoteVar> { - public ListProxy(String host, String object) { - super(host, object); - } + public ListProxy(String host, String object) { + super(host, object); + } - public ListProxy(String object) { - super(object); - } + public ListProxy(String object) { + super(object); + } - public int size() { - return super.get().size(); - } + public int size() { + return super.get().size(); + } - public boolean isEmpty() { - return super.get().isEmpty(); - } + public boolean isEmpty() { + return super.get().isEmpty(); + } - public boolean contains(Object o) { - return super.get().contains(o); - } + public boolean contains(Object o) { + return super.get().contains(o); + } - public boolean containsAll(Collection c) { - return super.get().containsAll(c); - } + public boolean containsAll(Collection c) { + return super.get().containsAll(c); + } - public T get(int index) { - return super.get().get(index); - } + public T get(int index) { + return super.get().get(index); + } - public int indexOf(Object o) { - return super.get().indexOf(o); - } + public int indexOf(Object o) { + return super.get().indexOf(o); + } - public int lastIndexOf(Object o) { - return super.get().lastIndexOf(o); - } + public int lastIndexOf(Object o) { + return super.get().lastIndexOf(o); + } - public List subList(int fromIndex, int toIndex) { - return super.get().subList(fromIndex, toIndex); - } + public List subList(int fromIndex, int toIndex) { + return super.get().subList(fromIndex, toIndex); + } } diff --git a/Dream2/src/main/java/javareact/common/types/Observable.java b/Dream2/src/main/java/javareact/common/types/Observable.java index 9b72ad4..3bb55fd 100755 --- a/Dream2/src/main/java/javareact/common/types/Observable.java +++ b/Dream2/src/main/java/javareact/common/types/Observable.java @@ -11,31 +11,31 @@ import javareact.common.packets.content.Event; abstract class Observable implements ProxyGenerator { - protected final String observableId; - private final ClientEventForwarder forwarder; - private final boolean persistent; - - protected Observable(String observableId, boolean persistent) { - forwarder = ClientEventForwarder.get(); - this.observableId = observableId; - this.persistent = persistent; - sendAdvertisement(); - } - - protected Observable(String observableId) { - this(observableId, false); - } - - private final void sendAdvertisement() { - Advertisement adv = new Advertisement(Consts.hostName, observableId); - forwarder.advertise(adv, true); - } - - protected synchronized void sendEvent(Attribute[] attributes) { - Event ev = new Event(Consts.hostName, observableId, persistent, attributes); - Set computedFrom = new HashSet(); - computedFrom.add(ev.getSignature()); - forwarder.sendEvent(UUID.randomUUID(), ev, computedFrom, false); - } + protected final String observableId; + private final ClientEventForwarder forwarder; + private final boolean persistent; + + protected Observable(String observableId, boolean persistent) { + forwarder = ClientEventForwarder.get(); + this.observableId = observableId; + this.persistent = persistent; + sendAdvertisement(); + } + + protected Observable(String observableId) { + this(observableId, false); + } + + private final void sendAdvertisement() { + Advertisement adv = new Advertisement(Consts.hostName, observableId); + forwarder.advertise(adv, true); + } + + protected synchronized void sendEvent(Attribute[] attributes) { + Event ev = new Event(Consts.hostName, observableId, persistent, attributes); + Set computedFrom = new HashSet(); + computedFrom.add(ev.getSignature()); + forwarder.sendEvent(UUID.randomUUID(), ev, computedFrom, false); + } } diff --git a/Dream2/src/main/java/javareact/common/types/ObservableBool.java b/Dream2/src/main/java/javareact/common/types/ObservableBool.java index 3d45a74..4dd08d9 100755 --- a/Dream2/src/main/java/javareact/common/types/ObservableBool.java +++ b/Dream2/src/main/java/javareact/common/types/ObservableBool.java @@ -3,16 +3,16 @@ import javareact.common.packets.content.ValueType; public class ObservableBool extends Var { - public ObservableBool(String observableId, boolean persistent, Boolean val) { - super(observableId, persistent, val); - } + public ObservableBool(String observableId, boolean persistent, Boolean val) { + super(observableId, persistent, val); + } - public ObservableBool(String observableId, Boolean val) { - super(observableId, val); - } - - @Override - public final synchronized BooleanProxy getProxy() { - return (BooleanProxy)super.getProxy().toProxyOfType(ValueType.BOOL); - } + public ObservableBool(String observableId, Boolean val) { + super(observableId, val); + } + + @Override + public final synchronized BooleanProxy getProxy() { + return (BooleanProxy) super.getProxy().toProxyOfType(ValueType.BOOL); + } } diff --git a/Dream2/src/main/java/javareact/common/types/ObservableDouble.java b/Dream2/src/main/java/javareact/common/types/ObservableDouble.java index 66463aa..4365a3f 100755 --- a/Dream2/src/main/java/javareact/common/types/ObservableDouble.java +++ b/Dream2/src/main/java/javareact/common/types/ObservableDouble.java @@ -3,16 +3,16 @@ import javareact.common.packets.content.ValueType; public class ObservableDouble extends Var { - public ObservableDouble(String observableId, boolean persistent, Double val) { - super(observableId, persistent, val); - } + public ObservableDouble(String observableId, boolean persistent, Double val) { + super(observableId, persistent, val); + } - public ObservableDouble(String observableId, Double val) { - super(observableId, val); - } - - @Override - public final synchronized DoubleProxy getProxy() { - return (DoubleProxy)super.getProxy().toProxyOfType(ValueType.DOUBLE); - } + public ObservableDouble(String observableId, Double val) { + super(observableId, val); + } + + @Override + public final synchronized DoubleProxy getProxy() { + return (DoubleProxy) super.getProxy().toProxyOfType(ValueType.DOUBLE); + } } diff --git a/Dream2/src/main/java/javareact/common/types/ObservableInteger.java b/Dream2/src/main/java/javareact/common/types/ObservableInteger.java index 1a35332..c3b6742 100755 --- a/Dream2/src/main/java/javareact/common/types/ObservableInteger.java +++ b/Dream2/src/main/java/javareact/common/types/ObservableInteger.java @@ -3,16 +3,16 @@ import javareact.common.packets.content.ValueType; public class ObservableInteger extends Var { - public ObservableInteger(String observableId, boolean persistent, Integer val) { - super(observableId, persistent, val); - } + public ObservableInteger(String observableId, boolean persistent, Integer val) { + super(observableId, persistent, val); + } - public ObservableInteger(String observableId, Integer val) { - super(observableId, val); - } - - @Override - public final synchronized IntegerProxy getProxy() { - return (IntegerProxy)super.getProxy().toProxyOfType(ValueType.INT); - } + public ObservableInteger(String observableId, Integer val) { + super(observableId, val); + } + + @Override + public final synchronized IntegerProxy getProxy() { + return (IntegerProxy) super.getProxy().toProxyOfType(ValueType.INT); + } } diff --git a/Dream2/src/main/java/javareact/common/types/ObservableList.java b/Dream2/src/main/java/javareact/common/types/ObservableList.java index 166f91d..1aa9f98 100755 --- a/Dream2/src/main/java/javareact/common/types/ObservableList.java +++ b/Dream2/src/main/java/javareact/common/types/ObservableList.java @@ -6,99 +6,99 @@ import java.util.List; public class ObservableList extends Var> { - public ObservableList(String observableId, boolean persistent, List val) { - super(observableId, persistent, val); - } - - public ObservableList(String observableId, List val) { - super(observableId, val); - } - - public int size() { - return super.get().size(); - } - - public boolean isEmpty() { - return super.get().isEmpty(); - } - - public boolean contains(Object o) { - return super.get().contains(o); - } - - public synchronized boolean add(T e) { - boolean result = false; - modify(self -> self.add(e)); - return result; - } - - public synchronized boolean remove(Object o) { - boolean result = false; - modify(self -> self.remove(o)); - return result; - } - - public boolean containsAll(Collection c) { - return super.get().containsAll(c); - } - - public synchronized boolean addAll(Collection c) { - boolean result = false; - modify(self -> self.addAll(c)); - return result; - } - - public synchronized boolean addAll(int index, Collection c) { - boolean result = false; - modify(self -> self.addAll(index, c)); - return result; - } - - public synchronized boolean removeAll(Collection c) { - boolean result = false; - modify(self -> self.removeAll(c)); - return result; - } - - public synchronized boolean retainAll(Collection c) { - boolean result = false; - modify(self -> self.retainAll(c)); - return result; - } - - public synchronized void clear() { - modify(self -> self.clear()); - } - - public T get(int index) { - return super.get().get(index); - } - - public synchronized T set(int index, T element) { - T result = null; - modify(self -> self.set(index, element)); - return result; - } - - public synchronized void add(int index, T element) { - modify(self -> self.add(index, element)); - } - - public synchronized T remove(int index) { - T result = null; - modify(self -> self.remove(index)); - return result; - } - - public int indexOf(Object o) { - return super.get().indexOf(o); - } - - public int lastIndexOf(Object o) { - return super.get().lastIndexOf(o); - } - - public List subList(int fromIndex, int toIndex) { - return super.get().subList(fromIndex, toIndex); - } + public ObservableList(String observableId, boolean persistent, List val) { + super(observableId, persistent, val); + } + + public ObservableList(String observableId, List val) { + super(observableId, val); + } + + public int size() { + return super.get().size(); + } + + public boolean isEmpty() { + return super.get().isEmpty(); + } + + public boolean contains(Object o) { + return super.get().contains(o); + } + + public synchronized boolean add(T e) { + boolean result = false; + modify(self -> self.add(e)); + return result; + } + + public synchronized boolean remove(Object o) { + boolean result = false; + modify(self -> self.remove(o)); + return result; + } + + public boolean containsAll(Collection c) { + return super.get().containsAll(c); + } + + public synchronized boolean addAll(Collection c) { + boolean result = false; + modify(self -> self.addAll(c)); + return result; + } + + public synchronized boolean addAll(int index, Collection c) { + boolean result = false; + modify(self -> self.addAll(index, c)); + return result; + } + + public synchronized boolean removeAll(Collection c) { + boolean result = false; + modify(self -> self.removeAll(c)); + return result; + } + + public synchronized boolean retainAll(Collection c) { + boolean result = false; + modify(self -> self.retainAll(c)); + return result; + } + + public synchronized void clear() { + modify(self -> self.clear()); + } + + public T get(int index) { + return super.get().get(index); + } + + public synchronized T set(int index, T element) { + T result = null; + modify(self -> self.set(index, element)); + return result; + } + + public synchronized void add(int index, T element) { + modify(self -> self.add(index, element)); + } + + public synchronized T remove(int index) { + T result = null; + modify(self -> self.remove(index)); + return result; + } + + public int indexOf(Object o) { + return super.get().indexOf(o); + } + + public int lastIndexOf(Object o) { + return super.get().lastIndexOf(o); + } + + public List subList(int fromIndex, int toIndex) { + return super.get().subList(fromIndex, toIndex); + } } diff --git a/Dream2/src/main/java/javareact/common/types/ObservableString.java b/Dream2/src/main/java/javareact/common/types/ObservableString.java index 01c1a8a..bb54ad0 100755 --- a/Dream2/src/main/java/javareact/common/types/ObservableString.java +++ b/Dream2/src/main/java/javareact/common/types/ObservableString.java @@ -5,188 +5,188 @@ import javareact.common.packets.content.ValueType; public class ObservableString extends Var { - public ObservableString(String observableId, boolean persistent, String val) { - super(observableId, persistent, val); - } + public ObservableString(String observableId, boolean persistent, String val) { + super(observableId, persistent, val); + } - public ObservableString(String observableId, String val) { - super(observableId, val); - } + public ObservableString(String observableId, String val) { + super(observableId, val); + } - public int length() { - return super.get().length(); - } + public int length() { + return super.get().length(); + } - public boolean isEmpty() { - return super.get().isEmpty(); - } + public boolean isEmpty() { + return super.get().isEmpty(); + } - public char charAt(int index) { - return super.get().charAt(index); - } + public char charAt(int index) { + return super.get().charAt(index); + } - public int codePointAt(int index) { - return super.get().codePointAt(index); - } + public int codePointAt(int index) { + return super.get().codePointAt(index); + } - public int codePointBefore(int index) { - return super.get().codePointBefore(index); - } + public int codePointBefore(int index) { + return super.get().codePointBefore(index); + } - public int codePointCount(int beginIndex, int endIndex) { - return super.get().codePointCount(beginIndex, endIndex); - } + public int codePointCount(int beginIndex, int endIndex) { + return super.get().codePointCount(beginIndex, endIndex); + } - public int offsetByCodePoints(int index, int codePointOffset) { - return super.get().offsetByCodePoints(index, codePointOffset); - } + public int offsetByCodePoints(int index, int codePointOffset) { + return super.get().offsetByCodePoints(index, codePointOffset); + } - public byte[] getBytes() { - return super.get().getBytes(); - } + public byte[] getBytes() { + return super.get().getBytes(); + } - public boolean contentEquals(StringBuffer sb) { - return super.get().contentEquals(sb); - } + public boolean contentEquals(StringBuffer sb) { + return super.get().contentEquals(sb); + } - public boolean contentEquals(CharSequence cs) { - return super.get().contentEquals(cs); - } + public boolean contentEquals(CharSequence cs) { + return super.get().contentEquals(cs); + } - public int compareTo(String anotherString) { - return super.get().compareTo(anotherString); - } + public int compareTo(String anotherString) { + return super.get().compareTo(anotherString); + } - public int compareToIgnoreCase(String str) { - return super.get().compareToIgnoreCase(str); - } + public int compareToIgnoreCase(String str) { + return super.get().compareToIgnoreCase(str); + } - public boolean regionMatches(int toffset, String other, int ooffset, int len) { - return super.get().regionMatches(toffset, other, ooffset, len); - } + public boolean regionMatches(int toffset, String other, int ooffset, int len) { + return super.get().regionMatches(toffset, other, ooffset, len); + } - public boolean regionMatches(boolean ignoreCase, int toffset, String other, int ooffset, int len) { - return super.get().regionMatches(ignoreCase, toffset, other, ooffset, len); - } + public boolean regionMatches(boolean ignoreCase, int toffset, String other, int ooffset, int len) { + return super.get().regionMatches(ignoreCase, toffset, other, ooffset, len); + } - public boolean startsWith(String prefix, int toffset) { - return super.get().startsWith(prefix, toffset); - } + public boolean startsWith(String prefix, int toffset) { + return super.get().startsWith(prefix, toffset); + } - public boolean startsWith(String prefix) { - return super.get().startsWith(prefix); - } + public boolean startsWith(String prefix) { + return super.get().startsWith(prefix); + } - public boolean endsWith(String suffix) { - return super.get().endsWith(suffix); - } + public boolean endsWith(String suffix) { + return super.get().endsWith(suffix); + } - public int indexOf(int ch) { - return super.get().indexOf(ch); - } - - public int indexOf(int ch, int fromIndex) { - return super.get().indexOf(ch, fromIndex); - } - - public int lastIndexOf(int ch) { - return super.get().lastIndexOf(ch); - } - - public int lastIndexOf(int ch, int fromIndex) { - return super.get().lastIndexOf(ch, fromIndex); - } - - public int indexOf(String str) { - return super.get().indexOf(str); - } - - public int indexOf(String str, int fromIndex) { - return super.get().indexOf(str, fromIndex); - } - - public int lastIndexOf(String str) { - return super.get().lastIndexOf(str); - } - - public int lastIndexOf(String str, int fromIndex) { - return super.get().lastIndexOf(str, fromIndex); - } - - public String substring(int beginIndex) { - return super.get().substring(beginIndex); - } - - public String substring(int beginIndex, int endIndex) { - return super.get().substring(beginIndex, endIndex); - } - - public CharSequence subSequence(int beginIndex, int endIndex) { - return super.get().subSequence(beginIndex, endIndex); - } - - public String concat(String str) { - return super.get().concat(str); - } - - public String replace(char oldChar, char newChar) { - return super.get().replace(oldChar, newChar); - } - - public boolean matches(String regex) { - return super.get().matches(regex); - } - - public boolean contains(CharSequence s) { - return super.get().contains(s); - } - - public String replaceFirst(String regex, String replacement) { - return super.get().replaceFirst(regex, replacement); - } - - public String replaceAll(String regex, String replacement) { - return super.get().replaceAll(regex, replacement); - } - - public String replace(CharSequence target, CharSequence replacement) { - return super.get().replace(target, replacement); - } - - public String[] split(String regex, int limit) { - return super.get().split(regex, limit); - } - - public String[] split(String regex) { - return super.get().split(regex); - } - - public String toLowerCase(Locale locale) { - return super.get().toLowerCase(locale); - } - - public String toLowerCase() { - return super.get().toLowerCase(); - } - - public String toUpperCase(Locale locale) { - return super.get().toUpperCase(locale); - } - - public String toUpperCase() { - return super.get().toUpperCase(); - } - - public String trim() { - return super.get().trim(); - } - - public char[] toCharArray() { - return super.get().toCharArray(); - } - - @Override - public final synchronized StringProxy getProxy() { - return (StringProxy)super.getProxy().toProxyOfType(ValueType.STRING); - } + public int indexOf(int ch) { + return super.get().indexOf(ch); + } + + public int indexOf(int ch, int fromIndex) { + return super.get().indexOf(ch, fromIndex); + } + + public int lastIndexOf(int ch) { + return super.get().lastIndexOf(ch); + } + + public int lastIndexOf(int ch, int fromIndex) { + return super.get().lastIndexOf(ch, fromIndex); + } + + public int indexOf(String str) { + return super.get().indexOf(str); + } + + public int indexOf(String str, int fromIndex) { + return super.get().indexOf(str, fromIndex); + } + + public int lastIndexOf(String str) { + return super.get().lastIndexOf(str); + } + + public int lastIndexOf(String str, int fromIndex) { + return super.get().lastIndexOf(str, fromIndex); + } + + public String substring(int beginIndex) { + return super.get().substring(beginIndex); + } + + public String substring(int beginIndex, int endIndex) { + return super.get().substring(beginIndex, endIndex); + } + + public CharSequence subSequence(int beginIndex, int endIndex) { + return super.get().subSequence(beginIndex, endIndex); + } + + public String concat(String str) { + return super.get().concat(str); + } + + public String replace(char oldChar, char newChar) { + return super.get().replace(oldChar, newChar); + } + + public boolean matches(String regex) { + return super.get().matches(regex); + } + + public boolean contains(CharSequence s) { + return super.get().contains(s); + } + + public String replaceFirst(String regex, String replacement) { + return super.get().replaceFirst(regex, replacement); + } + + public String replaceAll(String regex, String replacement) { + return super.get().replaceAll(regex, replacement); + } + + public String replace(CharSequence target, CharSequence replacement) { + return super.get().replace(target, replacement); + } + + public String[] split(String regex, int limit) { + return super.get().split(regex, limit); + } + + public String[] split(String regex) { + return super.get().split(regex); + } + + public String toLowerCase(Locale locale) { + return super.get().toLowerCase(locale); + } + + public String toLowerCase() { + return super.get().toLowerCase(); + } + + public String toUpperCase(Locale locale) { + return super.get().toUpperCase(locale); + } + + public String toUpperCase() { + return super.get().toUpperCase(); + } + + public String trim() { + return super.get().trim(); + } + + public char[] toCharArray() { + return super.get().toCharArray(); + } + + @Override + public final synchronized StringProxy getProxy() { + return (StringProxy) super.getProxy().toProxyOfType(ValueType.STRING); + } } diff --git a/Dream2/src/main/java/javareact/common/types/Proxy.java b/Dream2/src/main/java/javareact/common/types/Proxy.java index a58a572..c0354f0 100755 --- a/Dream2/src/main/java/javareact/common/types/Proxy.java +++ b/Dream2/src/main/java/javareact/common/types/Proxy.java @@ -16,123 +16,126 @@ import javareact.common.packets.content.Subscription; public abstract class Proxy implements Subscriber, ProxyGenerator { - private final ClientEventForwarder forwarder; - private final Set listeners = new HashSet(); - - private final Queue eventsQueue = new ArrayDeque(); - private final Set pendingAcks = new HashSet(); - - private final Logger logger = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME); - - protected final String host; - protected final String object; - protected final String method = "get"; - private final UUID proxyID; - - public Proxy(String name) { - //this(Consts.hostName, object); - if (name.contains("@")) { - String[] s = name.split("@", 2); - this.host = s[1]; - this.object = s[0]; - } - else { - this.host = Consts.hostName; - this.object = name; - } - - forwarder = ClientEventForwarder.get(); - proxyID = UUID.randomUUID(); - Subscription sub = new Subscription(host, object, proxyID, new Constraint(method)); - forwarder.addSubscription(this, sub); - } - - public Proxy(String host, String object) { - this(object + "@" + host); -// this.host = host; -// this.object = object; -// forwarder = ClientEventForwarder.get(); -// proxyID = UUID.randomUUID(); -// Subscription sub = new Subscription(host, object, proxyID, new Constraint(method)); -// forwarder.addSubscription(this, sub); - } - - final void addProxyChangeListener(ProxyChangeListener listener) { - listeners.add(listener); - } - - final void removeProxyChangeListener(ProxyChangeListener listener) { - listeners.remove(listener); - } - - @Override - public void notifyValueChanged(EventPacket evPkt) { - eventsQueue.add(evPkt); - logger.finest("Received event packet " + evPkt + ". Added to the queue."); - if (eventsQueue.size() == 1) { - logger.finest("The element is the only one in the queue. Let's process it."); - processEvent(evPkt.getEvent()); - sendEventPacketToListeners(evPkt); - } - } - - /** - * Method invoked by a ProxyChangeListener to acknowledge that the eventPacket has been processed. After receiving - * this acknowledgement from all registered ProxyChangeListeners, the Proxy can safely start processing the next - * EventPacket received (if any). - * - * @param proxyChangeListener the ProxyChangeListener. - * @param event the event processed by the proxy. - */ - final void notifyEventProcessed(ProxyChangeListener proxyChangeListener, EventPacket event) { - pendingAcks.remove(proxyChangeListener); - processNextEvent(); - } - - private void processNextEvent() { - if (pendingAcks.isEmpty() && !eventsQueue.isEmpty()) { - assert (!eventsQueue.isEmpty()); - eventsQueue.poll(); - EventPacket nextPkt = eventsQueue.peek(); - if (nextPkt != null) { - processEvent(nextPkt.getEvent()); - sendEventPacketToListeners(nextPkt); - } - } - } - - protected abstract void processEvent(Event ev); - - private final void sendEventPacketToListeners(EventPacket evPkt) { - if (!listeners.isEmpty()) { - pendingAcks.addAll(listeners); - EventProxyPair pair = new EventProxyPair(evPkt, this); - for (ProxyChangeListener listener : listeners) { - listener.update(pair); - } - } else - processNextEvent(); - } - - final String getHost() { - return host; - } - - final String getObject() { - return object; - } - - final String getMethod() { - return method; - } - - final UUID getProxyID() { - return proxyID; - } - - @Override - public Proxy getProxy() { - return this; - } + private final ClientEventForwarder forwarder; + private final Set listeners = new HashSet(); + + private final Queue eventsQueue = new ArrayDeque(); + private final Set pendingAcks = new HashSet(); + + private final Logger logger = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME); + + protected final String host; + protected final String object; + protected final String method = "get"; + private final UUID proxyID; + + public Proxy(String name) { + // this(Consts.hostName, object); + if (name.contains("@")) { + String[] s = name.split("@", 2); + this.host = s[1]; + this.object = s[0]; + } else { + this.host = Consts.hostName; + this.object = name; + } + + forwarder = ClientEventForwarder.get(); + proxyID = UUID.randomUUID(); + Subscription sub = new Subscription(host, object, proxyID, new Constraint(method)); + forwarder.addSubscription(this, sub); + } + + public Proxy(String host, String object) { + this(object + "@" + host); + // this.host = host; + // this.object = object; + // forwarder = ClientEventForwarder.get(); + // proxyID = UUID.randomUUID(); + // Subscription sub = new Subscription(host, object, proxyID, new + // Constraint(method)); + // forwarder.addSubscription(this, sub); + } + + final void addProxyChangeListener(ProxyChangeListener listener) { + listeners.add(listener); + } + + final void removeProxyChangeListener(ProxyChangeListener listener) { + listeners.remove(listener); + } + + @Override + public void notifyValueChanged(EventPacket evPkt) { + eventsQueue.add(evPkt); + logger.finest("Received event packet " + evPkt + ". Added to the queue."); + if (eventsQueue.size() == 1) { + logger.finest("The element is the only one in the queue. Let's process it."); + processEvent(evPkt.getEvent()); + sendEventPacketToListeners(evPkt); + } + } + + /** + * Method invoked by a ProxyChangeListener to acknowledge that the + * eventPacket has been processed. After receiving this acknowledgement from + * all registered ProxyChangeListeners, the Proxy can safely start + * processing the next EventPacket received (if any). + * + * @param proxyChangeListener + * the ProxyChangeListener. + * @param event + * the event processed by the proxy. + */ + final void notifyEventProcessed(ProxyChangeListener proxyChangeListener, EventPacket event) { + pendingAcks.remove(proxyChangeListener); + processNextEvent(); + } + + private void processNextEvent() { + if (pendingAcks.isEmpty() && !eventsQueue.isEmpty()) { + assert (!eventsQueue.isEmpty()); + eventsQueue.poll(); + EventPacket nextPkt = eventsQueue.peek(); + if (nextPkt != null) { + processEvent(nextPkt.getEvent()); + sendEventPacketToListeners(nextPkt); + } + } + } + + protected abstract void processEvent(Event ev); + + private final void sendEventPacketToListeners(EventPacket evPkt) { + if (!listeners.isEmpty()) { + pendingAcks.addAll(listeners); + EventProxyPair pair = new EventProxyPair(evPkt, this); + for (ProxyChangeListener listener : listeners) { + listener.update(pair); + } + } else + processNextEvent(); + } + + final String getHost() { + return host; + } + + final String getObject() { + return object; + } + + final String getMethod() { + return method; + } + + final UUID getProxyID() { + return proxyID; + } + + @Override + public Proxy getProxy() { + return this; + } } diff --git a/Dream2/src/main/java/javareact/common/types/ProxyChangeListener.java b/Dream2/src/main/java/javareact/common/types/ProxyChangeListener.java index 84a8249..64381e0 100755 --- a/Dream2/src/main/java/javareact/common/types/ProxyChangeListener.java +++ b/Dream2/src/main/java/javareact/common/types/ProxyChangeListener.java @@ -1,14 +1,14 @@ package javareact.common.types; - /** - * Interface used to register to a proxy. It exposes an update method called whenever the proxy changes. + * Interface used to register to a proxy. It exposes an update method called + * whenever the proxy changes. */ interface ProxyChangeListener { - /** - * Method invoked from a proxy to trigger an update. - */ - void update(EventProxyPair eventProxyPair); + /** + * Method invoked from a proxy to trigger an update. + */ + void update(EventProxyPair eventProxyPair); } diff --git a/Dream2/src/main/java/javareact/common/types/ProxyGenerator.java b/Dream2/src/main/java/javareact/common/types/ProxyGenerator.java index db725e0..05cf658 100755 --- a/Dream2/src/main/java/javareact/common/types/ProxyGenerator.java +++ b/Dream2/src/main/java/javareact/common/types/ProxyGenerator.java @@ -1,15 +1,16 @@ package javareact.common.types; /** - * A ProxyGenerator can return a proxy which gets automatically notified when its state changes. + * A ProxyGenerator can return a proxy which gets automatically notified when + * its state changes. */ public interface ProxyGenerator { - /** - * Return a proxy for the object. - * - * @return a proxy for the object. - */ - public Proxy getProxy(); + /** + * Return a proxy for the object. + * + * @return a proxy for the object. + */ + public Proxy getProxy(); } diff --git a/Dream2/src/main/java/javareact/common/types/Reactive.java b/Dream2/src/main/java/javareact/common/types/Reactive.java index 2a9d8aa..2b88b0c 100755 --- a/Dream2/src/main/java/javareact/common/types/Reactive.java +++ b/Dream2/src/main/java/javareact/common/types/Reactive.java @@ -3,15 +3,16 @@ /** * Represents a generic reactive object. * - * In particular, it exposes an evaluate() method which is automatically invoked whenever one of the observable methods - * it depends on changes. + * In particular, it exposes an evaluate() method which is automatically invoked + * whenever one of the observable methods it depends on changes. * */ public interface Reactive extends ProxyGenerator { - /** - * The evaluate method is automatically invoked whenever one of the observable methods this object depends on changes. - */ - public T evaluate(); + /** + * The evaluate method is automatically invoked whenever one of the + * observable methods this object depends on changes. + */ + public T evaluate(); } diff --git a/Dream2/src/main/java/javareact/common/types/ReactiveBoolean.java b/Dream2/src/main/java/javareact/common/types/ReactiveBoolean.java index c3f1b71..edc9d4e 100755 --- a/Dream2/src/main/java/javareact/common/types/ReactiveBoolean.java +++ b/Dream2/src/main/java/javareact/common/types/ReactiveBoolean.java @@ -1,13 +1,13 @@ package javareact.common.types; public abstract class ReactiveBoolean extends AbstractReactive { - public ReactiveBoolean(String name, Proxy... proxies) { - super(name, proxies); - val = false; - } - - @Override - public final synchronized BooleanProxy getProxy() { - return (BooleanProxy)super.getProxy(); - } + public ReactiveBoolean(String name, Proxy... proxies) { + super(name, proxies); + val = false; + } + + @Override + public final synchronized BooleanProxy getProxy() { + return (BooleanProxy) super.getProxy(); + } } diff --git a/Dream2/src/main/java/javareact/common/types/ReactiveChangeListener.java b/Dream2/src/main/java/javareact/common/types/ReactiveChangeListener.java index 8a0c09f..1a9dc10 100755 --- a/Dream2/src/main/java/javareact/common/types/ReactiveChangeListener.java +++ b/Dream2/src/main/java/javareact/common/types/ReactiveChangeListener.java @@ -1,10 +1,11 @@ package javareact.common.types; /** - * A ReactiveChangeListener can register to a reactive object R and gets notified whenever the value of R changes. + * A ReactiveChangeListener can register to a reactive object R and gets + * notified whenever the value of R changes. */ public interface ReactiveChangeListener { - public void notifyReactiveChanged(T oldValue, T newValue, String host); + public void notifyReactiveChanged(T oldValue, T newValue, String host); } \ No newline at end of file diff --git a/Dream2/src/main/java/javareact/common/types/ReactiveDouble.java b/Dream2/src/main/java/javareact/common/types/ReactiveDouble.java index ad30d0d..78ffb66 100755 --- a/Dream2/src/main/java/javareact/common/types/ReactiveDouble.java +++ b/Dream2/src/main/java/javareact/common/types/ReactiveDouble.java @@ -1,13 +1,13 @@ package javareact.common.types; public abstract class ReactiveDouble extends AbstractReactive { - public ReactiveDouble(String name, Proxy... proxies) { - super(name, proxies); - val = 0.0; - } - - @Override - public final synchronized DoubleProxy getProxy() { - return (DoubleProxy)super.getProxy(); - } + public ReactiveDouble(String name, Proxy... proxies) { + super(name, proxies); + val = 0.0; + } + + @Override + public final synchronized DoubleProxy getProxy() { + return (DoubleProxy) super.getProxy(); + } } \ No newline at end of file diff --git a/Dream2/src/main/java/javareact/common/types/ReactiveInteger.java b/Dream2/src/main/java/javareact/common/types/ReactiveInteger.java index b91f956..8980fde 100755 --- a/Dream2/src/main/java/javareact/common/types/ReactiveInteger.java +++ b/Dream2/src/main/java/javareact/common/types/ReactiveInteger.java @@ -3,13 +3,13 @@ import javareact.common.packets.content.ValueType; public abstract class ReactiveInteger extends AbstractReactive { - public ReactiveInteger(String name, Proxy... proxies) { - super(name, proxies); - val = 0; - } - - @Override - public final synchronized IntegerProxy getProxy() { - return (IntegerProxy)super.getProxy().toProxyOfType(ValueType.INT); - } + public ReactiveInteger(String name, Proxy... proxies) { + super(name, proxies); + val = 0; + } + + @Override + public final synchronized IntegerProxy getProxy() { + return (IntegerProxy) super.getProxy().toProxyOfType(ValueType.INT); + } } diff --git a/Dream2/src/main/java/javareact/common/types/ReactiveList.java b/Dream2/src/main/java/javareact/common/types/ReactiveList.java index 2fd4716..ac7b970 100755 --- a/Dream2/src/main/java/javareact/common/types/ReactiveList.java +++ b/Dream2/src/main/java/javareact/common/types/ReactiveList.java @@ -6,36 +6,36 @@ import java.util.List; public abstract class ReactiveList extends AbstractReactive> { - public ReactiveList(String name, Proxy... proxies) { - super(name, proxies); - val = new ArrayList(); - } - - public int size() { - return val.size(); - } - - public boolean isEmpty() { - return val.isEmpty(); - } - - public boolean contains(Object o) { - return val.contains(o); - } - - public boolean containsAll(Collection c) { - return val.containsAll(c); - } - - public T get(int index) { - return val.get(index); - } - - public int indexOf(Object o) { - return val.indexOf(o); - } - - public List subList(int fromIndex, int toIndex) { - return val.subList(fromIndex, toIndex); - } + public ReactiveList(String name, Proxy... proxies) { + super(name, proxies); + val = new ArrayList(); + } + + public int size() { + return val.size(); + } + + public boolean isEmpty() { + return val.isEmpty(); + } + + public boolean contains(Object o) { + return val.contains(o); + } + + public boolean containsAll(Collection c) { + return val.containsAll(c); + } + + public T get(int index) { + return val.get(index); + } + + public int indexOf(Object o) { + return val.indexOf(o); + } + + public List subList(int fromIndex, int toIndex) { + return val.subList(fromIndex, toIndex); + } } diff --git a/Dream2/src/main/java/javareact/common/types/ReactiveString.java b/Dream2/src/main/java/javareact/common/types/ReactiveString.java index 7f110fa..3947274 100755 --- a/Dream2/src/main/java/javareact/common/types/ReactiveString.java +++ b/Dream2/src/main/java/javareact/common/types/ReactiveString.java @@ -1,13 +1,13 @@ package javareact.common.types; public abstract class ReactiveString extends AbstractReactive { - public ReactiveString(String name, Proxy... proxies) { - super(name, proxies); - val = ""; - } - - @Override - public final synchronized StringProxy getProxy() { - return (StringProxy)super.getProxy(); - } + public ReactiveString(String name, Proxy... proxies) { + super(name, proxies); + val = ""; + } + + @Override + public final synchronized StringProxy getProxy() { + return (StringProxy) super.getProxy(); + } } diff --git a/Dream2/src/main/java/javareact/common/types/RemoteVar.java b/Dream2/src/main/java/javareact/common/types/RemoteVar.java index f31d4c4..fd48c64 100755 --- a/Dream2/src/main/java/javareact/common/types/RemoteVar.java +++ b/Dream2/src/main/java/javareact/common/types/RemoteVar.java @@ -43,7 +43,7 @@ public final Proxy toProxyOfType(ValueType t) { protected void processEvent(Event ev) { if (ev.hasAttribute(method)) { Attribute attr = ev.getAttributeFor(method); - val = (T)attr.getValue(); + val = (T) attr.getValue(); } } }; @@ -55,7 +55,7 @@ protected final void processEvent(Event ev) { if (ev.hasAttribute(method)) { Attribute attr = ev.getAttributeFor(method); oldVal = val; - val = (T)attr.getValue(); + val = (T) attr.getValue(); } } diff --git a/Dream2/src/main/java/javareact/common/types/Signal.java b/Dream2/src/main/java/javareact/common/types/Signal.java index 4975d76..75b0b72 100755 --- a/Dream2/src/main/java/javareact/common/types/Signal.java +++ b/Dream2/src/main/java/javareact/common/types/Signal.java @@ -14,11 +14,11 @@ private static Proxy[] proxiesFromVars(ProxyGenerator[] vars) { return proxies; } -// public Signal(String name, Supplier evaluation, Proxy... proxies) { -// super(name, proxies); -// val = null; -// this.evaluation = evaluation; -// } + // public Signal(String name, Supplier evaluation, Proxy... proxies) { + // super(name, proxies); + // val = null; + // this.evaluation = evaluation; + // } @SafeVarargs public Signal(String name, Supplier evaluation, ProxyGenerator... vars) { diff --git a/Dream2/src/main/java/javareact/common/types/StringProxy.java b/Dream2/src/main/java/javareact/common/types/StringProxy.java index d126deb..d4a8779 100755 --- a/Dream2/src/main/java/javareact/common/types/StringProxy.java +++ b/Dream2/src/main/java/javareact/common/types/StringProxy.java @@ -9,206 +9,206 @@ import javareact.common.packets.content.ValueType; public class StringProxy extends RemoteVar { - private String val = ""; + private String val = ""; - public StringProxy(String host, String object) { - super(host, object); - } + public StringProxy(String host, String object) { + super(host, object); + } - public StringProxy(String object) { - super(object); - } + public StringProxy(String object) { + super(object); + } - public int length() { - return val.length(); - } + public int length() { + return val.length(); + } - public boolean isEmpty() { - return val.isEmpty(); - } + public boolean isEmpty() { + return val.isEmpty(); + } - public char charAt(int index) { - return val.charAt(index); - } + public char charAt(int index) { + return val.charAt(index); + } - public int codePointAt(int index) { - return val.codePointAt(index); - } + public int codePointAt(int index) { + return val.codePointAt(index); + } - public int codePointBefore(int index) { - return val.codePointBefore(index); - } + public int codePointBefore(int index) { + return val.codePointBefore(index); + } - public int codePointCount(int beginIndex, int endIndex) { - return val.codePointCount(beginIndex, endIndex); - } + public int codePointCount(int beginIndex, int endIndex) { + return val.codePointCount(beginIndex, endIndex); + } - public int offsetByCodePoints(int index, int codePointOffset) { - return val.offsetByCodePoints(index, codePointOffset); - } + public int offsetByCodePoints(int index, int codePointOffset) { + return val.offsetByCodePoints(index, codePointOffset); + } - public void getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin) { - val.getChars(srcBegin, srcEnd, dst, dstBegin); - } + public void getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin) { + val.getChars(srcBegin, srcEnd, dst, dstBegin); + } - public byte[] getBytes(String charsetName) throws UnsupportedEncodingException { - return val.getBytes(charsetName); - } + public byte[] getBytes(String charsetName) throws UnsupportedEncodingException { + return val.getBytes(charsetName); + } - public byte[] getBytes(Charset charset) { - return val.getBytes(charset); - } + public byte[] getBytes(Charset charset) { + return val.getBytes(charset); + } - public byte[] getBytes() { - return val.getBytes(); - } + public byte[] getBytes() { + return val.getBytes(); + } - public boolean contentEquals(StringBuffer sb) { - return val.contentEquals(sb); - } + public boolean contentEquals(StringBuffer sb) { + return val.contentEquals(sb); + } - public boolean contentEquals(CharSequence cs) { - return val.contentEquals(cs); - } + public boolean contentEquals(CharSequence cs) { + return val.contentEquals(cs); + } - public boolean equalsIgnoreCase(String anotherString) { - return val.equalsIgnoreCase(anotherString); - } + public boolean equalsIgnoreCase(String anotherString) { + return val.equalsIgnoreCase(anotherString); + } - public int compareTo(String anotherString) { - return val.compareTo(anotherString); - } + public int compareTo(String anotherString) { + return val.compareTo(anotherString); + } - public int compareToIgnoreCase(String str) { - return val.compareToIgnoreCase(str); - } + public int compareToIgnoreCase(String str) { + return val.compareToIgnoreCase(str); + } - public boolean regionMatches(int toffset, String other, int ooffset, int len) { - return val.regionMatches(toffset, other, ooffset, len); - } + public boolean regionMatches(int toffset, String other, int ooffset, int len) { + return val.regionMatches(toffset, other, ooffset, len); + } - public boolean regionMatches(boolean ignoreCase, int toffset, String other, int ooffset, int len) { - return val.regionMatches(ignoreCase, toffset, other, ooffset, len); - } + public boolean regionMatches(boolean ignoreCase, int toffset, String other, int ooffset, int len) { + return val.regionMatches(ignoreCase, toffset, other, ooffset, len); + } - public boolean startsWith(String prefix, int toffset) { - return val.startsWith(prefix, toffset); - } + public boolean startsWith(String prefix, int toffset) { + return val.startsWith(prefix, toffset); + } - public boolean startsWith(String prefix) { - return val.startsWith(prefix); - } + public boolean startsWith(String prefix) { + return val.startsWith(prefix); + } - public boolean endsWith(String suffix) { - return val.endsWith(suffix); - } + public boolean endsWith(String suffix) { + return val.endsWith(suffix); + } - public int indexOf(int ch) { - return val.indexOf(ch); - } + public int indexOf(int ch) { + return val.indexOf(ch); + } - public int indexOf(int ch, int fromIndex) { - return val.indexOf(ch, fromIndex); - } + public int indexOf(int ch, int fromIndex) { + return val.indexOf(ch, fromIndex); + } - public int lastIndexOf(int ch) { - return val.lastIndexOf(ch); - } + public int lastIndexOf(int ch) { + return val.lastIndexOf(ch); + } - public int lastIndexOf(int ch, int fromIndex) { - return val.lastIndexOf(ch, fromIndex); - } + public int lastIndexOf(int ch, int fromIndex) { + return val.lastIndexOf(ch, fromIndex); + } - public int indexOf(String str) { - return val.indexOf(str); - } + public int indexOf(String str) { + return val.indexOf(str); + } - public int indexOf(String str, int fromIndex) { - return val.indexOf(str, fromIndex); - } + public int indexOf(String str, int fromIndex) { + return val.indexOf(str, fromIndex); + } - public int lastIndexOf(String str) { - return val.lastIndexOf(str); - } + public int lastIndexOf(String str) { + return val.lastIndexOf(str); + } - public int lastIndexOf(String str, int fromIndex) { - return val.lastIndexOf(str, fromIndex); - } + public int lastIndexOf(String str, int fromIndex) { + return val.lastIndexOf(str, fromIndex); + } - public String substring(int beginIndex) { - return val.substring(beginIndex); - } + public String substring(int beginIndex) { + return val.substring(beginIndex); + } - public String substring(int beginIndex, int endIndex) { - return val.substring(beginIndex, endIndex); - } + public String substring(int beginIndex, int endIndex) { + return val.substring(beginIndex, endIndex); + } - public CharSequence subSequence(int beginIndex, int endIndex) { - return val.subSequence(beginIndex, endIndex); - } + public CharSequence subSequence(int beginIndex, int endIndex) { + return val.subSequence(beginIndex, endIndex); + } - public String concat(String str) { - return val.concat(str); - } + public String concat(String str) { + return val.concat(str); + } - public String replace(char oldChar, char newChar) { - return val.replace(oldChar, newChar); - } + public String replace(char oldChar, char newChar) { + return val.replace(oldChar, newChar); + } - public boolean matches(String regex) { - return val.matches(regex); - } + public boolean matches(String regex) { + return val.matches(regex); + } - public boolean contains(CharSequence s) { - return val.contains(s); - } + public boolean contains(CharSequence s) { + return val.contains(s); + } - public String replaceFirst(String regex, String replacement) { - return val.replaceFirst(regex, replacement); - } + public String replaceFirst(String regex, String replacement) { + return val.replaceFirst(regex, replacement); + } - public String replaceAll(String regex, String replacement) { - return val.replaceAll(regex, replacement); - } + public String replaceAll(String regex, String replacement) { + return val.replaceAll(regex, replacement); + } - public String replace(CharSequence target, CharSequence replacement) { - return val.replace(target, replacement); - } + public String replace(CharSequence target, CharSequence replacement) { + return val.replace(target, replacement); + } - public String[] split(String regex, int limit) { - return val.split(regex, limit); - } + public String[] split(String regex, int limit) { + return val.split(regex, limit); + } - public String[] split(String regex) { - return val.split(regex); - } + public String[] split(String regex) { + return val.split(regex); + } - public String toLowerCase(Locale locale) { - return val.toLowerCase(locale); - } + public String toLowerCase(Locale locale) { + return val.toLowerCase(locale); + } - public String toLowerCase() { - return val.toLowerCase(); - } + public String toLowerCase() { + return val.toLowerCase(); + } - public String toUpperCase(Locale locale) { - return val.toUpperCase(locale); - } + public String toUpperCase(Locale locale) { + return val.toUpperCase(locale); + } - public String toUpperCase() { - return val.toUpperCase(); - } + public String toUpperCase() { + return val.toUpperCase(); + } - public String trim() { - return val.trim(); - } + public String trim() { + return val.trim(); + } - @Override - public String toString() { - return val.toString(); - } - - public char[] toCharArray() { - return val.toCharArray(); - } + @Override + public String toString() { + return val.toString(); + } + + public char[] toCharArray() { + return val.toCharArray(); + } } diff --git a/Dream2/src/main/java/javareact/common/types/Var.java b/Dream2/src/main/java/javareact/common/types/Var.java index b103eda..d0019a3 100755 --- a/Dream2/src/main/java/javareact/common/types/Var.java +++ b/Dream2/src/main/java/javareact/common/types/Var.java @@ -5,49 +5,48 @@ import javareact.common.packets.content.Value; public class Var extends Observable { - private T val; - private RemoteVar proxy; - - public Var(String observableId, boolean persistent, T val) { - super(observableId, persistent); - set(val); - } - - public Var(String observableId, T val) { - super(observableId); - set(val); - } - - public final synchronized void set(T val) { - this.val = val; - impactOnGet(); - } - - public final synchronized void modify(Consumer modification) { - modification.accept(this.val); - impactOnGet(); - } - - public final T get() { - return val; - } - - @Override - public synchronized RemoteVar getProxy() { - if (proxy == null) { - proxy = new RemoteVar(observableId); - } - return proxy; - } - - private final void impactOnGet() { - Attribute[] attrs = new Attribute[1]; - try { - attrs[0] = new Attribute("get", get()); - } - catch (Exception e) { - e.printStackTrace(); - } - sendEvent(attrs); - } + private T val; + private RemoteVar proxy; + + public Var(String observableId, boolean persistent, T val) { + super(observableId, persistent); + set(val); + } + + public Var(String observableId, T val) { + super(observableId); + set(val); + } + + public final synchronized void set(T val) { + this.val = val; + impactOnGet(); + } + + public final synchronized void modify(Consumer modification) { + modification.accept(this.val); + impactOnGet(); + } + + public final T get() { + return val; + } + + @Override + public synchronized RemoteVar getProxy() { + if (proxy == null) { + proxy = new RemoteVar(observableId); + } + return proxy; + } + + private final void impactOnGet() { + Attribute[] attrs = new Attribute[1]; + try { + attrs[0] = new Attribute("get", get()); + } catch (Exception e) { + e.printStackTrace(); + } + sendEvent(attrs); + } } diff --git a/Dream2/src/main/java/javareact/examples/StartRegistry.java b/Dream2/src/main/java/javareact/examples/StartRegistry.java index 7d721f2..6d789ea 100755 --- a/Dream2/src/main/java/javareact/examples/StartRegistry.java +++ b/Dream2/src/main/java/javareact/examples/StartRegistry.java @@ -4,8 +4,8 @@ public class StartRegistry { - public static void main(String[] args) { - RegistryLauncher.start(); - } + public static void main(String[] args) { + RegistryLauncher.start(); + } } diff --git a/Dream2/src/main/java/javareact/examples/StartServer.java b/Dream2/src/main/java/javareact/examples/StartServer.java index f8d8f7a..faaf89a 100755 --- a/Dream2/src/main/java/javareact/examples/StartServer.java +++ b/Dream2/src/main/java/javareact/examples/StartServer.java @@ -4,8 +4,8 @@ public class StartServer { - public static void main(String[] args) { - ServerLauncher.start(); - } + public static void main(String[] args) { + ServerLauncher.start(); + } } diff --git a/Dream2/src/main/java/javareact/examples/StartTokenService.java b/Dream2/src/main/java/javareact/examples/StartTokenService.java index 4ae1135..d93fcab 100755 --- a/Dream2/src/main/java/javareact/examples/StartTokenService.java +++ b/Dream2/src/main/java/javareact/examples/StartTokenService.java @@ -7,27 +7,27 @@ import javareact.token_service.TokenServiceLauncher; public class StartTokenService { - private static final String addressDelim = "#"; + private static final String addressDelim = "#"; - public static void main(String[] args) { - if (args.length < 1) { - err(); - } - Collection addresses = getBrokerAddresses(args[0]); - TokenServiceLauncher.start(addresses); - } + public static void main(String[] args) { + if (args.length < 1) { + err(); + } + Collection addresses = getBrokerAddresses(args[0]); + TokenServiceLauncher.start(addresses); + } - private static final void err() { - System.out.println("Usage: StartTokenService [" + addressDelim + "[" + addressDelim + " getBrokerAddresses(String args) { - Collection result = new ArrayList(); - StringTokenizer tokenizer = new StringTokenizer(args, addressDelim); - while (tokenizer.hasMoreTokens()) { - result.add(tokenizer.nextToken()); - } - return result; - } + private static Collection getBrokerAddresses(String args) { + Collection result = new ArrayList(); + StringTokenizer tokenizer = new StringTokenizer(args, addressDelim); + while (tokenizer.hasMoreTokens()) { + result.add(tokenizer.nextToken()); + } + return result; + } } diff --git a/Dream2/src/main/java/javareact/examples/local/LocalExample.java b/Dream2/src/main/java/javareact/examples/local/LocalExample.java index 1a71858..68aebb0 100755 --- a/Dream2/src/main/java/javareact/examples/local/LocalExample.java +++ b/Dream2/src/main/java/javareact/examples/local/LocalExample.java @@ -6,57 +6,52 @@ public class LocalExample { - public static void main(String args[]) { - Var obInt = new Var<>("obInt", 1); - Var obDouble = new Var<>("obDouble", 1.0); - Var obBool = new Var<>("obBool", false); - Var obString1 = new Var<>("obString1", ""); - Var obString2 = new Var<>("obString2", ""); - - final RemoteVar obIntProxy = obInt.getProxy(); - final RemoteVar obDoubleProxy = obDouble.getProxy(); - final RemoteVar obBoolProxy = obBool.getProxy(); - final RemoteVar obString1Proxy = obString1.getProxy(); - final RemoteVar obString2Proxy = obString2.getProxy(); - - Signal reactInt = new Signal("reactInt", - () -> 10 - 2 + ((obIntProxy.get() * 2) + obIntProxy.get()) / 2, - obIntProxy); - - Signal reactDouble = new Signal("reactDouble", - () -> obDoubleProxy.get() + obDoubleProxy.get() * 2, - obDoubleProxy); - - Signal reactString = new Signal("reactString", - () -> obString1Proxy.get() + obString2Proxy.get(), - obString1Proxy, obString2Proxy); - - Signal reactBool = new Signal("reactBool", - () -> !obBoolProxy.get(), - obBoolProxy); - - try { - Thread.sleep(500); - } catch (InterruptedException e) { - e.printStackTrace(); - } - - obInt.set(100); - obDouble.set(1.6); - obBool.set(true); - obString1.set("Hello "); - obString2.set("World!"); - - try { - Thread.sleep(500); - } catch (InterruptedException e) { - e.printStackTrace(); - } - - System.out.println("reactInt: " + reactInt.get() + " (correct value: 158)"); - System.out.println("reactDouble: " + reactDouble.get() + " (correct value: 4.8)"); - System.out.println("reactBool: " + reactBool.get() + " (correct value: false)"); - System.out.println("reactString: " + reactString.get() + " (correct value: Hello World!)"); - - } + public static void main(String args[]) { + Var obInt = new Var<>("obInt", 1); + Var obDouble = new Var<>("obDouble", 1.0); + Var obBool = new Var<>("obBool", false); + Var obString1 = new Var<>("obString1", ""); + Var obString2 = new Var<>("obString2", ""); + + final RemoteVar obIntProxy = obInt.getProxy(); + final RemoteVar obDoubleProxy = obDouble.getProxy(); + final RemoteVar obBoolProxy = obBool.getProxy(); + final RemoteVar obString1Proxy = obString1.getProxy(); + final RemoteVar obString2Proxy = obString2.getProxy(); + + Signal reactInt = new Signal("reactInt", + () -> 10 - 2 + ((obIntProxy.get() * 2) + obIntProxy.get()) / 2, obIntProxy); + + Signal reactDouble = new Signal("reactDouble", () -> obDoubleProxy.get() + obDoubleProxy.get() + * 2, obDoubleProxy); + + Signal reactString = new Signal("reactString", () -> obString1Proxy.get() + + obString2Proxy.get(), obString1Proxy, obString2Proxy); + + Signal reactBool = new Signal("reactBool", () -> !obBoolProxy.get(), obBoolProxy); + + try { + Thread.sleep(500); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + obInt.set(100); + obDouble.set(1.6); + obBool.set(true); + obString1.set("Hello "); + obString2.set("World!"); + + try { + Thread.sleep(500); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + System.out.println("reactInt: " + reactInt.get() + " (correct value: 158)"); + System.out.println("reactDouble: " + reactDouble.get() + " (correct value: 4.8)"); + System.out.println("reactBool: " + reactBool.get() + " (correct value: false)"); + System.out.println("reactString: " + reactString.get() + " (correct value: Hello World!)"); + + } } diff --git a/Dream2/src/main/java/javareact/examples/local/LocalExample2.java b/Dream2/src/main/java/javareact/examples/local/LocalExample2.java index 1d6ca61..2d783cf 100755 --- a/Dream2/src/main/java/javareact/examples/local/LocalExample2.java +++ b/Dream2/src/main/java/javareact/examples/local/LocalExample2.java @@ -6,47 +6,43 @@ public class LocalExample2 { - public static void main(String args[]) { - LocalExample2 example = new LocalExample2(); - example.launch(); - } + public static void main(String args[]) { + LocalExample2 example = new LocalExample2(); + example.launch(); + } - public void launch() { - Var obDouble1 = new Var<>("obDouble1", 1.0); - Var obDouble2 = new Var<>("obDouble2", 1.0); + public void launch() { + Var obDouble1 = new Var<>("obDouble1", 1.0); + Var obDouble2 = new Var<>("obDouble2", 1.0); - final RemoteVar obDouble1Proxy = obDouble1.getProxy(); - final RemoteVar obDouble2Proxy = obDouble2.getProxy(); + final RemoteVar obDouble1Proxy = obDouble1.getProxy(); + final RemoteVar obDouble2Proxy = obDouble2.getProxy(); - Signal reactDouble1 = new Signal("reactDouble1", - () -> obDouble1Proxy.get() * obDouble2Proxy.get(), - obDouble1Proxy, obDouble2Proxy); + Signal reactDouble1 = new Signal("reactDouble1", () -> obDouble1Proxy.get() + * obDouble2Proxy.get(), obDouble1Proxy, obDouble2Proxy); - Signal reactDouble2 = new Signal("reactDouble2", - () -> obDouble1Proxy.get() / obDouble2Proxy.get(), - obDouble1Proxy, obDouble2Proxy); + Signal reactDouble2 = new Signal("reactDouble2", () -> obDouble1Proxy.get() + / obDouble2Proxy.get(), obDouble1Proxy, obDouble2Proxy); - final RemoteVar reactDouble1Proxy = reactDouble1.getProxy(); - final RemoteVar reactDouble2Proxy = reactDouble2.getProxy(); + final RemoteVar reactDouble1Proxy = reactDouble1.getProxy(); + final RemoteVar reactDouble2Proxy = reactDouble2.getProxy(); - new Signal("sub", - () -> reactDouble1Proxy.get() - reactDouble2Proxy.get(), - reactDouble1Proxy, reactDouble2Proxy).change().addHandler( - (oldValue, newValue) -> System.out.println(newValue)); + new Signal("sub", () -> reactDouble1Proxy.get() - reactDouble2Proxy.get(), reactDouble1Proxy, + reactDouble2Proxy).change().addHandler((oldValue, newValue) -> System.out.println(newValue)); - try { - Thread.sleep(500); - } catch (InterruptedException e) { - e.printStackTrace(); - } + try { + Thread.sleep(500); + } catch (InterruptedException e) { + e.printStackTrace(); + } - obDouble1.set(10.0); - obDouble2.set(20.0); + obDouble1.set(10.0); + obDouble2.set(20.0); - obDouble1.set(20.0); - obDouble2.set(30.0); + obDouble1.set(20.0); + obDouble2.set(30.0); - obDouble1.set(40.0); - obDouble2.set(50.0); - } + obDouble1.set(40.0); + obDouble2.set(50.0); + } } diff --git a/Dream2/src/main/java/javareact/examples/local/LocalExample3.java b/Dream2/src/main/java/javareact/examples/local/LocalExample3.java index 8cb9134..226c54d 100755 --- a/Dream2/src/main/java/javareact/examples/local/LocalExample3.java +++ b/Dream2/src/main/java/javareact/examples/local/LocalExample3.java @@ -9,22 +9,20 @@ public class LocalExample3 { - public static void main(String args[]) { - Var> obList = new Var<>("obList", new ArrayList()); - final RemoteVar> obListProxy = obList.getProxy(); + public static void main(String args[]) { + Var> obList = new Var<>("obList", new ArrayList()); + final RemoteVar> obListProxy = obList.getProxy(); - Signal reactInt = new Signal("reactInt", - () -> 1000 + obListProxy.get().size(), - obListProxy); + Signal reactInt = new Signal("reactInt", () -> 1000 + obListProxy.get().size(), obListProxy); - reactInt.change().addHandler((oldValue, newValue) -> System.out.println(newValue)); + reactInt.change().addHandler((oldValue, newValue) -> System.out.println(newValue)); - obList.modify(self -> self.add(10)); - obList.modify(self -> self.add(20)); - obList.modify(self -> self.add(30)); - obList.modify(self -> self.remove(1)); - obList.modify(self -> self.clear()); + obList.modify(self -> self.add(10)); + obList.modify(self -> self.add(20)); + obList.modify(self -> self.add(30)); + obList.modify(self -> self.remove(1)); + obList.modify(self -> self.clear()); - } + } } diff --git a/Dream2/src/main/java/javareact/examples/local/LocalExample4.java b/Dream2/src/main/java/javareact/examples/local/LocalExample4.java index e95d9a4..7250094 100755 --- a/Dream2/src/main/java/javareact/examples/local/LocalExample4.java +++ b/Dream2/src/main/java/javareact/examples/local/LocalExample4.java @@ -6,57 +6,52 @@ public class LocalExample4 { - public static void main(String args[]) { - Var obInt = new Var<>("obInt", 1); - Var obDouble = new Var<>("obDouble", 1.0); - Var obBool = new Var<>("obBool", false); - Var obString1 = new Var<>("obString1", ""); - Var obString2 = new Var<>("obString2", ""); - - final RemoteVar obIntProxy = obInt.getProxy(); - final RemoteVar obDoubleProxy = obDouble.getProxy(); - final RemoteVar obBoolProxy = obBool.getProxy(); - final RemoteVar obString1Proxy = obString1.getProxy(); - final RemoteVar obString2Proxy = obString2.getProxy(); - - Signal reactInt = new Signal("reactInt", - () -> 10 - 2 + ((obIntProxy.get() * 2) + obIntProxy.get()) / 2, - obIntProxy); - - Signal reactDouble = new Signal("reactDouble", - () -> obDoubleProxy.get() + obDoubleProxy.get() * 2, - obDoubleProxy); - - Signal reactString = new Signal("reactString", - () -> obString1Proxy.get() + obString2Proxy.get(), - obString1Proxy, obString2Proxy); - - Signal reactBool = new Signal("reactBool", - () -> !obBoolProxy.get(), - obBoolProxy); - - try { - Thread.sleep(500); - } catch (InterruptedException e) { - e.printStackTrace(); - } - - obInt.set(100); - obDouble.set(1.6); - obBool.set(true); - obString1.set("Hello "); - obString2.set("World!"); - - try { - Thread.sleep(500); - } catch (InterruptedException e) { - e.printStackTrace(); - } - - System.out.println("reactInt: " + reactInt.get() + " (correct value: 158)"); - System.out.println("reactDouble: " + reactDouble.get() + " (correct value: 4.8)"); - System.out.println("reactBool: " + reactBool.get() + " (correct value: false)"); - System.out.println("reactString: " + reactString.get() + " (correct value: Hello World!)"); - - } + public static void main(String args[]) { + Var obInt = new Var<>("obInt", 1); + Var obDouble = new Var<>("obDouble", 1.0); + Var obBool = new Var<>("obBool", false); + Var obString1 = new Var<>("obString1", ""); + Var obString2 = new Var<>("obString2", ""); + + final RemoteVar obIntProxy = obInt.getProxy(); + final RemoteVar obDoubleProxy = obDouble.getProxy(); + final RemoteVar obBoolProxy = obBool.getProxy(); + final RemoteVar obString1Proxy = obString1.getProxy(); + final RemoteVar obString2Proxy = obString2.getProxy(); + + Signal reactInt = new Signal("reactInt", + () -> 10 - 2 + ((obIntProxy.get() * 2) + obIntProxy.get()) / 2, obIntProxy); + + Signal reactDouble = new Signal("reactDouble", () -> obDoubleProxy.get() + obDoubleProxy.get() + * 2, obDoubleProxy); + + Signal reactString = new Signal("reactString", () -> obString1Proxy.get() + + obString2Proxy.get(), obString1Proxy, obString2Proxy); + + Signal reactBool = new Signal("reactBool", () -> !obBoolProxy.get(), obBoolProxy); + + try { + Thread.sleep(500); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + obInt.set(100); + obDouble.set(1.6); + obBool.set(true); + obString1.set("Hello "); + obString2.set("World!"); + + try { + Thread.sleep(500); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + System.out.println("reactInt: " + reactInt.get() + " (correct value: 158)"); + System.out.println("reactDouble: " + reactDouble.get() + " (correct value: 4.8)"); + System.out.println("reactBool: " + reactBool.get() + " (correct value: false)"); + System.out.println("reactString: " + reactString.get() + " (correct value: Hello World!)"); + + } } diff --git a/Dream2/src/main/java/javareact/examples/remote/RemoteObservable.java b/Dream2/src/main/java/javareact/examples/remote/RemoteObservable.java index 5da384d..80f87c0 100755 --- a/Dream2/src/main/java/javareact/examples/remote/RemoteObservable.java +++ b/Dream2/src/main/java/javareact/examples/remote/RemoteObservable.java @@ -10,26 +10,26 @@ public class RemoteObservable { - public static void main(String args[]) { - Consts.hostName = "Remote"; - ObservableInteger obInt = new ObservableInteger("obInt", 1); - ObservableString obString1 = new ObservableString("obString1", "a"); - ObservableString obString2 = new ObservableString("obString2", "b"); - ObservableList obList = new ObservableList("obList", new ArrayList()); - Random random = new Random(); + public static void main(String args[]) { + Consts.hostName = "Remote"; + ObservableInteger obInt = new ObservableInteger("obInt", 1); + ObservableString obString1 = new ObservableString("obString1", "a"); + ObservableString obString2 = new ObservableString("obString2", "b"); + ObservableList obList = new ObservableList("obList", new ArrayList()); + Random random = new Random(); - while (true) { - obInt.set(random.nextInt(1000)); - obString1.set(String.valueOf(random.nextInt(10)) + " "); - obString2.set(String.valueOf(random.nextInt(10)) + "!"); - obList.add(random.nextInt(1000)); - try { - Thread.sleep(1000); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } + while (true) { + obInt.set(random.nextInt(1000)); + obString1.set(String.valueOf(random.nextInt(10)) + " "); + obString2.set(String.valueOf(random.nextInt(10)) + "!"); + obList.add(random.nextInt(1000)); + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } - } + } } diff --git a/Dream2/src/main/java/javareact/examples/remote/RemoteReactive.java b/Dream2/src/main/java/javareact/examples/remote/RemoteReactive.java index 788408c..d6e811f 100755 --- a/Dream2/src/main/java/javareact/examples/remote/RemoteReactive.java +++ b/Dream2/src/main/java/javareact/examples/remote/RemoteReactive.java @@ -9,54 +9,54 @@ public class RemoteReactive { - public static void main(String args[]) { - Consts.hostName = "Reactive"; - - final IntegerProxy obIntProxy = new IntegerProxy("Remote", "obInt"); - final StringProxy obString1Proxy = new StringProxy("Remote", "obString1"); - final StringProxy obString2Proxy = new StringProxy("Remote", "obString2"); - final ListProxy obListProxy = new ListProxy("Remote", "obList"); - - ReactiveInteger react1 = new ReactiveInteger("react1", obIntProxy, obString1Proxy) { - @Override - public Integer evaluate() { - return obIntProxy.get() + obString1Proxy.get().length(); - } - }; - - ReactiveInteger react2 = new ReactiveInteger("react2", obIntProxy) { - @Override - public Integer evaluate() { - return obIntProxy.get(); - } - }; - - ReactiveString react3 = new ReactiveString("react3", obString1Proxy, obString2Proxy) { - @Override - public String evaluate() { - return obString1Proxy.get() + obString2Proxy.get(); - } - }; - - ReactiveInteger react4 = new ReactiveInteger("react4", obString1Proxy, obListProxy) { - @Override - public Integer evaluate() { - return obString1Proxy.length() + obListProxy.size(); - } - }; - - while (true) { - try { - Thread.sleep(1000); - } catch (InterruptedException e) { - e.printStackTrace(); - } - System.out.println("React1: " + react1.get()); - System.out.println("React2: " + react2.get()); - System.out.println("React3: " + react3.get()); - System.out.println("React4: " + react4.get()); - - } - - } + public static void main(String args[]) { + Consts.hostName = "Reactive"; + + final IntegerProxy obIntProxy = new IntegerProxy("Remote", "obInt"); + final StringProxy obString1Proxy = new StringProxy("Remote", "obString1"); + final StringProxy obString2Proxy = new StringProxy("Remote", "obString2"); + final ListProxy obListProxy = new ListProxy("Remote", "obList"); + + ReactiveInteger react1 = new ReactiveInteger("react1", obIntProxy, obString1Proxy) { + @Override + public Integer evaluate() { + return obIntProxy.get() + obString1Proxy.get().length(); + } + }; + + ReactiveInteger react2 = new ReactiveInteger("react2", obIntProxy) { + @Override + public Integer evaluate() { + return obIntProxy.get(); + } + }; + + ReactiveString react3 = new ReactiveString("react3", obString1Proxy, obString2Proxy) { + @Override + public String evaluate() { + return obString1Proxy.get() + obString2Proxy.get(); + } + }; + + ReactiveInteger react4 = new ReactiveInteger("react4", obString1Proxy, obListProxy) { + @Override + public Integer evaluate() { + return obString1Proxy.length() + obListProxy.size(); + } + }; + + while (true) { + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + System.out.println("React1: " + react1.get()); + System.out.println("React2: " + react2.get()); + System.out.println("React3: " + react3.get()); + System.out.println("React4: " + react4.get()); + + } + + } } diff --git a/Dream2/src/main/java/javareact/registry/Registry.java b/Dream2/src/main/java/javareact/registry/Registry.java index cae7fd8..7c897e4 100755 --- a/Dream2/src/main/java/javareact/registry/Registry.java +++ b/Dream2/src/main/java/javareact/registry/Registry.java @@ -16,78 +16,80 @@ import polimi.reds.broker.routing.PacketForwarder; public class Registry implements PacketForwarder { - // HostId -> ObservableId -> List of Events - private final Map>> lastEvents = new HashMap>>(); - private final Logger logger = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME); + // HostId -> ObservableId -> List of Events + private final Map>> lastEvents = new HashMap>>(); + private final Logger logger = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME); - @Override - public Collection forwardPacket(String subject, NodeDescriptor sender, Serializable packet, Collection neighbors, Outbox outbox) { - if (subject.equals(EventPacket.subject)) { - assert (packet instanceof EventPacket); - EventPacket evPkt = (EventPacket) packet; - logger.finer("Received an event packet " + evPkt); - processEvent(evPkt); - } else if (subject.equals(SubscriptionPacket.subject)) { - assert (packet instanceof SubscriptionPacket); - SubscriptionPacket subPkt = (SubscriptionPacket) packet; - logger.fine("Received a subscription packet " + subPkt); - EventPacket ev = getInformationFor(subPkt.getSubscription()); - if (ev != null) { - logger.fine("Found information for packet " + subPkt + ". Replying with " + ev); - Collection replyTo = new ArrayList(); - replyTo.add(sender); - outbox.add(EventPacket.subject, ev, replyTo); - } - } else { - assert false : subject; - } - return new ArrayList(); - } + @Override + public Collection forwardPacket(String subject, NodeDescriptor sender, Serializable packet, + Collection neighbors, Outbox outbox) { + if (subject.equals(EventPacket.subject)) { + assert (packet instanceof EventPacket); + EventPacket evPkt = (EventPacket) packet; + logger.finer("Received an event packet " + evPkt); + processEvent(evPkt); + } else if (subject.equals(SubscriptionPacket.subject)) { + assert (packet instanceof SubscriptionPacket); + SubscriptionPacket subPkt = (SubscriptionPacket) packet; + logger.fine("Received a subscription packet " + subPkt); + EventPacket ev = getInformationFor(subPkt.getSubscription()); + if (ev != null) { + logger.fine("Found information for packet " + subPkt + ". Replying with " + ev); + Collection replyTo = new ArrayList(); + replyTo.add(sender); + outbox.add(EventPacket.subject, ev, replyTo); + } + } else { + assert false : subject; + } + return new ArrayList(); + } - /** - * Stores the given event and deletes events that it overrides. - */ - private final void processEvent(EventPacket ev) { - String hostId = ev.getEvent().getHostId(); - String observableId = ev.getEvent().getObservableId(); - Map> observableIdMap = lastEvents.get(hostId); - if (observableIdMap == null) { - observableIdMap = new HashMap>(); - lastEvents.put(hostId, observableIdMap); - } - Collection events = observableIdMap.get(observableId); - if (events == null) { - events = new ArrayList(); - observableIdMap.put(observableId, events); - } - Iterator eventsIt = events.iterator(); - while (eventsIt.hasNext()) { - EventPacket storedEvent = eventsIt.next(); - if (storedEvent.getEvent().containsTheSameInformationAs(ev.getEvent())) { - eventsIt.remove(); - } - } - events.add(ev); - } + /** + * Stores the given event and deletes events that it overrides. + */ + private final void processEvent(EventPacket ev) { + String hostId = ev.getEvent().getHostId(); + String observableId = ev.getEvent().getObservableId(); + Map> observableIdMap = lastEvents.get(hostId); + if (observableIdMap == null) { + observableIdMap = new HashMap>(); + lastEvents.put(hostId, observableIdMap); + } + Collection events = observableIdMap.get(observableId); + if (events == null) { + events = new ArrayList(); + observableIdMap.put(observableId, events); + } + Iterator eventsIt = events.iterator(); + while (eventsIt.hasNext()) { + EventPacket storedEvent = eventsIt.next(); + if (storedEvent.getEvent().containsTheSameInformationAs(ev.getEvent())) { + eventsIt.remove(); + } + } + events.add(ev); + } - /** - * Returns the last event storing the information required by sub, if any, and null otherwise. - */ - private final EventPacket getInformationFor(Subscription sub) { - Map> observableIdMap = lastEvents.get(sub.getHostId()); - if (observableIdMap == null) { - return null; - } - Collection events = observableIdMap.get(sub.getObservableId()); - if (events == null) { - return null; - } - for (EventPacket ev : events) { - if (sub.isSatisfiedBy(ev.getEvent())) { - return ev; - } - } - return null; - } + /** + * Returns the last event storing the information required by sub, if any, + * and null otherwise. + */ + private final EventPacket getInformationFor(Subscription sub) { + Map> observableIdMap = lastEvents.get(sub.getHostId()); + if (observableIdMap == null) { + return null; + } + Collection events = observableIdMap.get(sub.getObservableId()); + if (events == null) { + return null; + } + for (EventPacket ev : events) { + if (sub.isSatisfiedBy(ev.getEvent())) { + return ev; + } + } + return null; + } } diff --git a/Dream2/src/main/java/javareact/registry/RegistryLauncher.java b/Dream2/src/main/java/javareact/registry/RegistryLauncher.java index 1b8c34d..74de020 100755 --- a/Dream2/src/main/java/javareact/registry/RegistryLauncher.java +++ b/Dream2/src/main/java/javareact/registry/RegistryLauncher.java @@ -21,58 +21,59 @@ import polimi.reds.broker.routing.GenericRouter; public class RegistryLauncher { - private static RegistryLauncher launcher; + private static RegistryLauncher launcher; - private final Overlay overlay; - private final Logger logger = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME); + private final Overlay overlay; + private final Logger logger = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME); - private RegistryLauncher() { - Transport tr = null; - try { - tr = new TCPTransport(); - } catch (IOException e) { - e.printStackTrace(); - } - TopologyManager tm = new SimpleTopologyManager(); - overlay = new GenericOverlay(tm, tr); - GenericRouter router = new GenericRouter(overlay); - Registry registry = new Registry(); - router.setPacketForwarder(EventPacket.subject, registry); - router.setPacketForwarder(SubscriptionPacket.subject, registry); - } + private RegistryLauncher() { + Transport tr = null; + try { + tr = new TCPTransport(); + } catch (IOException e) { + e.printStackTrace(); + } + TopologyManager tm = new SimpleTopologyManager(); + overlay = new GenericOverlay(tm, tr); + GenericRouter router = new GenericRouter(overlay); + Registry registry = new Registry(); + router.setPacketForwarder(EventPacket.subject, registry); + router.setPacketForwarder(SubscriptionPacket.subject, registry); + } - public static final void start() { - if (launcher == null) { - launcher = new RegistryLauncher(); - } - launcher.logger.fine("Starting registry"); - launcher.overlay.start(); - try { - launcher.overlay.addNeighbor(Consts.serverAddr); - } catch (ConnectException | MalformedURLException | NotRunningException e) { - e.printStackTrace(); - } - for (NodeDescriptor node : launcher.overlay.getNeighbors()) { - try { - launcher.overlay.send(RegistryAdvertisePacket.subject, new RegistryAdvertisePacket(AdvType.ADV), node); - } catch (IOException | NotRunningException e) { - e.printStackTrace(); - } - } - } + public static final void start() { + if (launcher == null) { + launcher = new RegistryLauncher(); + } + launcher.logger.fine("Starting registry"); + launcher.overlay.start(); + try { + launcher.overlay.addNeighbor(Consts.serverAddr); + } catch (ConnectException | MalformedURLException | NotRunningException e) { + e.printStackTrace(); + } + for (NodeDescriptor node : launcher.overlay.getNeighbors()) { + try { + launcher.overlay.send(RegistryAdvertisePacket.subject, new RegistryAdvertisePacket(AdvType.ADV), node); + } catch (IOException | NotRunningException e) { + e.printStackTrace(); + } + } + } - public static final void stop() { - if (launcher != null) { - for (NodeDescriptor node : launcher.overlay.getNeighbors()) { - try { - launcher.overlay.send(RegistryAdvertisePacket.subject, new RegistryAdvertisePacket(AdvType.UNADV), node); - } catch (IOException | NotRunningException e) { - e.printStackTrace(); - } - } - launcher.logger.fine("Stopping registry"); - launcher.overlay.stop(); - } - } + public static final void stop() { + if (launcher != null) { + for (NodeDescriptor node : launcher.overlay.getNeighbors()) { + try { + launcher.overlay.send(RegistryAdvertisePacket.subject, new RegistryAdvertisePacket(AdvType.UNADV), + node); + } catch (IOException | NotRunningException e) { + e.printStackTrace(); + } + } + launcher.logger.fine("Stopping registry"); + launcher.overlay.stop(); + } + } } diff --git a/Dream2/src/main/java/javareact/server/AdvertisementTable.java b/Dream2/src/main/java/javareact/server/AdvertisementTable.java index 7ce1f4c..9b121ea 100755 --- a/Dream2/src/main/java/javareact/server/AdvertisementTable.java +++ b/Dream2/src/main/java/javareact/server/AdvertisementTable.java @@ -12,41 +12,42 @@ import polimi.reds.NodeDescriptor; final class AdvertisementTable { - private final Map> advs = new HashMap>(); - - final void addAdvertisement(NodeDescriptor node, Advertisement adv) { - Collection advsList = advs.get(node); - if (advsList == null) { - advsList = new ArrayList(); - advs.put(node, advsList); - } - advsList.add(adv); - } - - final void removeAdvertisement(NodeDescriptor node, Advertisement adv) { - Collection advsList = advs.get(node); - if (advsList == null) return; - advsList.remove(adv); - if (advsList.isEmpty()) { - advs.remove(node); - } - } - - final Set getMatchingNodes(Subscription sub) { - Set nodes = new HashSet(); - nodesLoop: for (NodeDescriptor node : advs.keySet()) { - for (Advertisement adv : advs.get(node)) { - if (adv.isSatisfiedBy(sub)) { - nodes.add(node); - continue nodesLoop; - } - } - } - return nodes; - } - - final void removeAllAdvertisementsFor(NodeDescriptor node) { - advs.remove(node); - } + private final Map> advs = new HashMap>(); + + final void addAdvertisement(NodeDescriptor node, Advertisement adv) { + Collection advsList = advs.get(node); + if (advsList == null) { + advsList = new ArrayList(); + advs.put(node, advsList); + } + advsList.add(adv); + } + + final void removeAdvertisement(NodeDescriptor node, Advertisement adv) { + Collection advsList = advs.get(node); + if (advsList == null) + return; + advsList.remove(adv); + if (advsList.isEmpty()) { + advs.remove(node); + } + } + + final Set getMatchingNodes(Subscription sub) { + Set nodes = new HashSet(); + nodesLoop: for (NodeDescriptor node : advs.keySet()) { + for (Advertisement adv : advs.get(node)) { + if (adv.isSatisfiedBy(sub)) { + nodes.add(node); + continue nodesLoop; + } + } + } + return nodes; + } + + final void removeAllAdvertisementsFor(NodeDescriptor node) { + advs.remove(node); + } } diff --git a/Dream2/src/main/java/javareact/server/DependencyDetector.java b/Dream2/src/main/java/javareact/server/DependencyDetector.java index ace5f30..87f5fc4 100755 --- a/Dream2/src/main/java/javareact/server/DependencyDetector.java +++ b/Dream2/src/main/java/javareact/server/DependencyDetector.java @@ -15,225 +15,228 @@ import javareact.common.packets.content.Subscription; final class DependencyDetector { - private final Map> dependencyGraph = new HashMap>(); - - // Stores the dependencies for computing expressions - // Expression (A) -> Changed expression (B) that led to the re-computation of (A) -> Wait recommendations - private final Map>> recommendations = new HashMap>>(); - - final Set getWaitRecommendations(Event event, Set computedFrom) { - String eventSignature = event.getSignature(); - Map> innerMap = recommendations.get(eventSignature); - Set result = new HashSet(); - if (innerMap == null) { - return result; - } - for (String exp : innerMap.keySet()) { - if (computedFrom.contains(exp)) { - result.addAll(innerMap.get(exp)); - } - } - return result; - } - - final void processAdvertisementPacket(AdvertisementPacket advPkt) { - switch (advPkt.getAdvType()) { - case ADV: - processAdv(advPkt); - break; - case UNADV: - processUnadv(advPkt); - break; - default: - assert false : advPkt.getAdvType(); - } - } - - final void consolidate() { - recommendations.clear(); - computeRecommendations(); - } - - private final void processAdv(AdvertisementPacket advPkt) { - if (!advPkt.containtsSubscriptions()) return; - for (Subscription sub : advPkt.getSubscriptions()) { - String advSignature = advPkt.getAdvertisement().getSignature(); - String subSignature = sub.getSignature(); - Collection subSignatures = dependencyGraph.get(advSignature); - if (subSignatures == null) { - subSignatures = new HashSet(); - dependencyGraph.put(advSignature, subSignatures); - } - subSignatures.add(subSignature); - } - } - - private final void processUnadv(AdvertisementPacket advPkt) { - // TODO: manage unadvertisements - } - - private final void computeRecommendations() { - for (String expression : dependencyGraph.keySet()) { - Collection dependingFrom = dependencyGraph.get(expression); - if (dependingFrom.size() > 1) { - List paths = generatePathsFor(expression); - computeRecommendationsFromPaths(expression, paths); - } - } - } - - private final List generatePathsFor(String expression) { - List results = new ArrayList(); - generatePathsFor(expression, new Path(), results); - Collections.sort(results); - return results; - } - - private final void generatePathsFor(String expression, Path path, List paths) { - path.addFirst(expression); - if (!dependencyGraph.containsKey(expression)) { - paths.add(path); - } else { - for (String depNode : dependencyGraph.get(expression)) { - generatePathsFor(depNode, new Path(path), paths); - } - } - } - - private final void computeRecommendationsFromPaths(String expression, List paths) { - Path firstPath = paths.get(0); - Path prefix = firstPath; - List consideredPaths = new ArrayList(); - consideredPaths.add(firstPath); - for (int i = 1; i < paths.size(); i++) { - Path path = paths.get(i); - Path newPrefix = prefix.getCommonPrefix(path); - // The path is the new element of a new dependency - if (newPrefix.isEmpty()) { - // Save the computed dependency - storeRecommendationsFromPath(prefix, consideredPaths); - // Initialize variables for new iteration (if any) - consideredPaths.clear(); - consideredPaths.add(path); - prefix = path; - } else { - // Initialize variables for new iteration (if any) - prefix = newPrefix; - consideredPaths.add(path); - // If it is the last iteration, then save the computed dependency - if (i == paths.size() - 1) { - storeRecommendationsFromPath(newPrefix, consideredPaths); - } - } - } - } - - private final void storeRecommendationsFromPath(Path prefix, List consideredPaths) { - String lastCommonExpression = prefix.getLastExpression(); - WaitRecommendations waitRecommendations = getRecommendationsFor(consideredPaths); - Set recommendationsSet = waitRecommendations.getRecommendations(); - for (String expressionToWaitFor : recommendationsSet) { - WaitRecommendations recommendationsToStore = waitRecommendations.dup(); - recommendationsToStore.removeExpressionToWaitFor(expressionToWaitFor); - Map> innerMap = recommendations.get(expressionToWaitFor); - if (innerMap == null) { - innerMap = new HashMap>(); - recommendations.put(expressionToWaitFor, innerMap); - } - Set waitSet = innerMap.get(lastCommonExpression); - if (waitSet == null) { - waitSet = new HashSet(); - innerMap.put(lastCommonExpression, waitSet); - } - waitSet.add(recommendationsToStore); - } - } - - private final WaitRecommendations getRecommendationsFor(List paths) { - WaitRecommendations result = null; - for (Path path : paths) { - if (result == null) { - String lastExpression = path.getLastExpression(); - result = new WaitRecommendations(lastExpression); - } - String waitForExpression = path.getSecondLastExpression(); - result.addRecommendation(waitForExpression); - } - return result; - } - - /** - * Represents a path in the expression graph as a list of expressions. - */ - private class Path implements Comparable { - private final LinkedList expressions; - - Path() { - expressions = new LinkedList(); - } - - Path(Path path) { - expressions = new LinkedList(path.expressions); - } - - final void addFirst(String expression) { - expressions.addFirst(expression); - } - - private final void addLast(String expression) { - expressions.addLast(expression); - } - - final Path getCommonPrefix(Path other) { - Path prefix = new Path(); - for (int i = 0; i < expressions.size(); i++) { - if (other.expressions.size() <= i) { - break; - } - String myString = expressions.get(i); - String otherString = other.expressions.get(i); - if (myString.equals(otherString)) { - prefix.addLast(myString); - } else { - break; - } - } - return prefix; - } - - final boolean isEmpty() { - return expressions.isEmpty(); - } - - final String getLastExpression() { - return expressions.getLast(); - } - - final String getSecondLastExpression() { - return expressions.get(expressions.size() - 2); - } - - @Override - public final int compareTo(Path other) { - for (int i = 0; i < expressions.size(); i++) { - if (other.expressions.size() <= i) { - return 1; - } - String myString = expressions.get(i); - String otherString = other.expressions.get(i); - int compareStrings = myString.compareTo(otherString); - if (compareStrings != 0) { - return compareStrings; - } - } - return 0; - } - - @Override - public final String toString() { - return expressions.toString(); - } - - } + private final Map> dependencyGraph = new HashMap>(); + + // Stores the dependencies for computing expressions + // Expression (A) -> Changed expression (B) that led to the re-computation + // of (A) -> Wait recommendations + private final Map>> recommendations = new HashMap>>(); + + final Set getWaitRecommendations(Event event, Set computedFrom) { + String eventSignature = event.getSignature(); + Map> innerMap = recommendations.get(eventSignature); + Set result = new HashSet(); + if (innerMap == null) { + return result; + } + for (String exp : innerMap.keySet()) { + if (computedFrom.contains(exp)) { + result.addAll(innerMap.get(exp)); + } + } + return result; + } + + final void processAdvertisementPacket(AdvertisementPacket advPkt) { + switch (advPkt.getAdvType()) { + case ADV: + processAdv(advPkt); + break; + case UNADV: + processUnadv(advPkt); + break; + default: + assert false : advPkt.getAdvType(); + } + } + + final void consolidate() { + recommendations.clear(); + computeRecommendations(); + } + + private final void processAdv(AdvertisementPacket advPkt) { + if (!advPkt.containtsSubscriptions()) + return; + for (Subscription sub : advPkt.getSubscriptions()) { + String advSignature = advPkt.getAdvertisement().getSignature(); + String subSignature = sub.getSignature(); + Collection subSignatures = dependencyGraph.get(advSignature); + if (subSignatures == null) { + subSignatures = new HashSet(); + dependencyGraph.put(advSignature, subSignatures); + } + subSignatures.add(subSignature); + } + } + + private final void processUnadv(AdvertisementPacket advPkt) { + // TODO: manage unadvertisements + } + + private final void computeRecommendations() { + for (String expression : dependencyGraph.keySet()) { + Collection dependingFrom = dependencyGraph.get(expression); + if (dependingFrom.size() > 1) { + List paths = generatePathsFor(expression); + computeRecommendationsFromPaths(expression, paths); + } + } + } + + private final List generatePathsFor(String expression) { + List results = new ArrayList(); + generatePathsFor(expression, new Path(), results); + Collections.sort(results); + return results; + } + + private final void generatePathsFor(String expression, Path path, List paths) { + path.addFirst(expression); + if (!dependencyGraph.containsKey(expression)) { + paths.add(path); + } else { + for (String depNode : dependencyGraph.get(expression)) { + generatePathsFor(depNode, new Path(path), paths); + } + } + } + + private final void computeRecommendationsFromPaths(String expression, List paths) { + Path firstPath = paths.get(0); + Path prefix = firstPath; + List consideredPaths = new ArrayList(); + consideredPaths.add(firstPath); + for (int i = 1; i < paths.size(); i++) { + Path path = paths.get(i); + Path newPrefix = prefix.getCommonPrefix(path); + // The path is the new element of a new dependency + if (newPrefix.isEmpty()) { + // Save the computed dependency + storeRecommendationsFromPath(prefix, consideredPaths); + // Initialize variables for new iteration (if any) + consideredPaths.clear(); + consideredPaths.add(path); + prefix = path; + } else { + // Initialize variables for new iteration (if any) + prefix = newPrefix; + consideredPaths.add(path); + // If it is the last iteration, then save the computed + // dependency + if (i == paths.size() - 1) { + storeRecommendationsFromPath(newPrefix, consideredPaths); + } + } + } + } + + private final void storeRecommendationsFromPath(Path prefix, List consideredPaths) { + String lastCommonExpression = prefix.getLastExpression(); + WaitRecommendations waitRecommendations = getRecommendationsFor(consideredPaths); + Set recommendationsSet = waitRecommendations.getRecommendations(); + for (String expressionToWaitFor : recommendationsSet) { + WaitRecommendations recommendationsToStore = waitRecommendations.dup(); + recommendationsToStore.removeExpressionToWaitFor(expressionToWaitFor); + Map> innerMap = recommendations.get(expressionToWaitFor); + if (innerMap == null) { + innerMap = new HashMap>(); + recommendations.put(expressionToWaitFor, innerMap); + } + Set waitSet = innerMap.get(lastCommonExpression); + if (waitSet == null) { + waitSet = new HashSet(); + innerMap.put(lastCommonExpression, waitSet); + } + waitSet.add(recommendationsToStore); + } + } + + private final WaitRecommendations getRecommendationsFor(List paths) { + WaitRecommendations result = null; + for (Path path : paths) { + if (result == null) { + String lastExpression = path.getLastExpression(); + result = new WaitRecommendations(lastExpression); + } + String waitForExpression = path.getSecondLastExpression(); + result.addRecommendation(waitForExpression); + } + return result; + } + + /** + * Represents a path in the expression graph as a list of expressions. + */ + private class Path implements Comparable { + private final LinkedList expressions; + + Path() { + expressions = new LinkedList(); + } + + Path(Path path) { + expressions = new LinkedList(path.expressions); + } + + final void addFirst(String expression) { + expressions.addFirst(expression); + } + + private final void addLast(String expression) { + expressions.addLast(expression); + } + + final Path getCommonPrefix(Path other) { + Path prefix = new Path(); + for (int i = 0; i < expressions.size(); i++) { + if (other.expressions.size() <= i) { + break; + } + String myString = expressions.get(i); + String otherString = other.expressions.get(i); + if (myString.equals(otherString)) { + prefix.addLast(myString); + } else { + break; + } + } + return prefix; + } + + final boolean isEmpty() { + return expressions.isEmpty(); + } + + final String getLastExpression() { + return expressions.getLast(); + } + + final String getSecondLastExpression() { + return expressions.get(expressions.size() - 2); + } + + @Override + public final int compareTo(Path other) { + for (int i = 0; i < expressions.size(); i++) { + if (other.expressions.size() <= i) { + return 1; + } + String myString = expressions.get(i); + String otherString = other.expressions.get(i); + int compareStrings = myString.compareTo(otherString); + if (compareStrings != 0) { + return compareStrings; + } + } + return 0; + } + + @Override + public final String toString() { + return expressions.toString(); + } + + } } diff --git a/Dream2/src/main/java/javareact/server/ServerEventForwarder.java b/Dream2/src/main/java/javareact/server/ServerEventForwarder.java index 6254df7..5adfa4b 100755 --- a/Dream2/src/main/java/javareact/server/ServerEventForwarder.java +++ b/Dream2/src/main/java/javareact/server/ServerEventForwarder.java @@ -22,236 +22,249 @@ import polimi.reds.broker.routing.PacketForwarder; public class ServerEventForwarder implements PacketForwarder, NeighborhoodChangeListener { - protected final Logger logger = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME); + protected final Logger logger = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME); - protected final SubscriptionTable clientsSubTable = new SubscriptionTable(); - protected final SubscriptionTable brokersSubTable = new SubscriptionTable(); - protected final AdvertisementTable advTable = new AdvertisementTable(); - protected final DependencyDetector dependencyDetector = new DependencyDetector(); + protected final SubscriptionTable clientsSubTable = new SubscriptionTable(); + protected final SubscriptionTable brokersSubTable = new SubscriptionTable(); + protected final AdvertisementTable advTable = new AdvertisementTable(); + protected final DependencyDetector dependencyDetector = new DependencyDetector(); - protected NodeDescriptor registry = null; - protected NodeDescriptor tokenService = null; + protected NodeDescriptor registry = null; + protected NodeDescriptor tokenService = null; - @Override - public Collection forwardPacket(String subject, NodeDescriptor sender, Serializable packet, Collection neighbors, Outbox outbox) { - if (subject.equals(SubscriptionPacket.subject)) { - assert (packet instanceof SubscriptionPacket); - SubscriptionPacket subPkt = (SubscriptionPacket) packet; - logger.fine("Received a subscription packet: " + subPkt); - processSubscription(sender, subPkt, neighbors, outbox); - } else if (subject.equals(EventPacket.subject)) { - assert (packet instanceof EventPacket); - EventPacket evPkt = (EventPacket) packet; - logger.finer("Received an event packet: " + evPkt); - processEvent(sender, evPkt, neighbors, outbox); - } else if (subject.equals(AdvertisementPacket.subject)) { - assert (packet instanceof AdvertisementPacket); - AdvertisementPacket advPkt = (AdvertisementPacket) packet; - logger.fine("Received an advertisement packet: " + advPkt); - processAdvertisement(sender, advPkt, neighbors, outbox); - } else if (subject.equals(RegistryAdvertisePacket.subject)) { - assert (packet instanceof RegistryAdvertisePacket); - RegistryAdvertisePacket regAdvPkt = (RegistryAdvertisePacket) packet; - logger.fine("Received a registry advertise packet: " + regAdvPkt); - processRegistryAdvertise(sender, regAdvPkt); - } else if (subject.equals(TokenServiceAdvertisePacket.subject)) { - assert (packet instanceof TokenServiceAdvertisePacket); - TokenServiceAdvertisePacket tokenServiceAdvPkt = (TokenServiceAdvertisePacket) packet; - logger.fine("Received a token service advertise packet: " + tokenServiceAdvPkt); - processTokenServiceAdvertise(sender, tokenServiceAdvPkt); - } else { - assert false; - logger.warning("Received an unknown event subject"); - } - return new ArrayList(); - } + @Override + public Collection forwardPacket(String subject, NodeDescriptor sender, Serializable packet, + Collection neighbors, Outbox outbox) { + if (subject.equals(SubscriptionPacket.subject)) { + assert (packet instanceof SubscriptionPacket); + SubscriptionPacket subPkt = (SubscriptionPacket) packet; + logger.fine("Received a subscription packet: " + subPkt); + processSubscription(sender, subPkt, neighbors, outbox); + } else if (subject.equals(EventPacket.subject)) { + assert (packet instanceof EventPacket); + EventPacket evPkt = (EventPacket) packet; + logger.finer("Received an event packet: " + evPkt); + processEvent(sender, evPkt, neighbors, outbox); + } else if (subject.equals(AdvertisementPacket.subject)) { + assert (packet instanceof AdvertisementPacket); + AdvertisementPacket advPkt = (AdvertisementPacket) packet; + logger.fine("Received an advertisement packet: " + advPkt); + processAdvertisement(sender, advPkt, neighbors, outbox); + } else if (subject.equals(RegistryAdvertisePacket.subject)) { + assert (packet instanceof RegistryAdvertisePacket); + RegistryAdvertisePacket regAdvPkt = (RegistryAdvertisePacket) packet; + logger.fine("Received a registry advertise packet: " + regAdvPkt); + processRegistryAdvertise(sender, regAdvPkt); + } else if (subject.equals(TokenServiceAdvertisePacket.subject)) { + assert (packet instanceof TokenServiceAdvertisePacket); + TokenServiceAdvertisePacket tokenServiceAdvPkt = (TokenServiceAdvertisePacket) packet; + logger.fine("Received a token service advertise packet: " + tokenServiceAdvPkt); + processTokenServiceAdvertise(sender, tokenServiceAdvPkt); + } else { + assert false; + logger.warning("Received an unknown event subject"); + } + return new ArrayList(); + } - private final void processSubscription(NodeDescriptor sender, SubscriptionPacket packet, Collection neighbors, Outbox box) { - updateSubscriptionTables(sender, packet); - Set mathingNodes = advTable.getMatchingNodes(packet.getSubscription()); - if (!mathingNodes.isEmpty()) { - sendTo(SubscriptionPacket.subject, packet, box, mathingNodes); - } - sendSubscriptionToRegistryIfNeeded(packet, sender, neighbors, box); - } + private final void processSubscription(NodeDescriptor sender, SubscriptionPacket packet, + Collection neighbors, Outbox box) { + updateSubscriptionTables(sender, packet); + Set mathingNodes = advTable.getMatchingNodes(packet.getSubscription()); + if (!mathingNodes.isEmpty()) { + sendTo(SubscriptionPacket.subject, packet, box, mathingNodes); + } + sendSubscriptionToRegistryIfNeeded(packet, sender, neighbors, box); + } - private final void updateSubscriptionTables(NodeDescriptor sender, SubscriptionPacket subPkt) { - SubscriptionTable table = sender.isClient() ? clientsSubTable : brokersSubTable; - switch (subPkt.getSubType()) { - case SUB: - table.addSubscription(sender, subPkt.getSubscription()); - break; - case UNSUB: - table.removeSubscription(sender, subPkt.getSubscription()); - break; - default: - assert false : subPkt.getSubType(); - } - } + private final void updateSubscriptionTables(NodeDescriptor sender, SubscriptionPacket subPkt) { + SubscriptionTable table = sender.isClient() ? clientsSubTable : brokersSubTable; + switch (subPkt.getSubType()) { + case SUB: + table.addSubscription(sender, subPkt.getSubscription()); + break; + case UNSUB: + table.removeSubscription(sender, subPkt.getSubscription()); + break; + default: + assert false : subPkt.getSubType(); + } + } - private void processEvent(NodeDescriptor sender, EventPacket packet, Collection neighbors, Outbox outbox) { - // In case of atomic consistency, each initial event must be first delivered to the token service - if (Consts.consistencyType == ConsistencyType.ATOMIC && !packet.isApprovedByTokenService()) { - sendToTokenService(EventPacket.subject, packet, outbox); - } else { - if ((sender.isClient() || sender.equals(tokenService)) && (Consts.consistencyType == ConsistencyType.GLITCH_FREE || Consts.consistencyType == ConsistencyType.ATOMIC)) { - Set waitRecommendations = dependencyDetector.getWaitRecommendations(packet.getEvent(), packet.getComputedFrom()); - sendEvent(packet, waitRecommendations, outbox); - } else { - sendEvent(packet, outbox); - } - } - } + private void processEvent(NodeDescriptor sender, EventPacket packet, Collection neighbors, + Outbox outbox) { + // In case of atomic consistency, each initial event must be first + // delivered to the token service + if (Consts.consistencyType == ConsistencyType.ATOMIC && !packet.isApprovedByTokenService()) { + sendToTokenService(EventPacket.subject, packet, outbox); + } else { + if ((sender.isClient() || sender.equals(tokenService)) + && (Consts.consistencyType == ConsistencyType.GLITCH_FREE || Consts.consistencyType == ConsistencyType.ATOMIC)) { + Set waitRecommendations = dependencyDetector.getWaitRecommendations( + packet.getEvent(), packet.getComputedFrom()); + sendEvent(packet, waitRecommendations, outbox); + } else { + sendEvent(packet, outbox); + } + } + } - private final void sendEvent(EventPacket pkt, Outbox outbox) { - sendEvent(pkt, new HashSet(), outbox); - } + private final void sendEvent(EventPacket pkt, Outbox outbox) { + sendEvent(pkt, new HashSet(), outbox); + } - private final void sendEvent(EventPacket pkt, Set waitRecommendations, Outbox outbox) { - for (WaitRecommendations wr : waitRecommendations) { - pkt.addWaitRecommendations(wr); - } - sendEventToRegistryIfNeeded(pkt, outbox); - Map matchingClients = clientsSubTable.getMatchingNodes(pkt.getEvent()); - Map matchingBrokers = brokersSubTable.getMatchingNodes(pkt.getEvent()); - sendTo(EventPacket.subject, pkt, outbox, matchingClients.keySet()); - sendTo(EventPacket.subject, pkt, outbox, matchingBrokers.keySet()); - sendTokenAckIfNeeded(pkt, matchingClients, outbox); - } + private final void sendEvent(EventPacket pkt, Set waitRecommendations, Outbox outbox) { + for (WaitRecommendations wr : waitRecommendations) { + pkt.addWaitRecommendations(wr); + } + sendEventToRegistryIfNeeded(pkt, outbox); + Map matchingClients = clientsSubTable.getMatchingNodes(pkt.getEvent()); + Map matchingBrokers = brokersSubTable.getMatchingNodes(pkt.getEvent()); + sendTo(EventPacket.subject, pkt, outbox, matchingClients.keySet()); + sendTo(EventPacket.subject, pkt, outbox, matchingBrokers.keySet()); + sendTokenAckIfNeeded(pkt, matchingClients, outbox); + } - private final void sendTokenAckIfNeeded(EventPacket pkt, Map clients, Outbox outbox) { - if (Consts.consistencyType == ConsistencyType.ATOMIC && pkt.isFinal()) { - TokenAckPacket tokenAck = new TokenAckPacket(pkt.getEvent().getSignature(), getSubscribersCount(clients)); - sendToTokenService(TokenAckPacket.subject, tokenAck, outbox); - } - } + private final void sendTokenAckIfNeeded(EventPacket pkt, Map clients, Outbox outbox) { + if (Consts.consistencyType == ConsistencyType.ATOMIC && pkt.isFinal()) { + TokenAckPacket tokenAck = new TokenAckPacket(pkt.getEvent().getSignature(), getSubscribersCount(clients)); + sendToTokenService(TokenAckPacket.subject, tokenAck, outbox); + } + } - private final int getSubscribersCount(Map clients) { - int count = 0; - for (Integer val : clients.values()) { - count += val; - } - return count; - } + private final int getSubscribersCount(Map clients) { + int count = 0; + for (Integer val : clients.values()) { + count += val; + } + return count; + } - private final void processAdvertisement(NodeDescriptor sender, AdvertisementPacket packet, Collection neighbors, Outbox outbox) { - if (Consts.consistencyType == ConsistencyType.GLITCH_FREE || Consts.consistencyType == ConsistencyType.ATOMIC) { - dependencyDetector.processAdvertisementPacket(packet); - dependencyDetector.consolidate(); - } - if (Consts.consistencyType == ConsistencyType.ATOMIC && sender.isClient()) { - assert (tokenService != null); - sendToTokenService(AdvertisementPacket.subject, packet, outbox); - } - if (packet.isPublic()) { - switch (packet.getAdvType()) { - case ADV: - advTable.addAdvertisement(sender, packet.getAdvertisement()); - break; - case UNADV: - advTable.removeAdvertisement(sender, packet.getAdvertisement()); - break; - } - } - outbox.add(AdvertisementPacket.subject, packet, getAllBrokersExcept(sender, neighbors)); - } + private final void processAdvertisement(NodeDescriptor sender, AdvertisementPacket packet, + Collection neighbors, Outbox outbox) { + if (Consts.consistencyType == ConsistencyType.GLITCH_FREE || Consts.consistencyType == ConsistencyType.ATOMIC) { + dependencyDetector.processAdvertisementPacket(packet); + dependencyDetector.consolidate(); + } + if (Consts.consistencyType == ConsistencyType.ATOMIC && sender.isClient()) { + assert (tokenService != null); + sendToTokenService(AdvertisementPacket.subject, packet, outbox); + } + if (packet.isPublic()) { + switch (packet.getAdvType()) { + case ADV: + advTable.addAdvertisement(sender, packet.getAdvertisement()); + break; + case UNADV: + advTable.removeAdvertisement(sender, packet.getAdvertisement()); + break; + } + } + outbox.add(AdvertisementPacket.subject, packet, getAllBrokersExcept(sender, neighbors)); + } - private final void processRegistryAdvertise(NodeDescriptor sender, RegistryAdvertisePacket packet) { - switch (packet.getType()) { - case ADV: - registry = sender; - break; - case UNADV: - registry = null; - break; - default: - assert false : packet.getType(); - } - } + private final void processRegistryAdvertise(NodeDescriptor sender, RegistryAdvertisePacket packet) { + switch (packet.getType()) { + case ADV: + registry = sender; + break; + case UNADV: + registry = null; + break; + default: + assert false : packet.getType(); + } + } - private final void processTokenServiceAdvertise(NodeDescriptor sender, TokenServiceAdvertisePacket packet) { - switch (packet.getType()) { - case ADV: - tokenService = sender; - break; - case UNADV: - tokenService = null; - break; - default: - assert false : packet.getType(); - } + private final void processTokenServiceAdvertise(NodeDescriptor sender, TokenServiceAdvertisePacket packet) { + switch (packet.getType()) { + case ADV: + tokenService = sender; + break; + case UNADV: + tokenService = null; + break; + default: + assert false : packet.getType(); + } - } + } - private final void sendEventToRegistryIfNeeded(EventPacket pkt, Outbox outbox) { - if (registry != null && pkt.getEvent().isPersistent()) { - sendToRegistry(EventPacket.subject, pkt, outbox); - } - } + private final void sendEventToRegistryIfNeeded(EventPacket pkt, Outbox outbox) { + if (registry != null && pkt.getEvent().isPersistent()) { + sendToRegistry(EventPacket.subject, pkt, outbox); + } + } - private final void sendSubscriptionToRegistryIfNeeded(SubscriptionPacket subPkt, NodeDescriptor sender, Collection neighbors, Outbox box) { - if (registry != null && nodeIsLastBroker(sender, neighbors)) { - sendToRegistry(SubscriptionPacket.subject, subPkt, box); - } - } + private final void sendSubscriptionToRegistryIfNeeded(SubscriptionPacket subPkt, NodeDescriptor sender, + Collection neighbors, Outbox box) { + if (registry != null && nodeIsLastBroker(sender, neighbors)) { + sendToRegistry(SubscriptionPacket.subject, subPkt, box); + } + } - private final void sendToRegistry(String subject, Serializable packet, Outbox box) { - sendTo(subject, packet, box, registry); - } + private final void sendToRegistry(String subject, Serializable packet, Outbox box) { + sendTo(subject, packet, box, registry); + } - private final void sendToTokenService(String subject, Serializable packet, Outbox box) { - sendTo(subject, packet, box, tokenService); - } + private final void sendToTokenService(String subject, Serializable packet, Outbox box) { + sendTo(subject, packet, box, tokenService); + } - private final void sendTo(String subject, Serializable packet, Outbox box, Collection recipients) { - box.add(subject, packet, recipients); - } + private final void sendTo(String subject, Serializable packet, Outbox box, Collection recipients) { + box.add(subject, packet, recipients); + } - private final void sendTo(String subject, Serializable packet, Outbox box, NodeDescriptor recipient) { - Collection recipients = new ArrayList(1); - recipients.add(recipient); - box.add(subject, packet, recipients); - } + private final void sendTo(String subject, Serializable packet, Outbox box, NodeDescriptor recipient) { + Collection recipients = new ArrayList(1); + recipients.add(recipient); + box.add(subject, packet, recipients); + } - private final boolean nodeIsLastBroker(NodeDescriptor sender, Collection neighbors) { - return getAllBrokersExcept(sender, neighbors).isEmpty(); - } + private final boolean nodeIsLastBroker(NodeDescriptor sender, Collection neighbors) { + return getAllBrokersExcept(sender, neighbors).isEmpty(); + } - private final Collection getAllBrokersExcept(NodeDescriptor nodeToSkip, Collection neighbors) { - Collection result = new ArrayList(); - for (NodeDescriptor neighbor : neighbors) { - if (!neighbor.isBroker()) continue; - if (neighbor.equals(nodeToSkip)) continue; - if (registry != null && neighbor.equals(registry)) continue; - if (tokenService != null && neighbor.equals(tokenService)) continue; - result.add(neighbor); - } - return result; - } + private final Collection getAllBrokersExcept(NodeDescriptor nodeToSkip, + Collection neighbors) { + Collection result = new ArrayList(); + for (NodeDescriptor neighbor : neighbors) { + if (!neighbor.isBroker()) + continue; + if (neighbor.equals(nodeToSkip)) + continue; + if (registry != null && neighbor.equals(registry)) + continue; + if (tokenService != null && neighbor.equals(tokenService)) + continue; + result.add(neighbor); + } + return result; + } - private final void reactToRemovedNeighbor(NodeDescriptor node) { - logger.fine("Removing neighbor"); - if (node.isBroker()) { - brokersSubTable.removeAllSubscriptionsFor(node); - advTable.removeAllAdvertisementsFor(node); - } else { - clientsSubTable.removeAllSubscriptionsFor(node); - } - } + private final void reactToRemovedNeighbor(NodeDescriptor node) { + logger.fine("Removing neighbor"); + if (node.isBroker()) { + brokersSubTable.removeAllSubscriptionsFor(node); + advTable.removeAllAdvertisementsFor(node); + } else { + clientsSubTable.removeAllSubscriptionsFor(node); + } + } - @Override - public final void notifyNeighborAdded(NodeDescriptor node) { - // Nothing to do - } + @Override + public final void notifyNeighborAdded(NodeDescriptor node) { + // Nothing to do + } - @Override - public final void notifyNeighborDead(NodeDescriptor node) { - reactToRemovedNeighbor(node); - } + @Override + public final void notifyNeighborDead(NodeDescriptor node) { + reactToRemovedNeighbor(node); + } - @Override - public final void notifyNeighborRemoved(NodeDescriptor node) { - reactToRemovedNeighbor(node); - } + @Override + public final void notifyNeighborRemoved(NodeDescriptor node) { + reactToRemovedNeighbor(node); + } } diff --git a/Dream2/src/main/java/javareact/server/ServerLauncher.java b/Dream2/src/main/java/javareact/server/ServerLauncher.java index df5a546..dc879fe 100755 --- a/Dream2/src/main/java/javareact/server/ServerLauncher.java +++ b/Dream2/src/main/java/javareact/server/ServerLauncher.java @@ -17,38 +17,38 @@ import polimi.reds.broker.routing.GenericRouter; public class ServerLauncher { - private static ServerLauncher launcher; - - private final Logger logger = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME); - private final Overlay overlay; - - private ServerLauncher() { - Transport tr = new TCPTransport(Consts.serverPort); - TopologyManager tm = new SimpleTopologyManager(); - overlay = new GenericOverlay(tm, tr); - GenericRouter router = new GenericRouter(overlay); - ServerEventForwarder forwarder = new ServerEventForwarder(); - overlay.addNeighborhoodChangeListener(forwarder); - router.setPacketForwarder(EventPacket.subject, forwarder); - router.setPacketForwarder(SubscriptionPacket.subject, forwarder); - router.setPacketForwarder(AdvertisementPacket.subject, forwarder); - router.setPacketForwarder(RegistryAdvertisePacket.subject, forwarder); - router.setPacketForwarder(TokenServiceAdvertisePacket.subject, forwarder); - } - - public static final void start() { - if (launcher == null) { - launcher = new ServerLauncher(); - } - launcher.logger.info("Starting server"); - launcher.overlay.start(); - } - - public static final void stop() { - if (launcher != null) { - launcher.logger.info("Stopping server"); - launcher.overlay.stop(); - } - } + private static ServerLauncher launcher; + + private final Logger logger = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME); + private final Overlay overlay; + + private ServerLauncher() { + Transport tr = new TCPTransport(Consts.serverPort); + TopologyManager tm = new SimpleTopologyManager(); + overlay = new GenericOverlay(tm, tr); + GenericRouter router = new GenericRouter(overlay); + ServerEventForwarder forwarder = new ServerEventForwarder(); + overlay.addNeighborhoodChangeListener(forwarder); + router.setPacketForwarder(EventPacket.subject, forwarder); + router.setPacketForwarder(SubscriptionPacket.subject, forwarder); + router.setPacketForwarder(AdvertisementPacket.subject, forwarder); + router.setPacketForwarder(RegistryAdvertisePacket.subject, forwarder); + router.setPacketForwarder(TokenServiceAdvertisePacket.subject, forwarder); + } + + public static final void start() { + if (launcher == null) { + launcher = new ServerLauncher(); + } + launcher.logger.info("Starting server"); + launcher.overlay.start(); + } + + public static final void stop() { + if (launcher != null) { + launcher.logger.info("Stopping server"); + launcher.overlay.stop(); + } + } } diff --git a/Dream2/src/main/java/javareact/server/SubscriptionTable.java b/Dream2/src/main/java/javareact/server/SubscriptionTable.java index e4b178e..ff8c88d 100755 --- a/Dream2/src/main/java/javareact/server/SubscriptionTable.java +++ b/Dream2/src/main/java/javareact/server/SubscriptionTable.java @@ -10,49 +10,50 @@ import polimi.reds.NodeDescriptor; final class SubscriptionTable { - private final Map> subs = new HashMap>(); - - final void addSubscription(NodeDescriptor node, Subscription sub) { - Collection subsList = subs.get(node); - if (subsList == null) { - subsList = new ArrayList(); - subs.put(node, subsList); - } - subsList.add(sub); - } - - final void removeSubscription(NodeDescriptor node, Subscription sub) { - Collection subsList = subs.get(node); - if (subsList == null) return; - subsList.remove(sub); - if (subsList.isEmpty()) { - subs.remove(node); - } - } - - final Map getMatchingNodes(Event ev) { - Map result = new HashMap(); - for (NodeDescriptor node : subs.keySet()) { - int count = 0; - for (Subscription sub : subs.get(node)) { - if (sub.isSatisfiedBy(ev)) { - count++; - } - } - if (count != 0) { - result.put(node, count); - } - } - return result; - } - - final void removeAllSubscriptionsFor(NodeDescriptor node) { - subs.remove(node); - } - - @Override - public String toString() { - return "SubscriptionTable [subs=" + subs + "]"; - } + private final Map> subs = new HashMap>(); + + final void addSubscription(NodeDescriptor node, Subscription sub) { + Collection subsList = subs.get(node); + if (subsList == null) { + subsList = new ArrayList(); + subs.put(node, subsList); + } + subsList.add(sub); + } + + final void removeSubscription(NodeDescriptor node, Subscription sub) { + Collection subsList = subs.get(node); + if (subsList == null) + return; + subsList.remove(sub); + if (subsList.isEmpty()) { + subs.remove(node); + } + } + + final Map getMatchingNodes(Event ev) { + Map result = new HashMap(); + for (NodeDescriptor node : subs.keySet()) { + int count = 0; + for (Subscription sub : subs.get(node)) { + if (sub.isSatisfiedBy(ev)) { + count++; + } + } + if (count != 0) { + result.put(node, count); + } + } + return result; + } + + final void removeAllSubscriptionsFor(NodeDescriptor node) { + subs.remove(node); + } + + @Override + public String toString() { + return "SubscriptionTable [subs=" + subs + "]"; + } } diff --git a/Dream2/src/main/java/javareact/server/WaitRecommendations.java b/Dream2/src/main/java/javareact/server/WaitRecommendations.java index 754fb29..6f97317 100755 --- a/Dream2/src/main/java/javareact/server/WaitRecommendations.java +++ b/Dream2/src/main/java/javareact/server/WaitRecommendations.java @@ -5,44 +5,45 @@ import java.util.Set; /** - * A WaitRecommendations object is stored inside an event E. It is used by the server to prevent glitches while - * delivering E to the clients. It tells a client C that it must wait for other events before processing E. + * A WaitRecommendations object is stored inside an event E. It is used by the + * server to prevent glitches while delivering E to the clients. It tells a + * client C that it must wait for other events before processing E. */ public class WaitRecommendations implements Serializable { - private static final long serialVersionUID = 5700944080087353096L; + private static final long serialVersionUID = 5700944080087353096L; - private final String expression; - private final Set expressionsToWaitFor = new HashSet(); + private final String expression; + private final Set expressionsToWaitFor = new HashSet(); - public WaitRecommendations(String expression) { - this.expression = expression; - } + public WaitRecommendations(String expression) { + this.expression = expression; + } - public final void addRecommendation(String expressionToWaitFor) { - expressionsToWaitFor.add(expressionToWaitFor); - } + public final void addRecommendation(String expressionToWaitFor) { + expressionsToWaitFor.add(expressionToWaitFor); + } - public final String getExpression() { - return expression; - } + public final String getExpression() { + return expression; + } - public final Set getRecommendations() { - return expressionsToWaitFor; - } + public final Set getRecommendations() { + return expressionsToWaitFor; + } - public final WaitRecommendations dup() { - WaitRecommendations result = new WaitRecommendations(expression); - result.expressionsToWaitFor.addAll(expressionsToWaitFor); - return result; - } + public final WaitRecommendations dup() { + WaitRecommendations result = new WaitRecommendations(expression); + result.expressionsToWaitFor.addAll(expressionsToWaitFor); + return result; + } - public final void removeExpressionToWaitFor(String expressionToWaitFor) { - expressionsToWaitFor.remove(expressionToWaitFor); - } + public final void removeExpressionToWaitFor(String expressionToWaitFor) { + expressionsToWaitFor.remove(expressionToWaitFor); + } - @Override - public String toString() { - return "WaitRecommendations [expression=" + expression + ", expressionsToWaitFor=" + expressionsToWaitFor + "]"; - } + @Override + public String toString() { + return "WaitRecommendations [expression=" + expression + ", expressionsToWaitFor=" + expressionsToWaitFor + "]"; + } } diff --git a/Dream2/src/main/java/javareact/token_service/FinalExpressionsDetector.java b/Dream2/src/main/java/javareact/token_service/FinalExpressionsDetector.java index 0304b94..35f6dbf 100755 --- a/Dream2/src/main/java/javareact/token_service/FinalExpressionsDetector.java +++ b/Dream2/src/main/java/javareact/token_service/FinalExpressionsDetector.java @@ -10,109 +10,111 @@ import javareact.common.packets.content.Subscription; /** - * A FinalExpressionsDetector is used by the token service in case of atomic consistency. It detects all the final - * expressions in a reactive graph. Final expressions travel with an event packet. The token service waits until all the - * final expressions have been processed before releasing the token. To enable this, each server notifies the token - * service when processing a final expression. + * A FinalExpressionsDetector is used by the token service in case of atomic + * consistency. It detects all the final expressions in a reactive graph. Final + * expressions travel with an event packet. The token service waits until all + * the final expressions have been processed before releasing the token. To + * enable this, each server notifies the token service when processing a final + * expression. */ final class FinalExpressionsDetector { - // Initial expressions (pure observables) - private final Set initialExpressions = new HashSet(); - // Expression E -> expressions that directly depend from E - private final Map> dependencyGraph = new HashMap>(); - // Expression E -> final expressions generated from E - private final Map> finalExpressions = new HashMap>(); + // Initial expressions (pure observables) + private final Set initialExpressions = new HashSet(); + // Expression E -> expressions that directly depend from E + private final Map> dependencyGraph = new HashMap>(); + // Expression E -> final expressions generated from E + private final Map> finalExpressions = new HashMap>(); - final void processAdvertisementPacket(AdvertisementPacket advPkt) { - switch (advPkt.getAdvType()) { - case ADV: - processAdv(advPkt); - break; - case UNADV: - processUnadv(advPkt); - break; - default: - assert false : advPkt.getAdvType(); - } - } + final void processAdvertisementPacket(AdvertisementPacket advPkt) { + switch (advPkt.getAdvType()) { + case ADV: + processAdv(advPkt); + break; + case UNADV: + processUnadv(advPkt); + break; + default: + assert false : advPkt.getAdvType(); + } + } - final void consolidate() { - finalExpressions.clear(); - computeFinalExpressions(); - } + final void consolidate() { + finalExpressions.clear(); + computeFinalExpressions(); + } - final Map getFinalExpressionsFor(String expr) { - assert (finalExpressions.containsKey(expr)); - return finalExpressions.get(expr); - } + final Map getFinalExpressionsFor(String expr) { + assert (finalExpressions.containsKey(expr)); + return finalExpressions.get(expr); + } - private final void processAdv(AdvertisementPacket advPkt) { - String advSignature = advPkt.getAdvertisement().getSignature(); - if (!advPkt.containtsSubscriptions()) { - initialExpressions.add(advSignature); - } else { - for (Subscription sub : advPkt.getSubscriptions()) { - String subSignature = sub.getSignature(); - addDependency(subSignature, advSignature, sub.getProxyID()); - } - } - } + private final void processAdv(AdvertisementPacket advPkt) { + String advSignature = advPkt.getAdvertisement().getSignature(); + if (!advPkt.containtsSubscriptions()) { + initialExpressions.add(advSignature); + } else { + for (Subscription sub : advPkt.getSubscriptions()) { + String subSignature = sub.getSignature(); + addDependency(subSignature, advSignature, sub.getProxyID()); + } + } + } - private final void processUnadv(AdvertisementPacket advPkt) { - // TODO: manage unadvertisements - } + private final void processUnadv(AdvertisementPacket advPkt) { + // TODO: manage unadvertisements + } - private final void addDependency(String expression, String depending, UUID proxyID) { - Map dependingMap = dependencyGraph.get(expression); - if (dependingMap == null) { - dependingMap = new HashMap(); - dependencyGraph.put(expression, dependingMap); - } - dependingMap.put(depending, proxyID); - } + private final void addDependency(String expression, String depending, UUID proxyID) { + Map dependingMap = dependencyGraph.get(expression); + if (dependingMap == null) { + dependingMap = new HashMap(); + dependencyGraph.put(expression, dependingMap); + } + dependingMap.put(depending, proxyID); + } - private final void computeFinalExpressions() { - for (String initialExpr : initialExpressions) { - computeFinalExpressionsFor(initialExpr); - } - } + private final void computeFinalExpressions() { + for (String initialExpr : initialExpressions) { + computeFinalExpressionsFor(initialExpr); + } + } - private final void computeFinalExpressionsFor(String initialExpr) { - Map finalExpressionsMap = new HashMap(); - computeFinalExpressionsFor(initialExpr, finalExpressionsMap); - finalExpressions.put(initialExpr, finalExpressionsMap); - } + private final void computeFinalExpressionsFor(String initialExpr) { + Map finalExpressionsMap = new HashMap(); + computeFinalExpressionsFor(initialExpr, finalExpressionsMap); + finalExpressions.put(initialExpr, finalExpressionsMap); + } - private final void computeFinalExpressionsFor(String currentExpr, Map results) { - int count = getFinalLinksFor(currentExpr); - if (count != 0) { - results.put(currentExpr, count); - } - if (dependencyGraph.containsKey(currentExpr)) { - for (String expr : dependencyGraph.get(currentExpr).keySet()) { - computeFinalExpressionsFor(expr, results); - } - } - } + private final void computeFinalExpressionsFor(String currentExpr, Map results) { + int count = getFinalLinksFor(currentExpr); + if (count != 0) { + results.put(currentExpr, count); + } + if (dependencyGraph.containsKey(currentExpr)) { + for (String expr : dependencyGraph.get(currentExpr).keySet()) { + computeFinalExpressionsFor(expr, results); + } + } + } - private final int getFinalLinksFor(String expr) { - int count = 0; - Set alreadyUsedIds = new HashSet(); - if (dependencyGraph.containsKey(expr)) { - Map innerMap = dependencyGraph.get(expr); - for (String e : innerMap.keySet()) { - UUID proxyID = innerMap.get(e); - if (alreadyUsedIds.contains(proxyID)) { - continue; - } - if (!dependencyGraph.containsKey(e)) { - alreadyUsedIds.add(proxyID); - count++; - } else { - return 0; - } - } - } - return count; - } + private final int getFinalLinksFor(String expr) { + int count = 0; + Set alreadyUsedIds = new HashSet(); + if (dependencyGraph.containsKey(expr)) { + Map innerMap = dependencyGraph.get(expr); + for (String e : innerMap.keySet()) { + UUID proxyID = innerMap.get(e); + if (alreadyUsedIds.contains(proxyID)) { + continue; + } + if (!dependencyGraph.containsKey(e)) { + alreadyUsedIds.add(proxyID); + count++; + } else { + return 0; + } + } + } + return count; + } } diff --git a/Dream2/src/main/java/javareact/token_service/TokenService.java b/Dream2/src/main/java/javareact/token_service/TokenService.java index 1c3b98d..d55799f 100755 --- a/Dream2/src/main/java/javareact/token_service/TokenService.java +++ b/Dream2/src/main/java/javareact/token_service/TokenService.java @@ -17,114 +17,116 @@ import polimi.reds.broker.routing.PacketForwarder; /** - * A token service ensures that only one event (called active event) is propagated through the broker network at any - * given time. In particular, it stores incoming events in a queue and delivers them only when no more forwarding are - * planned for the currently active event. + * A token service ensures that only one event (called active event) is + * propagated through the broker network at any given time. In particular, it + * stores incoming events in a queue and delivers them only when no more + * forwarding are planned for the currently active event. */ public class TokenService implements PacketForwarder { - private final Logger logger = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME); - private final Queue queue = new ArrayDeque(); - private final FinalExpressionsDetector finalExpressionsDetector = new FinalExpressionsDetector(); - - @Override - public Collection forwardPacket(String subject, NodeDescriptor sender, Serializable packet, Collection neighbors, Outbox box) { - if (subject.equals(EventPacket.subject)) { - assert (packet instanceof EventPacket); - logger.fine("Received an event packet"); - processEventPacket((EventPacket) packet, sender, box); - } else if (subject.equals(AdvertisementPacket.subject)) { - assert (packet instanceof AdvertisementPacket); - logger.fine("Received an advertisement packet"); - processAdvertisementPacket((AdvertisementPacket) packet, sender, box); - } else if (subject.equals(TokenAckPacket.subject)) { - assert (packet instanceof TokenAckPacket); - logger.fine("Received a token ack packet"); - processTokenAckPacket((TokenAckPacket) packet, box); - } else { - logger.warning("Received a message of unknown type"); - } - return new ArrayList(); - } - - private final void processEventPacket(EventPacket pkt, NodeDescriptor sender, Outbox box) { - PendingEvent pending = new PendingEvent(pkt, sender); - boolean canStartProcessing = queue.isEmpty(); - queue.add(pending); - if (canStartProcessing) { - forwardNextEventIfAny(box); - } - } - - private final void processAdvertisementPacket(AdvertisementPacket pkt, NodeDescriptor sender, Outbox box) { - finalExpressionsDetector.processAdvertisementPacket(pkt); - finalExpressionsDetector.consolidate(); - } - - private final void processTokenAckPacket(TokenAckPacket pkt, Outbox box) { - if (queue.isEmpty()) { - assert (false); - logger.warning("Received a TokenAck but the queue is empty"); - } - PendingEvent pending = queue.peek(); - pending.processTokenAck(pkt); - if (pending.hasFinishedWaiting()) { - queue.poll(); - forwardNextEventIfAny(box); - } - } - - private final void forwardNextEventIfAny(Outbox box) { - if (!queue.isEmpty()) { - PendingEvent pending = queue.peek(); - box.add(EventPacket.subject, pending.getEventPkt(), pending.getRecipients()); - } - } - - private class PendingEvent { - private final EventPacket eventPkt; - private final Collection recipients = new ArrayList(1); - private final Map waitingReplies = new HashMap(); - - PendingEvent(EventPacket eventPkt, NodeDescriptor sender) { - this.eventPkt = eventPkt; - recipients.add(sender); - waitingReplies.putAll(finalExpressionsDetector.getFinalExpressionsFor(eventPkt.getEvent().getSignature())); - addFinalExpressionsToPacket(); - } - - final EventPacket getEventPkt() { - eventPkt.tokenServiceApproves(); - return eventPkt; - } - - final Collection getRecipients() { - return recipients; - } - - final void processTokenAck(TokenAckPacket pkt) { - removeFromWaiting(pkt.getFinalExpression(), pkt.getCount()); - } - - final boolean hasFinishedWaiting() { - return waitingReplies.isEmpty(); - } - - private final void removeFromWaiting(String finalExpression, int count) { - assert (waitingReplies.containsKey(finalExpression)); - int currentCount = waitingReplies.get(finalExpression); - int newCount = currentCount - count; - assert (newCount >= 0); - waitingReplies.remove(finalExpression); - if (newCount > 0) { - waitingReplies.put(finalExpression, newCount); - } - } - - private final void addFinalExpressionsToPacket() { - for (String finalExpr : waitingReplies.keySet()) { - eventPkt.addFinalExpression(finalExpr); - } - } - } + private final Logger logger = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME); + private final Queue queue = new ArrayDeque(); + private final FinalExpressionsDetector finalExpressionsDetector = new FinalExpressionsDetector(); + + @Override + public Collection forwardPacket(String subject, NodeDescriptor sender, Serializable packet, + Collection neighbors, Outbox box) { + if (subject.equals(EventPacket.subject)) { + assert (packet instanceof EventPacket); + logger.fine("Received an event packet"); + processEventPacket((EventPacket) packet, sender, box); + } else if (subject.equals(AdvertisementPacket.subject)) { + assert (packet instanceof AdvertisementPacket); + logger.fine("Received an advertisement packet"); + processAdvertisementPacket((AdvertisementPacket) packet, sender, box); + } else if (subject.equals(TokenAckPacket.subject)) { + assert (packet instanceof TokenAckPacket); + logger.fine("Received a token ack packet"); + processTokenAckPacket((TokenAckPacket) packet, box); + } else { + logger.warning("Received a message of unknown type"); + } + return new ArrayList(); + } + + private final void processEventPacket(EventPacket pkt, NodeDescriptor sender, Outbox box) { + PendingEvent pending = new PendingEvent(pkt, sender); + boolean canStartProcessing = queue.isEmpty(); + queue.add(pending); + if (canStartProcessing) { + forwardNextEventIfAny(box); + } + } + + private final void processAdvertisementPacket(AdvertisementPacket pkt, NodeDescriptor sender, Outbox box) { + finalExpressionsDetector.processAdvertisementPacket(pkt); + finalExpressionsDetector.consolidate(); + } + + private final void processTokenAckPacket(TokenAckPacket pkt, Outbox box) { + if (queue.isEmpty()) { + assert (false); + logger.warning("Received a TokenAck but the queue is empty"); + } + PendingEvent pending = queue.peek(); + pending.processTokenAck(pkt); + if (pending.hasFinishedWaiting()) { + queue.poll(); + forwardNextEventIfAny(box); + } + } + + private final void forwardNextEventIfAny(Outbox box) { + if (!queue.isEmpty()) { + PendingEvent pending = queue.peek(); + box.add(EventPacket.subject, pending.getEventPkt(), pending.getRecipients()); + } + } + + private class PendingEvent { + private final EventPacket eventPkt; + private final Collection recipients = new ArrayList(1); + private final Map waitingReplies = new HashMap(); + + PendingEvent(EventPacket eventPkt, NodeDescriptor sender) { + this.eventPkt = eventPkt; + recipients.add(sender); + waitingReplies.putAll(finalExpressionsDetector.getFinalExpressionsFor(eventPkt.getEvent().getSignature())); + addFinalExpressionsToPacket(); + } + + final EventPacket getEventPkt() { + eventPkt.tokenServiceApproves(); + return eventPkt; + } + + final Collection getRecipients() { + return recipients; + } + + final void processTokenAck(TokenAckPacket pkt) { + removeFromWaiting(pkt.getFinalExpression(), pkt.getCount()); + } + + final boolean hasFinishedWaiting() { + return waitingReplies.isEmpty(); + } + + private final void removeFromWaiting(String finalExpression, int count) { + assert (waitingReplies.containsKey(finalExpression)); + int currentCount = waitingReplies.get(finalExpression); + int newCount = currentCount - count; + assert (newCount >= 0); + waitingReplies.remove(finalExpression); + if (newCount > 0) { + waitingReplies.put(finalExpression, newCount); + } + } + + private final void addFinalExpressionsToPacket() { + for (String finalExpr : waitingReplies.keySet()) { + eventPkt.addFinalExpression(finalExpr); + } + } + } } diff --git a/Dream2/src/main/java/javareact/token_service/TokenServiceLauncher.java b/Dream2/src/main/java/javareact/token_service/TokenServiceLauncher.java index 6bebc7b..6a002ef 100755 --- a/Dream2/src/main/java/javareact/token_service/TokenServiceLauncher.java +++ b/Dream2/src/main/java/javareact/token_service/TokenServiceLauncher.java @@ -22,61 +22,63 @@ import polimi.reds.broker.routing.GenericRouter; public class TokenServiceLauncher { - private static TokenServiceLauncher launcher; + private static TokenServiceLauncher launcher; - private final Overlay overlay; - private final Logger logger = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME); + private final Overlay overlay; + private final Logger logger = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME); - private TokenServiceLauncher() { - Transport tr = null; - try { - tr = new TCPTransport(); - } catch (IOException e) { - e.printStackTrace(); - } - TopologyManager tm = new SimpleTopologyManager(); - overlay = new GenericOverlay(tm, tr); - GenericRouter router = new GenericRouter(overlay); - TokenService registry = new TokenService(); - router.setPacketForwarder(EventPacket.subject, registry); - router.setPacketForwarder(AdvertisementPacket.subject, registry); - router.setPacketForwarder(TokenAckPacket.subject, registry); - } + private TokenServiceLauncher() { + Transport tr = null; + try { + tr = new TCPTransport(); + } catch (IOException e) { + e.printStackTrace(); + } + TopologyManager tm = new SimpleTopologyManager(); + overlay = new GenericOverlay(tm, tr); + GenericRouter router = new GenericRouter(overlay); + TokenService registry = new TokenService(); + router.setPacketForwarder(EventPacket.subject, registry); + router.setPacketForwarder(AdvertisementPacket.subject, registry); + router.setPacketForwarder(TokenAckPacket.subject, registry); + } - public static final void start(Collection addresses) { - if (launcher == null) { - launcher = new TokenServiceLauncher(); - } - launcher.logger.fine("Starting registry"); - launcher.overlay.start(); - for (String address : addresses) { - try { - launcher.overlay.addNeighbor(address); - } catch (ConnectException | MalformedURLException | NotRunningException e) { - e.printStackTrace(); - } - } - for (NodeDescriptor node : launcher.overlay.getNeighbors()) { - try { - launcher.overlay.send(TokenServiceAdvertisePacket.subject, new TokenServiceAdvertisePacket(AdvType.ADV), node); - } catch (IOException | NotRunningException e) { - e.printStackTrace(); - } - } - } + public static final void start(Collection addresses) { + if (launcher == null) { + launcher = new TokenServiceLauncher(); + } + launcher.logger.fine("Starting registry"); + launcher.overlay.start(); + for (String address : addresses) { + try { + launcher.overlay.addNeighbor(address); + } catch (ConnectException | MalformedURLException | NotRunningException e) { + e.printStackTrace(); + } + } + for (NodeDescriptor node : launcher.overlay.getNeighbors()) { + try { + launcher.overlay.send(TokenServiceAdvertisePacket.subject, + new TokenServiceAdvertisePacket(AdvType.ADV), node); + } catch (IOException | NotRunningException e) { + e.printStackTrace(); + } + } + } - public static final void stop() { - if (launcher != null) { - for (NodeDescriptor node : launcher.overlay.getNeighbors()) { - try { - launcher.overlay.send(TokenServiceAdvertisePacket.subject, new TokenServiceAdvertisePacket(AdvType.UNADV), node); - } catch (IOException | NotRunningException e) { - e.printStackTrace(); - } - } - launcher.logger.fine("Stopping registry"); - launcher.overlay.stop(); - } - } + public static final void stop() { + if (launcher != null) { + for (NodeDescriptor node : launcher.overlay.getNeighbors()) { + try { + launcher.overlay.send(TokenServiceAdvertisePacket.subject, new TokenServiceAdvertisePacket( + AdvType.UNADV), node); + } catch (IOException | NotRunningException e) { + e.printStackTrace(); + } + } + launcher.logger.fine("Stopping registry"); + launcher.overlay.stop(); + } + } } diff --git a/Dream2/src/test/java/javareact/LocalTest.java b/Dream2/src/test/java/javareact/LocalTest.java index e66ee1f..4da2f17 100755 --- a/Dream2/src/test/java/javareact/LocalTest.java +++ b/Dream2/src/test/java/javareact/LocalTest.java @@ -26,56 +26,49 @@ public void localTest1() { Var obString1 = new Var<>("obString1", ""); Var obString2 = new Var<>("obString2", ""); - Signal reactInt = new Signal("reactInt", - () -> { - if(obInt.get() == null) return null; - return 10 - 2 + ((obInt.get() * 2) + obInt.get()) / 2; - }, - obInt); - - Signal reactString = new Signal("reactString", - () -> obString1.get() + obString2.get(), + Signal reactInt = new Signal("reactInt", () -> { + if (obInt.get() == null) + return null; + return 10 - 2 + ((obInt.get() * 2) + obInt.get()) / 2; + }, obInt); + + Signal reactString = new Signal("reactString", () -> obString1.get() + obString2.get(), obString1, obString2); - Signal reactInt2 = new Signal("reactInt2", - () -> { - if(reactInt.get() == null) return null; - return reactInt.get() * 2; - }, - reactInt); + Signal reactInt2 = new Signal("reactInt2", () -> { + if (reactInt.get() == null) + return null; + return reactInt.get() * 2; + }, reactInt); Var obIntStart = new Var<>("obIntStart", Integer.valueOf(1)); - - Signal reactInterm1 = new Signal("reactInterm1", - () -> { - System.out.println("reactInterm1: " + obIntStart.get()); - if(obIntStart.get() == null) return null; - return obIntStart.get() * 2; - }, - obIntStart); - - Signal reactInterm2 = new Signal("reactInterm2", - () -> { - System.out.println("reactInterm2: " + reactInterm1.get()); - if(reactInterm1.get() == null) return null; - return reactInterm1.get() * 2; - }, - reactInterm1); - - Signal reactFinal = new Signal("reactFinal", - () -> { - System.out.println("reactFinal: " + reactInterm1.get() + " " + reactInterm2.get()); - if(reactInterm1.get() == null || reactInterm2.get() == null) return null; - return reactInterm1.get() + reactInterm2.get(); - }, - reactInterm1, reactInterm2); - - Signal reactFinal2 = new Signal("reactFinal2", - () -> { - if(reactInterm1.get() == null || obIntStart.get() == null) return null; - return reactInterm1.get() + obIntStart.get(); - }, - reactInterm1, obIntStart); + + Signal reactInterm1 = new Signal("reactInterm1", () -> { + System.out.println("reactInterm1: " + obIntStart.get()); + if (obIntStart.get() == null) + return null; + return obIntStart.get() * 2; + }, obIntStart); + + Signal reactInterm2 = new Signal("reactInterm2", () -> { + System.out.println("reactInterm2: " + reactInterm1.get()); + if (reactInterm1.get() == null) + return null; + return reactInterm1.get() * 2; + }, reactInterm1); + + Signal reactFinal = new Signal("reactFinal", () -> { + System.out.println("reactFinal: " + reactInterm1.get() + " " + reactInterm2.get()); + if (reactInterm1.get() == null || reactInterm2.get() == null) + return null; + return reactInterm1.get() + reactInterm2.get(); + }, reactInterm1, reactInterm2); + + Signal reactFinal2 = new Signal("reactFinal2", () -> { + if (reactInterm1.get() == null || obIntStart.get() == null) + return null; + return reactInterm1.get() + obIntStart.get(); + }, reactInterm1, obIntStart); try { Thread.sleep(500); diff --git a/Dream2/src/test/java/javareact/LocalTest2.java b/Dream2/src/test/java/javareact/LocalTest2.java index b2cc817..cfb7008 100644 --- a/Dream2/src/test/java/javareact/LocalTest2.java +++ b/Dream2/src/test/java/javareact/LocalTest2.java @@ -14,94 +14,91 @@ import org.junit.Test; public class LocalTest2 { - private boolean serverStarted = false; - private boolean tokenServiceStarted = false; - - @Test - public void localTest2() { - startServerIfNeeded(); - startTokenServiceIfNeeded(); - - Var obIntStart = new Var<>("obIntStart", Integer.valueOf(1)); - RemoteVar obIntStartR = obIntStart.getProxy(); - Signal reactInterm1 = new Signal("reactInterm1", - () -> { - System.out.println("reactInterm1: " + obIntStartR.get()); - if(obIntStartR.get() == null) return null; - return obIntStartR.get() * 2; - }, - obIntStartR); - - RemoteVar reactInterm1R = reactInterm1.getProxy(); - - Signal reactInterm2 = new Signal("reactInterm2", - () -> { - System.out.println("reactInterm2: " + reactInterm1R.get()); - if(reactInterm1R.get() == null) return null; - return reactInterm1R.get() * 2; - }, - reactInterm1R); - - RemoteVar reactInterm2R = reactInterm2.getProxy(); - - Signal reactFinal = new Signal("reactFinal", - () -> { - System.out.println("reactFinal: " + reactInterm1.get() + " " + reactInterm2.get()); - if(reactInterm1.get() == null || reactInterm2.get() == null) return null; - return reactInterm1.get() + reactInterm2.get(); - }, - reactInterm1R, reactInterm2R); - - /*Signal reactFinal2 = new Signal("reactFinal2", - () -> { - if(reactInterm1.get() == null || obIntStart.get() == null) return null; - return reactInterm1.get() + obIntStart.get(); - }, - reactInterm1, obIntStart);*/ - - try { - Thread.sleep(500); - } catch (InterruptedException e) { - e.printStackTrace(); - } - - obIntStart.set(100); - - try { - Thread.sleep(500); - } catch (InterruptedException e) { - e.printStackTrace(); - } - - assertEquals(reactInterm1.get(), Integer.valueOf(200)); - assertEquals(reactInterm2.get(), Integer.valueOf(400)); - assertEquals(reactFinal.get(), Integer.valueOf(600)); - } - - private final void startServerIfNeeded() { - if (!serverStarted) { - ServerLauncher.start(); - serverStarted = true; - } - try { - Thread.sleep(500); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - - private final void startTokenServiceIfNeeded() { - if (!tokenServiceStarted) { - String serverAddress = "reds-tcp:localhost:9000"; - Set addresses = new HashSet(); - addresses.add(serverAddress); - TokenServiceLauncher.start(addresses); - tokenServiceStarted = true; - } - try { - Thread.sleep(500); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } + private boolean serverStarted = false; + private boolean tokenServiceStarted = false; + + @Test + public void localTest2() { + startServerIfNeeded(); + startTokenServiceIfNeeded(); + + Var obIntStart = new Var<>("obIntStart", Integer.valueOf(1)); + RemoteVar obIntStartR = obIntStart.getProxy(); + Signal reactInterm1 = new Signal("reactInterm1", () -> { + System.out.println("reactInterm1: " + obIntStartR.get()); + if (obIntStartR.get() == null) + return null; + return obIntStartR.get() * 2; + }, obIntStartR); + + RemoteVar reactInterm1R = reactInterm1.getProxy(); + + Signal reactInterm2 = new Signal("reactInterm2", () -> { + System.out.println("reactInterm2: " + reactInterm1R.get()); + if (reactInterm1R.get() == null) + return null; + return reactInterm1R.get() * 2; + }, reactInterm1R); + + RemoteVar reactInterm2R = reactInterm2.getProxy(); + + Signal reactFinal = new Signal("reactFinal", () -> { + System.out.println("reactFinal: " + reactInterm1.get() + " " + reactInterm2.get()); + if (reactInterm1.get() == null || reactInterm2.get() == null) + return null; + return reactInterm1.get() + reactInterm2.get(); + }, reactInterm1R, reactInterm2R); + + /* + * Signal reactFinal2 = new Signal("reactFinal2", () + * -> { if(reactInterm1.get() == null || obIntStart.get() == null) + * return null; return reactInterm1.get() + obIntStart.get(); }, + * reactInterm1, obIntStart); + */ + + try { + Thread.sleep(500); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + obIntStart.set(100); + + try { + Thread.sleep(500); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + assertEquals(reactInterm1.get(), Integer.valueOf(200)); + assertEquals(reactInterm2.get(), Integer.valueOf(400)); + assertEquals(reactFinal.get(), Integer.valueOf(600)); + } + + private final void startServerIfNeeded() { + if (!serverStarted) { + ServerLauncher.start(); + serverStarted = true; + } + try { + Thread.sleep(500); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + private final void startTokenServiceIfNeeded() { + if (!tokenServiceStarted) { + String serverAddress = "reds-tcp:localhost:9000"; + Set addresses = new HashSet(); + addresses.add(serverAddress); + TokenServiceLauncher.start(addresses); + tokenServiceStarted = true; + } + try { + Thread.sleep(500); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } } diff --git a/Dream2/src/test/java/javareact/LocalTestOld.java b/Dream2/src/test/java/javareact/LocalTestOld.java index 37c08b5..788230e 100644 --- a/Dream2/src/test/java/javareact/LocalTestOld.java +++ b/Dream2/src/test/java/javareact/LocalTestOld.java @@ -31,7 +31,8 @@ public void localTest1() { ReactiveInteger reactInt = new ReactiveInteger("reactInt", obInt.getProxy()) { @Override public Integer evaluate() { - if(obInt.get() == null) return null; + if (obInt.get() == null) + return null; return 10 - 2 + ((obInt.get() * 2) + obInt.get()) / 2; } }; @@ -46,7 +47,8 @@ public String evaluate() { ReactiveInteger reactInt2 = new ReactiveInteger("reactInt2", reactInt.getProxy()) { @Override public Integer evaluate() { - if(reactInt.get() == null) return null; + if (reactInt.get() == null) + return null; return reactInt.get() * 2; } }; @@ -57,7 +59,8 @@ public Integer evaluate() { @Override public Integer evaluate() { System.out.println("reactInterm1: " + obIntStartR.get()); - if(obIntStartR.get() == null) return null; + if (obIntStartR.get() == null) + return null; return obIntStartR.get() * 2; } }; @@ -68,7 +71,8 @@ public Integer evaluate() { @Override public Integer evaluate() { System.out.println("reactInterm2: " + reactInterm1R.get()); - if(reactInterm1R.get() == null) return null; + if (reactInterm1R.get() == null) + return null; return reactInterm1R.get() * 2; } }; @@ -79,7 +83,8 @@ public Integer evaluate() { @Override public Integer evaluate() { System.out.println("reactFinal: " + reactInterm1.get() + " " + reactInterm2.get()); - if(reactInterm1.get() == null || reactInterm2.get() == null) return null; + if (reactInterm1.get() == null || reactInterm2.get() == null) + return null; return reactInterm1.get() + reactInterm2.get(); } }; @@ -87,7 +92,8 @@ public Integer evaluate() { ReactiveInteger reactFinal2 = new ReactiveInteger("reactFinal2", reactInterm1.getProxy(), obIntStart.getProxy()) { @Override public Integer evaluate() { - if(reactInterm1.get() == null || obIntStart.get() == null) return null; + if (reactInterm1.get() == null || obIntStart.get() == null) + return null; return reactInterm1.get() + obIntStart.get(); } }; diff --git a/Dream2/src/test/java/javareact/RegressionTests.java b/Dream2/src/test/java/javareact/RegressionTests.java index 628aa9a..eaad13c 100755 --- a/Dream2/src/test/java/javareact/RegressionTests.java +++ b/Dream2/src/test/java/javareact/RegressionTests.java @@ -8,8 +8,8 @@ @RunWith(Suite.class) @Suite.SuiteClasses({ LocalTest.class, // - DependencyDetectorTest.class, // - FinalExpressionsDetectorTest.class // + DependencyDetectorTest.class, // + FinalExpressionsDetectorTest.class // }) public class RegressionTests { diff --git a/Dream2/src/test/java/javareact/RemoteObservable.java b/Dream2/src/test/java/javareact/RemoteObservable.java index 3a1044e..babd25a 100644 --- a/Dream2/src/test/java/javareact/RemoteObservable.java +++ b/Dream2/src/test/java/javareact/RemoteObservable.java @@ -16,13 +16,13 @@ public class RemoteObservable { public static void main(String[] args) { new RemoteObservable().start(); } - + public void start() { Consts.hostName = "def"; - + startServerIfNeeded(); startTokenServiceIfNeeded(); - + Var a = new Var<>("a", 1); for (int i = 0; i < 20; i++) { @@ -33,9 +33,9 @@ public void start() { } catch (InterruptedException e) { e.printStackTrace(); } - } + } } - + private final void startServerIfNeeded() { if (!serverStarted) { ServerLauncher.start(); diff --git a/Dream2/src/test/java/javareact/RemoteTest.java b/Dream2/src/test/java/javareact/RemoteTest.java index 399fe4e..bf87d17 100644 --- a/Dream2/src/test/java/javareact/RemoteTest.java +++ b/Dream2/src/test/java/javareact/RemoteTest.java @@ -34,13 +34,12 @@ public void remoteTest() { return a.get() * 2; }, a); - signal.change().addHandler((oldValue, newValue) -> assertTrue( signal.get().equals(a.get() * 2))); + signal.change().addHandler((oldValue, newValue) -> assertTrue(signal.get().equals(a.get() * 2))); for (int i = 0; i < 10; i++) { try { Thread.sleep(1000); - } - catch (InterruptedException ex) { + } catch (InterruptedException ex) { ex.printStackTrace(); } } diff --git a/Dream2/src/test/java/javareact/chat/ChatGUI.java b/Dream2/src/test/java/javareact/chat/ChatGUI.java index 65f1c55..197ffe8 100644 --- a/Dream2/src/test/java/javareact/chat/ChatGUI.java +++ b/Dream2/src/test/java/javareact/chat/ChatGUI.java @@ -18,26 +18,26 @@ import javax.swing.SpringLayout; public class ChatGUI extends JFrame { - + private static final long serialVersionUID = 4659984914364067514L; private JTextArea msgs; private JTextField sendText; private JList statusList; - + private Chat listener; - + public ChatGUI(String userName) { initUI(userName); } - + public void setListener(Chat c) { listener = c; } - + public String getTypedText() { return sendText.getText(); } - + public void resetTypedText() { sendText.setText(""); } @@ -48,7 +48,7 @@ public void displayMessage(String text) { else msgs.append(System.lineSeparator() + text); } - + private void initUI(String userName) { sendText = new JTextField(20); sendText.addKeyListener(new KeyListener() { diff --git a/Dream2/src/test/java/javareact/financial/FinancialApp.java b/Dream2/src/test/java/javareact/financial/FinancialApp.java index 64b898f..f7c0709 100644 --- a/Dream2/src/test/java/javareact/financial/FinancialApp.java +++ b/Dream2/src/test/java/javareact/financial/FinancialApp.java @@ -6,10 +6,10 @@ import javareact.common.types.Signal; public class FinancialApp implements ChangeEventHandler { - //private Signal f1Signal; - //private Signal f2Signal; - //private Signal f3Signal; - + // private Signal f1Signal; + // private Signal f2Signal; + // private Signal f3Signal; + private RemoteVar f1; private RemoteVar f2; private RemoteVar f3; @@ -17,7 +17,7 @@ public class FinancialApp implements ChangeEventHandler { public static void main(String[] args) { new FinancialApp().start(); } - + public void start() { Consts.hostName = "Local"; @@ -30,7 +30,7 @@ public void start() { f1 = new RemoteVar<>("f1@Model1"); f2 = new RemoteVar<>("f2@Model2"); f3 = new RemoteVar<>("f3@Model3"); - + RemoteVar model1 = new RemoteVar<>("Model1", "model1"); Signal f1Signal = new Signal<>("f1Signal", () -> f1.get(), f1); @@ -40,33 +40,34 @@ public void start() { f1Signal.change().addHandler(this); f2Signal.change().addHandler(this); f3Signal.change().addHandler(this); - + try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } - //System.out.println(f1Signal.get()); - //System.out.println(f2Signal.get()); - //System.out.println(f3Signal.get()); - + // System.out.println(f1Signal.get()); + // System.out.println(f2Signal.get()); + // System.out.println(f3Signal.get()); + System.out.println(f1.get()); System.out.println(f2.get()); System.out.println(f3.get()); - + System.out.println(model1.get()); } @Override public void handle(Integer oldValue, Integer newValue) { System.out.println("Value changed"); -// if (f1Signal.get() != null && f2Signal.get() != null && f3Signal.get() != null) { -// if ((f1Signal.get() + f2Signal.get() + f3Signal.get()) / 3.0 > 150) { -// System.out.println("Financial Alert!"); -// } -// } - + // if (f1Signal.get() != null && f2Signal.get() != null && + // f3Signal.get() != null) { + // if ((f1Signal.get() + f2Signal.get() + f3Signal.get()) / 3.0 > 150) { + // System.out.println("Financial Alert!"); + // } + // } + if (f1.get() != null && f2.get() != null && f3.get() != null) { if ((f1.get() + f2.get() + f3.get()) / 3.0 > 150) { System.out.println("Financial Alert!"); diff --git a/Dream2/src/test/java/javareact/financial/InputModel.java b/Dream2/src/test/java/javareact/financial/InputModel.java index e3ba065..51d07ed 100644 --- a/Dream2/src/test/java/javareact/financial/InputModel.java +++ b/Dream2/src/test/java/javareact/financial/InputModel.java @@ -12,38 +12,38 @@ public class InputModel { private boolean serverStarted = false; private boolean tokenServiceStarted = false; - + public static void main(String[] args) { new InputModel().start(); } - + public void start() { startServerIfNeeded(); startTokenServiceIfNeeded(); - + Consts.hostName = "InputModel"; - + Var marketIndex = new Var<>("marketIndex", 1); - Var stockOpts = new Var<>("stockOpts", 1); - Var news = new Var<>("news", 1); - - Random random = new Random(); + Var stockOpts = new Var<>("stockOpts", 1); + Var news = new Var<>("news", 1); - while (true) { - marketIndex.set(random.nextInt(100)); - stockOpts.set(random.nextInt(100)); - news.set(random.nextInt(100)); - - System.out.println("New values: " + marketIndex.get() + ", " + stockOpts.get() + ", " + news.get()); + Random random = new Random(); - try { - Thread.sleep(1000); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } + while (true) { + marketIndex.set(random.nextInt(100)); + stockOpts.set(random.nextInt(100)); + news.set(random.nextInt(100)); + + System.out.println("New values: " + marketIndex.get() + ", " + stockOpts.get() + ", " + news.get()); + + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } } - + private final void startServerIfNeeded() { if (!serverStarted) { ServerLauncher.start(); diff --git a/Dream2/src/test/java/javareact/financial/Model1.java b/Dream2/src/test/java/javareact/financial/Model1.java index 37c515b..be67b55 100644 --- a/Dream2/src/test/java/javareact/financial/Model1.java +++ b/Dream2/src/test/java/javareact/financial/Model1.java @@ -7,20 +7,21 @@ public class Model1 { public void start() { Consts.hostName = "Model1"; - + RemoteVar marketIndex = new RemoteVar<>("InputModel", "marketIndex"); RemoteVar stockOpts = new RemoteVar<>("InputModel", "stockOpts"); - - - + Signal f1 = new Signal<>("f1", () -> { - if (marketIndex.get() == null || stockOpts.get() == null) { return null; } - else { return marketIndex.get() * 2 + stockOpts.get(); - } }, marketIndex, stockOpts); - + if (marketIndex.get() == null || stockOpts.get() == null) { + return null; + } else { + return marketIndex.get() * 2 + stockOpts.get(); + } + }, marketIndex, stockOpts); + f1.change().addHandler((oldValue, newValue) -> System.out.println("New value for f1: " + newValue)); } - + public static void main(String[] args) { new Model1().start(); } diff --git a/Dream2/src/test/java/javareact/financial/Model2.java b/Dream2/src/test/java/javareact/financial/Model2.java index 527bf13..7d4cc06 100644 --- a/Dream2/src/test/java/javareact/financial/Model2.java +++ b/Dream2/src/test/java/javareact/financial/Model2.java @@ -12,13 +12,16 @@ public void start() { RemoteVar stockOpts = new RemoteVar<>("InputModel", "stockOpts"); Signal f2 = new Signal<>("f2", () -> { - if (marketIndex.get() == null || stockOpts.get() == null) { return null; } - else { return marketIndex.get() + stockOpts.get() * 2; } + if (marketIndex.get() == null || stockOpts.get() == null) { + return null; + } else { + return marketIndex.get() + stockOpts.get() * 2; + } }, marketIndex, stockOpts); - + f2.change().addHandler((oldValue, newValue) -> System.out.println("New value for f2: " + newValue)); } - + public static void main(String[] args) { new Model2().start(); } diff --git a/Dream2/src/test/java/javareact/financial/Model3.java b/Dream2/src/test/java/javareact/financial/Model3.java index 796a467..d0c65f9 100644 --- a/Dream2/src/test/java/javareact/financial/Model3.java +++ b/Dream2/src/test/java/javareact/financial/Model3.java @@ -7,15 +7,18 @@ public class Model3 { public void start() { Consts.hostName = "Model3"; - + RemoteVar marketIndex = new RemoteVar<>("InputModel", "marketIndex"); RemoteVar news = new RemoteVar<>("InputModel", "news"); Signal f3 = new Signal<>("f3", () -> { - if (marketIndex.get() == null || news.get() == null) { return null; } - else { return marketIndex.get() + news.get(); } + if (marketIndex.get() == null || news.get() == null) { + return null; + } else { + return marketIndex.get() + news.get(); + } }, marketIndex, news); - + f3.change().addHandler((oldValue, newValue) -> System.out.println("New value for f3: " + newValue)); } diff --git a/Dream2/src/test/java/javareact/server/DependencyDetectorTest.java b/Dream2/src/test/java/javareact/server/DependencyDetectorTest.java index 8d5e281..03723bf 100755 --- a/Dream2/src/test/java/javareact/server/DependencyDetectorTest.java +++ b/Dream2/src/test/java/javareact/server/DependencyDetectorTest.java @@ -17,821 +17,821 @@ public class DependencyDetectorTest { - @Test - public void noDependencyTest() { - // B = f(A) - // D = f(B, C) - DependencyDetector depDetector = new DependencyDetector(); - - Subscription subA = new Subscription("Host", "A", UUID.randomUUID()); - Subscription subB = new Subscription("Host", "B", UUID.randomUUID()); - Subscription subC = new Subscription("Host", "C", UUID.randomUUID()); - - Advertisement advB = new Advertisement("Host", "B"); - Advertisement advD = new Advertisement("Host", "D"); - - // Subscription to A (A generates B) - Set subsB = new HashSet(); - subsB.add(subA); - AdvertisementPacket advPktA = new AdvertisementPacket(advB, AdvType.ADV, subsB, true); - depDetector.processAdvertisementPacket(advPktA); - - // Subscription to B and C (B, C generate D) - Set subsD = new HashSet(); - subsD.add(subB); - subsD.add(subC); - AdvertisementPacket advPktD = new AdvertisementPacket(advD, AdvType.ADV, subsD, true); - depDetector.processAdvertisementPacket(advPktD); - - // Consolidate - depDetector.consolidate(); - - // Event A - Set computedFromA = new HashSet(); - Event evA = new Event("Host", "A"); - assertEquals(depDetector.getWaitRecommendations(evA, computedFromA).size(), 0); - - // Event B - Set computedFromB = new HashSet(); - Event evB = new Event("Host", "B"); - assertEquals(depDetector.getWaitRecommendations(evB, computedFromB).size(), 0); - - // Event C - Set computedFromC = new HashSet(); - Event evC = new Event("Host", "C"); - assertEquals(depDetector.getWaitRecommendations(evC, computedFromC).size(), 0); - - // Event D - Set computedFromD = new HashSet(); - Event evD = new Event("Host", "D"); - assertEquals(depDetector.getWaitRecommendations(evD, computedFromD).size(), 0); - } - - @Test - public void basicTriangularCycleTest() { - // B = f(A) - // C = f(A, B) - DependencyDetector depDetector = new DependencyDetector(); - - Subscription subA = new Subscription("Host", "A", UUID.randomUUID()); - Subscription subB = new Subscription("Host", "B", UUID.randomUUID()); - - Advertisement advB = new Advertisement("Host", "B"); - Advertisement advC = new Advertisement("Host", "C"); - - // Subscription to A (A generates B) - Set subsB = new HashSet(); - subsB.add(subA); - AdvertisementPacket advPktA = new AdvertisementPacket(advB, AdvType.ADV, subsB, true); - depDetector.processAdvertisementPacket(advPktA); - - // Subscription to A, B (A, B generates C) - Set subsC = new HashSet(); - subsC.add(subA); - subsC.add(subB); - AdvertisementPacket advPktC = new AdvertisementPacket(advC, AdvType.ADV, subsC, true); - depDetector.processAdvertisementPacket(advPktC); - - // Consolidate - depDetector.consolidate(); - - // Event A - Set computedFromA = new HashSet(); - Event evA = new Event("Host", "A"); - assertEquals(depDetector.getWaitRecommendations(evA, computedFromA).size(), 0); - computedFromA.add("Host.A"); - Event evA2 = new Event("Host", "A"); - assertEquals(depDetector.getWaitRecommendations(evA2, computedFromA).size(), 1); - for (WaitRecommendations wr : depDetector.getWaitRecommendations(evA2, computedFromA)) { - assertTrue(wr.getExpression().equals("Host.C")); - assertEquals(wr.getRecommendations().size(), 1); - assertTrue(wr.getRecommendations().contains("Host.B")); - } - - // Event B - Set computedFromB = new HashSet(); - Event evB1 = new Event("Host", "B"); - assertEquals(depDetector.getWaitRecommendations(evB1, computedFromB).size(), 0); - computedFromB.add("Host.A"); - Event evB2 = new Event("Host", "B"); - assertEquals(depDetector.getWaitRecommendations(evB2, computedFromB).size(), 1); - for (WaitRecommendations wr : depDetector.getWaitRecommendations(evB2, computedFromB)) { - assertTrue(wr.getExpression().equals("Host.C")); - assertEquals(wr.getRecommendations().size(), 1); - assertTrue(wr.getRecommendations().contains("Host.A")); - } - - // Event C - Set computedFromC = new HashSet(); - Event evC = new Event("Host", "C"); - assertEquals(depDetector.getWaitRecommendations(evC, computedFromC).size(), 0); - } - - @Test - public void basicDualCycleTest() { - // B = f(A) - // C = f(A) - // D = f(B, C) - DependencyDetector depDetector = new DependencyDetector(); - - Subscription subA = new Subscription("Host", "A", UUID.randomUUID()); - Subscription subB = new Subscription("Host", "B", UUID.randomUUID()); - Subscription subC = new Subscription("Host", "C", UUID.randomUUID()); - - Advertisement advB = new Advertisement("Host", "B"); - Advertisement advC = new Advertisement("Host", "C"); - Advertisement advD = new Advertisement("Host", "D"); - - // Subscription to A (A generates B) - Set subsB = new HashSet(); - subsB.add(subA); - AdvertisementPacket advPktA = new AdvertisementPacket(advB, AdvType.ADV, subsB, true); - depDetector.processAdvertisementPacket(advPktA); - - // Subscription to A (A generates C) - Set subsC = new HashSet(); - subsC.add(subA); - AdvertisementPacket advPktC = new AdvertisementPacket(advC, AdvType.ADV, subsC, true); - depDetector.processAdvertisementPacket(advPktC); - - // Subscription to B, C (B, C generate D) - Set subsD = new HashSet(); - subsD.add(subB); - subsD.add(subC); - AdvertisementPacket advPktD = new AdvertisementPacket(advD, AdvType.ADV, subsD, true); - depDetector.processAdvertisementPacket(advPktD); - - // Consolidate - depDetector.consolidate(); - - // Event A - Set computedFromA = new HashSet(); - Event evA = new Event("Host", "A"); - assertEquals(depDetector.getWaitRecommendations(evA, computedFromA).size(), 0); - - // Event B - Set computedFromB = new HashSet(); - Event evB1 = new Event("Host", "B"); - assertEquals(depDetector.getWaitRecommendations(evB1, computedFromB).size(), 0); - computedFromB.add("Host.A"); - Event evB2 = new Event("Host", "B"); - assertEquals(depDetector.getWaitRecommendations(evB2, computedFromB).size(), 1); - for (WaitRecommendations wr : depDetector.getWaitRecommendations(evB2, computedFromB)) { - assertTrue(wr.getExpression().equals("Host.D")); - assertEquals(wr.getRecommendations().size(), 1); - assertTrue(wr.getRecommendations().contains("Host.C")); - } - - // Event C - Set computedFromC = new HashSet(); - Event evC1 = new Event("Host", "C"); - assertEquals(depDetector.getWaitRecommendations(evC1, computedFromC).size(), 0); - computedFromC.add("Host.A"); - Event evC2 = new Event("Host", "C"); - assertEquals(depDetector.getWaitRecommendations(evC2, computedFromC).size(), 1); - for (WaitRecommendations wr : depDetector.getWaitRecommendations(evC2, computedFromC)) { - assertTrue(wr.getExpression().equals("Host.D")); - assertEquals(wr.getRecommendations().size(), 1); - assertTrue(wr.getRecommendations().contains("Host.B")); - } - - // Event D - Set computedFromD = new HashSet(); - Event evD = new Event("Host", "D"); - assertEquals(depDetector.getWaitRecommendations(evD, computedFromD).size(), 0); - } - - @Test - public void basicDualCycleTest2() { - // B = f(A) - // C = f(A) - // D = f(C) - // E = f(B, D) - DependencyDetector depDetector = new DependencyDetector(); - - Subscription subA = new Subscription("Host", "A", UUID.randomUUID()); - Subscription subB = new Subscription("Host", "B", UUID.randomUUID()); - Subscription subC = new Subscription("Host", "C", UUID.randomUUID()); - Subscription subD = new Subscription("Host", "D", UUID.randomUUID()); - - Advertisement advB = new Advertisement("Host", "B"); - Advertisement advC = new Advertisement("Host", "C"); - Advertisement advD = new Advertisement("Host", "D"); - Advertisement advE = new Advertisement("Host", "E"); - - // Subscription to A (A generates B) - Set subsB = new HashSet(); - subsB.add(subA); - AdvertisementPacket advPktA = new AdvertisementPacket(advB, AdvType.ADV, subsB, true); - depDetector.processAdvertisementPacket(advPktA); - - // Subscription to A (A generates C) - Set subsC = new HashSet(); - subsC.add(subA); - AdvertisementPacket advPktC = new AdvertisementPacket(advC, AdvType.ADV, subsC, true); - depDetector.processAdvertisementPacket(advPktC); - - // Subscription to C (C generate D) - Set subsD = new HashSet(); - subsD.add(subC); - AdvertisementPacket advPktD = new AdvertisementPacket(advD, AdvType.ADV, subsD, true); - depDetector.processAdvertisementPacket(advPktD); - - // Subscription to B, D (B, D generate E) - Set subsE = new HashSet(); - subsE.add(subB); - subsE.add(subD); - AdvertisementPacket advPktE = new AdvertisementPacket(advE, AdvType.ADV, subsE, true); - depDetector.processAdvertisementPacket(advPktE); - - // Consolidate - depDetector.consolidate(); - - // Event A - Set computedFromA = new HashSet(); - Event evA = new Event("Host", "A"); - assertEquals(depDetector.getWaitRecommendations(evA, computedFromA).size(), 0); - - // Event B - Set computedFromB = new HashSet(); - Event evB1 = new Event("Host", "B"); - assertEquals(depDetector.getWaitRecommendations(evB1, computedFromB).size(), 0); - computedFromB.add("Host.A"); - Event evB2 = new Event("Host", "B"); - assertEquals(depDetector.getWaitRecommendations(evB2, computedFromB).size(), 1); - for (WaitRecommendations wr : depDetector.getWaitRecommendations(evB2, computedFromB)) { - assertTrue(wr.getExpression().equals("Host.E")); - assertEquals(wr.getRecommendations().size(), 1); - assertTrue(wr.getRecommendations().contains("Host.D")); - } - - // Event C - Set computedFromC = new HashSet(); - Event evC1 = new Event("Host", "C"); - assertEquals(depDetector.getWaitRecommendations(evC1, computedFromC).size(), 0); - computedFromC.add("Host.A"); - Event evC2 = new Event("Host", "C"); - assertEquals(depDetector.getWaitRecommendations(evC2, computedFromC).size(), 0); - - // Event D - Set computedFromD = new HashSet(); - Event evD1 = new Event("Host", "D"); - assertEquals(depDetector.getWaitRecommendations(evD1, computedFromD).size(), 0); - computedFromD.add("Host.A"); - Event evD2 = new Event("Host", "D"); - assertEquals(depDetector.getWaitRecommendations(evD2, computedFromC).size(), 1); - for (WaitRecommendations wr : depDetector.getWaitRecommendations(evD2, computedFromC)) { - assertTrue(wr.getExpression().equals("Host.E")); - assertEquals(wr.getRecommendations().size(), 1); - assertTrue(wr.getRecommendations().contains("Host.B")); - } - - // Event E - Set computedFromE = new HashSet(); - Event evE = new Event("E", "Host"); - assertEquals(depDetector.getWaitRecommendations(evE, computedFromE).size(), 0); - } - - @Test - public void basicDualTriangle() { - // B = f(A) - // C = f(B) - // D = f(B, C) - DependencyDetector depDetector = new DependencyDetector(); - - Subscription subA = new Subscription("Host", "A", UUID.randomUUID()); - Subscription subB = new Subscription("Host", "B", UUID.randomUUID()); - Subscription subC = new Subscription("Host", "C", UUID.randomUUID()); - - Advertisement advB = new Advertisement("Host", "B"); - Advertisement advC = new Advertisement("Host", "C"); - Advertisement advD = new Advertisement("Host", "D"); - - // Subscription to A (A generates B) - Set subsB = new HashSet(); - subsB.add(subA); - AdvertisementPacket advPktA = new AdvertisementPacket(advB, AdvType.ADV, subsB, true); - depDetector.processAdvertisementPacket(advPktA); - - // Subscription to B (B generates C) - Set subsC = new HashSet(); - subsC.add(subB); - AdvertisementPacket advPktC = new AdvertisementPacket(advC, AdvType.ADV, subsC, true); - depDetector.processAdvertisementPacket(advPktC); - - // Subscription to B, C (B, C generate D) - Set subsD = new HashSet(); - subsD.add(subB); - subsD.add(subC); - AdvertisementPacket advPktD = new AdvertisementPacket(advD, AdvType.ADV, subsD, true); - depDetector.processAdvertisementPacket(advPktD); - - // Consolidate - depDetector.consolidate(); - - // Event A - Set computedFromA = new HashSet(); - Event evA = new Event("Host", "A"); - assertEquals(depDetector.getWaitRecommendations(evA, computedFromA).size(), 0); - - // Event B - Set computedFromB = new HashSet(); - Event evB1 = new Event("Host", "B"); - assertEquals(depDetector.getWaitRecommendations(evB1, computedFromB).size(), 0); - computedFromB.add("Host.B"); - Event evB2 = new Event("Host", "B"); - assertEquals(depDetector.getWaitRecommendations(evB2, computedFromB).size(), 1); - for (WaitRecommendations wr : depDetector.getWaitRecommendations(evB2, computedFromB)) { - assertTrue(wr.getExpression().equals("Host.D")); - assertEquals(wr.getRecommendations().size(), 1); - assertTrue(wr.getRecommendations().contains("Host.C")); - } - - // Event C - Set computedFromC = new HashSet(); - Event evC1 = new Event("Host", "C"); - assertEquals(depDetector.getWaitRecommendations(evC1, computedFromC).size(), 0); - computedFromC.add("Host.B"); - Event evC2 = new Event("Host", "C"); - assertEquals(depDetector.getWaitRecommendations(evC2, computedFromC).size(), 1); - for (WaitRecommendations wr : depDetector.getWaitRecommendations(evC2, computedFromB)) { - assertTrue(wr.getExpression().equals("Host.D")); - assertEquals(wr.getRecommendations().size(), 1); - assertTrue(wr.getRecommendations().contains("Host.B")); - } - - // Event D - Set computedFromD = new HashSet(); - Event evD1 = new Event("Host", "D"); - assertEquals(depDetector.getWaitRecommendations(evD1, computedFromD).size(), 0); - computedFromD.add("Host.A"); - Event evD2 = new Event("Host", "D"); - assertEquals(depDetector.getWaitRecommendations(evD2, computedFromC).size(), 0); - } - - @Test - public void basicTripleCycleTest() { - // B = f(A) - // C = f(A) - // D = f(A) - // E = f(B, C, D) - DependencyDetector depDetector = new DependencyDetector(); - - Subscription subA = new Subscription("Host", "A", UUID.randomUUID()); - Subscription subB = new Subscription("Host", "B", UUID.randomUUID()); - Subscription subC = new Subscription("Host", "C", UUID.randomUUID()); - Subscription subD = new Subscription("Host", "D", UUID.randomUUID()); - - Advertisement advB = new Advertisement("Host", "B"); - Advertisement advC = new Advertisement("Host", "C"); - Advertisement advD = new Advertisement("Host", "D"); - Advertisement advE = new Advertisement("Host", "E"); - - // Subscription to A (A generates B) - Set subsB = new HashSet(); - subsB.add(subA); - AdvertisementPacket advPktA = new AdvertisementPacket(advB, AdvType.ADV, subsB, true); - depDetector.processAdvertisementPacket(advPktA); - - // Subscription to A (A generates C) - Set subsC = new HashSet(); - subsC.add(subA); - AdvertisementPacket advPktC = new AdvertisementPacket(advC, AdvType.ADV, subsC, true); - depDetector.processAdvertisementPacket(advPktC); - - // Subscription to A (A generates D) - Set subsD = new HashSet(); - subsD.add(subA); - AdvertisementPacket advPktD = new AdvertisementPacket(advD, AdvType.ADV, subsD, true); - depDetector.processAdvertisementPacket(advPktD); - - // Subscription to B, C, D (B, C, D generate E) - Set subsE = new HashSet(); - subsE.add(subB); - subsE.add(subC); - subsE.add(subD); - AdvertisementPacket advPktE = new AdvertisementPacket(advE, AdvType.ADV, subsE, true); - depDetector.processAdvertisementPacket(advPktE); - - // Consolidate - depDetector.consolidate(); - - // Event A - Set computedFromA = new HashSet(); - Event evA = new Event("Host", "A"); - assertEquals(depDetector.getWaitRecommendations(evA, computedFromA).size(), 0); - - // Event B - Set computedFromB = new HashSet(); - Event evB1 = new Event("Host", "B"); - assertEquals(depDetector.getWaitRecommendations(evB1, computedFromB).size(), 0); - computedFromB.add("Host.A"); - Event evB2 = new Event("Host", "B"); - assertEquals(depDetector.getWaitRecommendations(evB2, computedFromB).size(), 1); - for (WaitRecommendations wr : depDetector.getWaitRecommendations(evB2, computedFromB)) { - assertTrue(wr.getExpression().equals("Host.E")); - assertEquals(wr.getRecommendations().size(), 2); - assertTrue(wr.getRecommendations().contains("Host.C")); - assertTrue(wr.getRecommendations().contains("Host.D")); - } - - // Event C - Set computedFromC = new HashSet(); - Event evC1 = new Event("Host", "C"); - assertEquals(depDetector.getWaitRecommendations(evC1, computedFromC).size(), 0); - computedFromC.add("Host.A"); - Event evC2 = new Event("Host", "C"); - assertEquals(depDetector.getWaitRecommendations(evC2, computedFromC).size(), 1); - for (WaitRecommendations wr : depDetector.getWaitRecommendations(evC2, computedFromC)) { - assertTrue(wr.getExpression().equals("Host.E")); - assertEquals(wr.getRecommendations().size(), 2); - assertTrue(wr.getRecommendations().contains("Host.B")); - assertTrue(wr.getRecommendations().contains("Host.D")); - } - - // Event D - Set computedFromD = new HashSet(); - Event evD1 = new Event("Host", "D"); - assertEquals(depDetector.getWaitRecommendations(evD1, computedFromD).size(), 0); - computedFromD.add("Host.A"); - Event evD2 = new Event("Host", "D"); - assertEquals(depDetector.getWaitRecommendations(evD2, computedFromD).size(), 1); - for (WaitRecommendations wr : depDetector.getWaitRecommendations(evD2, computedFromD)) { - assertTrue(wr.getExpression().equals("Host.E")); - assertEquals(wr.getRecommendations().size(), 2); - assertTrue(wr.getRecommendations().contains("Host.B")); - assertTrue(wr.getRecommendations().contains("Host.C")); - } - - // Event E - Set computedFromE = new HashSet(); - Event evE = new Event("Host", "E"); - assertEquals(depDetector.getWaitRecommendations(evE, computedFromE).size(), 0); - } - - @Test - public void dualCyclesTest() { - // B1 = f(A1) - // C1 = f(A1) - // B2 = f(A2) - // C2 = f(A2) - // D = f(B1, C1, B2, C2) - DependencyDetector depDetector = new DependencyDetector(); - - Subscription subA1 = new Subscription("Host", "A1", UUID.randomUUID()); - Subscription subA2 = new Subscription("Host", "A2", UUID.randomUUID()); - Subscription subB1 = new Subscription("Host", "B1", UUID.randomUUID()); - Subscription subB2 = new Subscription("Host", "B2", UUID.randomUUID()); - Subscription subC1 = new Subscription("Host", "C1", UUID.randomUUID()); - Subscription subC2 = new Subscription("Host", "C2", UUID.randomUUID()); - - Advertisement advB1 = new Advertisement("Host", "B1"); - Advertisement advB2 = new Advertisement("Host", "B2"); - Advertisement advC1 = new Advertisement("Host", "C1"); - Advertisement advC2 = new Advertisement("Host", "C2"); - Advertisement advD = new Advertisement("Host", "D"); - - // Subscription to A1 (A1 generates B2) - Set subsB1 = new HashSet(); - subsB1.add(subA1); - AdvertisementPacket advPktA1 = new AdvertisementPacket(advB1, AdvType.ADV, subsB1, true); - depDetector.processAdvertisementPacket(advPktA1); - - // Subscription to A2 (A2 generates B2) - Set subsB2 = new HashSet(); - subsB2.add(subA2); - AdvertisementPacket advPktA2 = new AdvertisementPacket(advB2, AdvType.ADV, subsB2, true); - depDetector.processAdvertisementPacket(advPktA2); - - // Subscription to A1 (A1 generates C1) - Set subsC1 = new HashSet(); - subsC1.add(subA1); - AdvertisementPacket advPktC1 = new AdvertisementPacket(advC1, AdvType.ADV, subsC1, true); - depDetector.processAdvertisementPacket(advPktC1); - - // Subscription to A2 (A2 generates C2) - Set subsC2 = new HashSet(); - subsC2.add(subA2); - AdvertisementPacket advPktC2 = new AdvertisementPacket(advC2, AdvType.ADV, subsC2, true); - depDetector.processAdvertisementPacket(advPktC2); - - // Subscription to B1, B2, C1, C2 (B1, B2, C1, C2 generate D) - Set subsD = new HashSet(); - subsD.add(subB1); - subsD.add(subB2); - subsD.add(subC1); - subsD.add(subC2); - AdvertisementPacket advPktD = new AdvertisementPacket(advD, AdvType.ADV, subsD, true); - depDetector.processAdvertisementPacket(advPktD); - - // Consolidate - depDetector.consolidate(); - - // Event A1 - Set computedFromA1 = new HashSet(); - Event evA1 = new Event("Host", "A1"); - assertEquals(depDetector.getWaitRecommendations(evA1, computedFromA1).size(), 0); - - // Event A2 - Set computedFromA2 = new HashSet(); - Event evA2 = new Event("Host", "A2"); - assertEquals(depDetector.getWaitRecommendations(evA2, computedFromA2).size(), 0); - - // Event B1 - Set computedFromB1 = new HashSet(); - computedFromB1.add("Host.A1"); - Event evB1 = new Event("Host", "B1"); - assertEquals(depDetector.getWaitRecommendations(evB1, computedFromB1).size(), 1); - for (WaitRecommendations wr : depDetector.getWaitRecommendations(evB1, computedFromB1)) { - assertTrue(wr.getExpression().equals("Host.D")); - assertEquals(wr.getRecommendations().size(), 1); - assertTrue(wr.getRecommendations().contains("Host.C1")); - } - - // Event C1 - Set computedFromC1 = new HashSet(); - computedFromC1.add("Host.A1"); - Event evC1 = new Event("Host", "C1"); - assertEquals(depDetector.getWaitRecommendations(evC1, computedFromC1).size(), 1); - for (WaitRecommendations wr : depDetector.getWaitRecommendations(evC1, computedFromC1)) { - assertTrue(wr.getExpression().equals("Host.D")); - assertEquals(wr.getRecommendations().size(), 1); - assertTrue(wr.getRecommendations().contains("Host.B1")); - } - - // Event B2 - Set computedFromB2 = new HashSet(); - computedFromB2.add("Host.A2"); - Event evB2 = new Event("Host", "B2"); - assertEquals(depDetector.getWaitRecommendations(evB2, computedFromB2).size(), 1); - for (WaitRecommendations wr : depDetector.getWaitRecommendations(evB2, computedFromB2)) { - assertTrue(wr.getExpression().equals("Host.D")); - assertEquals(wr.getRecommendations().size(), 1); - assertTrue(wr.getRecommendations().contains("Host.C2")); - } - - // Event C2 - Set computedFromC2 = new HashSet(); - computedFromC2.add("Host.A2"); - Event evC2 = new Event("Host", "C2"); - assertEquals(depDetector.getWaitRecommendations(evC2, computedFromC2).size(), 1); - for (WaitRecommendations wr : depDetector.getWaitRecommendations(evC2, computedFromC2)) { - assertTrue(wr.getExpression().equals("Host.D")); - assertEquals(wr.getRecommendations().size(), 1); - assertTrue(wr.getRecommendations().contains("Host.B2")); - } - - // Event D - Set computedFromD = new HashSet(); - Event evD = new Event("Host", "D"); - assertEquals(depDetector.getWaitRecommendations(evD, computedFromD).size(), 0); - } - - @Test - public void dualDependencyTest() { - // B = f(A1) - // C = f(A1, A2) - // D = f(B, C) - DependencyDetector depDetector = new DependencyDetector(); - - Subscription subA1 = new Subscription("Host", "A1", UUID.randomUUID()); - Subscription subA2 = new Subscription("Host", "A2", UUID.randomUUID()); - Subscription subB = new Subscription("Host", "B", UUID.randomUUID()); - Subscription subC = new Subscription("Host", "C", UUID.randomUUID()); - - Advertisement advB = new Advertisement("Host", "B"); - Advertisement advC = new Advertisement("Host", "C"); - Advertisement advD = new Advertisement("Host", "D"); - - // Subscription to A1 (A1 generates B) - Set subsB = new HashSet(); - subsB.add(subA1); - AdvertisementPacket advPktA = new AdvertisementPacket(advB, AdvType.ADV, subsB, true); - depDetector.processAdvertisementPacket(advPktA); - - // Subscription to A1, A2 (A1, A2 generate C) - Set subsC = new HashSet(); - subsC.add(subA1); - subsC.add(subA2); - AdvertisementPacket advPktC = new AdvertisementPacket(advC, AdvType.ADV, subsC, true); - depDetector.processAdvertisementPacket(advPktC); - - // Subscription to D (B, C generate D) - Set subsD = new HashSet(); - subsD.add(subB); - subsD.add(subC); - AdvertisementPacket advPktD = new AdvertisementPacket(advD, AdvType.ADV, subsD, true); - depDetector.processAdvertisementPacket(advPktD); - - // Consolidate - depDetector.consolidate(); - - // Event A1 - Set computedFromA1 = new HashSet(); - Event evA1 = new Event("Host", "A1"); - assertEquals(depDetector.getWaitRecommendations(evA1, computedFromA1).size(), 0); - - // Event A2 - Set computedFromA2 = new HashSet(); - Event evA2 = new Event("Host", "A2"); - assertEquals(depDetector.getWaitRecommendations(evA2, computedFromA2).size(), 0); - - // Event B from A1 - Set computedFromB = new HashSet(); - Event evB1 = new Event("Host", "B"); - assertEquals(depDetector.getWaitRecommendations(evB1, computedFromB).size(), 0); - computedFromB.add("Host.A1"); - Event evB2 = new Event("Host", "B"); - assertEquals(depDetector.getWaitRecommendations(evB2, computedFromB).size(), 1); - for (WaitRecommendations wr : depDetector.getWaitRecommendations(evB2, computedFromB)) { - assertTrue(wr.getExpression().equals("Host.D")); - assertEquals(wr.getRecommendations().size(), 1); - assertTrue(wr.getRecommendations().contains("Host.C")); - } - - // Event C from A1 - Set computedFromC1 = new HashSet(); - Event evC1_1 = new Event("Host", "C"); - assertEquals(depDetector.getWaitRecommendations(evC1_1, computedFromC1).size(), 0); - computedFromC1.add("Host.A1"); - Event evC1_2 = new Event("Host", "C"); - assertEquals(depDetector.getWaitRecommendations(evC1_2, computedFromC1).size(), 1); - for (WaitRecommendations wr : depDetector.getWaitRecommendations(evC1_2, computedFromC1)) { - assertTrue(wr.getExpression().equals("Host.D")); - assertEquals(wr.getRecommendations().size(), 1); - assertTrue(wr.getRecommendations().contains("Host.B")); - } - - // Event C from A2 - Set computedFromC2 = new HashSet(); - Event evC2_1 = new Event("Host", "C"); - assertEquals(depDetector.getWaitRecommendations(evC2_1, computedFromC2).size(), 0); - computedFromC1.add("Host.A2"); - Event evC2_2 = new Event("Host", "C"); - assertEquals(depDetector.getWaitRecommendations(evC2_2, computedFromC2).size(), 0); - - // Event D - Set computedFromD = new HashSet(); - Event evD = new Event("Host", "D"); - assertEquals(depDetector.getWaitRecommendations(evD, computedFromD).size(), 0); - } - - @Test - public void nestedCyclesTest() { - // B = f(A) - // C = f(A) - // E = f(A) - // G = f(E) - // F = f(E) - // H = f(F, G) - // D = f(B, C, H) - DependencyDetector depDetector = new DependencyDetector(); - - Subscription subA = new Subscription("Host", "A", UUID.randomUUID()); - Subscription subB = new Subscription("Host", "B", UUID.randomUUID()); - Subscription subC = new Subscription("Host", "C", UUID.randomUUID()); - Subscription subE = new Subscription("Host", "E", UUID.randomUUID()); - Subscription subF = new Subscription("Host", "F", UUID.randomUUID()); - Subscription subG = new Subscription("Host", "G", UUID.randomUUID()); - Subscription subH = new Subscription("Host", "H", UUID.randomUUID()); - - Advertisement advB = new Advertisement("Host", "B"); - Advertisement advC = new Advertisement("Host", "C"); - Advertisement advD = new Advertisement("Host", "D"); - Advertisement advE = new Advertisement("Host", "E"); - Advertisement advF = new Advertisement("Host", "F"); - Advertisement advG = new Advertisement("Host", "G"); - Advertisement advH = new Advertisement("Host", "H"); - - // Subscription to A (A generates B) - Set subsB = new HashSet(); - subsB.add(subA); - AdvertisementPacket advPktA = new AdvertisementPacket(advB, AdvType.ADV, subsB, true); - depDetector.processAdvertisementPacket(advPktA); - - // Subscription to A (A generates C) - Set subsC = new HashSet(); - subsC.add(subA); - AdvertisementPacket advPktC = new AdvertisementPacket(advC, AdvType.ADV, subsC, true); - depDetector.processAdvertisementPacket(advPktC); - - // Subscription to A (A generates E) - Set subsE = new HashSet(); - subsE.add(subA); - AdvertisementPacket advPktE = new AdvertisementPacket(advE, AdvType.ADV, subsE, true); - depDetector.processAdvertisementPacket(advPktE); - - // Subscription to E (E generates G) - Set subsG = new HashSet(); - subsG.add(subE); - AdvertisementPacket advPktG = new AdvertisementPacket(advG, AdvType.ADV, subsG, true); - depDetector.processAdvertisementPacket(advPktG); - - // Subscription to E (E generates F) - Set subsF = new HashSet(); - subsF.add(subE); - AdvertisementPacket advPktF = new AdvertisementPacket(advF, AdvType.ADV, subsF, true); - depDetector.processAdvertisementPacket(advPktF); - - // Subscription to F, G (F, G generate H) - Set subsH = new HashSet(); - subsH.add(subF); - subsH.add(subG); - AdvertisementPacket advPktH = new AdvertisementPacket(advH, AdvType.ADV, subsH, true); - depDetector.processAdvertisementPacket(advPktH); - - // Subscription to B, C, H (B, C, H generate D) - Set subsD = new HashSet(); - subsD.add(subB); - subsD.add(subC); - subsD.add(subH); - AdvertisementPacket subPktD = new AdvertisementPacket(advD, AdvType.ADV, subsD, true); - depDetector.processAdvertisementPacket(subPktD); - - // Consolidate - depDetector.consolidate(); - - // Event A - Set computedFromA = new HashSet(); - Event evA = new Event("Host", "A"); - assertEquals(depDetector.getWaitRecommendations(evA, computedFromA).size(), 0); - - // Event B - Set computedFromB = new HashSet(); - Event evB1 = new Event("Host", "B"); - assertEquals(depDetector.getWaitRecommendations(evB1, computedFromB).size(), 0); - computedFromB.add("Host.A"); - Event evB2 = new Event("Host", "B"); - assertEquals(depDetector.getWaitRecommendations(evB2, computedFromB).size(), 1); - for (WaitRecommendations wr : depDetector.getWaitRecommendations(evB2, computedFromB)) { - assertTrue(wr.getExpression().equals("Host.D")); - assertEquals(wr.getRecommendations().size(), 2); - assertTrue(wr.getRecommendations().contains("Host.C")); - assertTrue(wr.getRecommendations().contains("Host.H")); - } - - // Event C - Set computedFromC = new HashSet(); - Event evC1 = new Event("Host", "C"); - assertEquals(depDetector.getWaitRecommendations(evC1, computedFromC).size(), 0); - computedFromC.add("Host.A"); - Event evC2 = new Event("Host", "C"); - assertEquals(depDetector.getWaitRecommendations(evC2, computedFromC).size(), 1); - for (WaitRecommendations wr : depDetector.getWaitRecommendations(evC2, computedFromC)) { - assertTrue(wr.getExpression().equals("Host.D")); - assertEquals(wr.getRecommendations().size(), 2); - assertTrue(wr.getRecommendations().contains("Host.B")); - assertTrue(wr.getRecommendations().contains("Host.H")); - } - - // Event E - Set computedFromE = new HashSet(); - Event evE = new Event("Host", "E"); - assertEquals(depDetector.getWaitRecommendations(evE, computedFromE).size(), 0); - - // Event F - Set computedFromF = new HashSet(); - Event evF1 = new Event("Host", "F"); - assertEquals(depDetector.getWaitRecommendations(evF1, computedFromF).size(), 0); - computedFromF.add("Host.E"); - Event evF2 = new Event("Host", "F"); - assertEquals(depDetector.getWaitRecommendations(evF2, computedFromF).size(), 1); - for (WaitRecommendations wr : depDetector.getWaitRecommendations(evF2, computedFromF)) { - assertTrue(wr.getExpression().equals("Host.H")); - assertEquals(wr.getRecommendations().size(), 1); - assertTrue(wr.getRecommendations().contains("Host.G")); - } - - // Event G - Set computedFromG = new HashSet(); - Event evG1 = new Event("Host", "G"); - assertEquals(depDetector.getWaitRecommendations(evG1, computedFromG).size(), 0); - computedFromG.add("Host.E"); - Event evG2 = new Event("Host", "G"); - assertEquals(depDetector.getWaitRecommendations(evG2, computedFromG).size(), 1); - for (WaitRecommendations wr : depDetector.getWaitRecommendations(evG2, computedFromG)) { - assertTrue(wr.getExpression().equals("Host.H")); - assertEquals(wr.getRecommendations().size(), 1); - assertTrue(wr.getRecommendations().contains("Host.F")); - } - - // Event H - Set computedFromH = new HashSet(); - Event evH1 = new Event("Host", "H"); - assertEquals(depDetector.getWaitRecommendations(evH1, computedFromH).size(), 0); - computedFromH.add("Host.A"); - Event evH2 = new Event("Host", "H"); - assertEquals(depDetector.getWaitRecommendations(evH2, computedFromH).size(), 1); - for (WaitRecommendations wr : depDetector.getWaitRecommendations(evH2, computedFromH)) { - assertTrue(wr.getExpression().equals("Host.D")); - assertEquals(wr.getRecommendations().size(), 2); - assertTrue(wr.getRecommendations().contains("Host.B")); - assertTrue(wr.getRecommendations().contains("Host.C")); - } - - // Event D - Set computedFromD = new HashSet(); - Event evD = new Event("Host", "D"); - assertEquals(depDetector.getWaitRecommendations(evD, computedFromD).size(), 0); - } + @Test + public void noDependencyTest() { + // B = f(A) + // D = f(B, C) + DependencyDetector depDetector = new DependencyDetector(); + + Subscription subA = new Subscription("Host", "A", UUID.randomUUID()); + Subscription subB = new Subscription("Host", "B", UUID.randomUUID()); + Subscription subC = new Subscription("Host", "C", UUID.randomUUID()); + + Advertisement advB = new Advertisement("Host", "B"); + Advertisement advD = new Advertisement("Host", "D"); + + // Subscription to A (A generates B) + Set subsB = new HashSet(); + subsB.add(subA); + AdvertisementPacket advPktA = new AdvertisementPacket(advB, AdvType.ADV, subsB, true); + depDetector.processAdvertisementPacket(advPktA); + + // Subscription to B and C (B, C generate D) + Set subsD = new HashSet(); + subsD.add(subB); + subsD.add(subC); + AdvertisementPacket advPktD = new AdvertisementPacket(advD, AdvType.ADV, subsD, true); + depDetector.processAdvertisementPacket(advPktD); + + // Consolidate + depDetector.consolidate(); + + // Event A + Set computedFromA = new HashSet(); + Event evA = new Event("Host", "A"); + assertEquals(depDetector.getWaitRecommendations(evA, computedFromA).size(), 0); + + // Event B + Set computedFromB = new HashSet(); + Event evB = new Event("Host", "B"); + assertEquals(depDetector.getWaitRecommendations(evB, computedFromB).size(), 0); + + // Event C + Set computedFromC = new HashSet(); + Event evC = new Event("Host", "C"); + assertEquals(depDetector.getWaitRecommendations(evC, computedFromC).size(), 0); + + // Event D + Set computedFromD = new HashSet(); + Event evD = new Event("Host", "D"); + assertEquals(depDetector.getWaitRecommendations(evD, computedFromD).size(), 0); + } + + @Test + public void basicTriangularCycleTest() { + // B = f(A) + // C = f(A, B) + DependencyDetector depDetector = new DependencyDetector(); + + Subscription subA = new Subscription("Host", "A", UUID.randomUUID()); + Subscription subB = new Subscription("Host", "B", UUID.randomUUID()); + + Advertisement advB = new Advertisement("Host", "B"); + Advertisement advC = new Advertisement("Host", "C"); + + // Subscription to A (A generates B) + Set subsB = new HashSet(); + subsB.add(subA); + AdvertisementPacket advPktA = new AdvertisementPacket(advB, AdvType.ADV, subsB, true); + depDetector.processAdvertisementPacket(advPktA); + + // Subscription to A, B (A, B generates C) + Set subsC = new HashSet(); + subsC.add(subA); + subsC.add(subB); + AdvertisementPacket advPktC = new AdvertisementPacket(advC, AdvType.ADV, subsC, true); + depDetector.processAdvertisementPacket(advPktC); + + // Consolidate + depDetector.consolidate(); + + // Event A + Set computedFromA = new HashSet(); + Event evA = new Event("Host", "A"); + assertEquals(depDetector.getWaitRecommendations(evA, computedFromA).size(), 0); + computedFromA.add("Host.A"); + Event evA2 = new Event("Host", "A"); + assertEquals(depDetector.getWaitRecommendations(evA2, computedFromA).size(), 1); + for (WaitRecommendations wr : depDetector.getWaitRecommendations(evA2, computedFromA)) { + assertTrue(wr.getExpression().equals("Host.C")); + assertEquals(wr.getRecommendations().size(), 1); + assertTrue(wr.getRecommendations().contains("Host.B")); + } + + // Event B + Set computedFromB = new HashSet(); + Event evB1 = new Event("Host", "B"); + assertEquals(depDetector.getWaitRecommendations(evB1, computedFromB).size(), 0); + computedFromB.add("Host.A"); + Event evB2 = new Event("Host", "B"); + assertEquals(depDetector.getWaitRecommendations(evB2, computedFromB).size(), 1); + for (WaitRecommendations wr : depDetector.getWaitRecommendations(evB2, computedFromB)) { + assertTrue(wr.getExpression().equals("Host.C")); + assertEquals(wr.getRecommendations().size(), 1); + assertTrue(wr.getRecommendations().contains("Host.A")); + } + + // Event C + Set computedFromC = new HashSet(); + Event evC = new Event("Host", "C"); + assertEquals(depDetector.getWaitRecommendations(evC, computedFromC).size(), 0); + } + + @Test + public void basicDualCycleTest() { + // B = f(A) + // C = f(A) + // D = f(B, C) + DependencyDetector depDetector = new DependencyDetector(); + + Subscription subA = new Subscription("Host", "A", UUID.randomUUID()); + Subscription subB = new Subscription("Host", "B", UUID.randomUUID()); + Subscription subC = new Subscription("Host", "C", UUID.randomUUID()); + + Advertisement advB = new Advertisement("Host", "B"); + Advertisement advC = new Advertisement("Host", "C"); + Advertisement advD = new Advertisement("Host", "D"); + + // Subscription to A (A generates B) + Set subsB = new HashSet(); + subsB.add(subA); + AdvertisementPacket advPktA = new AdvertisementPacket(advB, AdvType.ADV, subsB, true); + depDetector.processAdvertisementPacket(advPktA); + + // Subscription to A (A generates C) + Set subsC = new HashSet(); + subsC.add(subA); + AdvertisementPacket advPktC = new AdvertisementPacket(advC, AdvType.ADV, subsC, true); + depDetector.processAdvertisementPacket(advPktC); + + // Subscription to B, C (B, C generate D) + Set subsD = new HashSet(); + subsD.add(subB); + subsD.add(subC); + AdvertisementPacket advPktD = new AdvertisementPacket(advD, AdvType.ADV, subsD, true); + depDetector.processAdvertisementPacket(advPktD); + + // Consolidate + depDetector.consolidate(); + + // Event A + Set computedFromA = new HashSet(); + Event evA = new Event("Host", "A"); + assertEquals(depDetector.getWaitRecommendations(evA, computedFromA).size(), 0); + + // Event B + Set computedFromB = new HashSet(); + Event evB1 = new Event("Host", "B"); + assertEquals(depDetector.getWaitRecommendations(evB1, computedFromB).size(), 0); + computedFromB.add("Host.A"); + Event evB2 = new Event("Host", "B"); + assertEquals(depDetector.getWaitRecommendations(evB2, computedFromB).size(), 1); + for (WaitRecommendations wr : depDetector.getWaitRecommendations(evB2, computedFromB)) { + assertTrue(wr.getExpression().equals("Host.D")); + assertEquals(wr.getRecommendations().size(), 1); + assertTrue(wr.getRecommendations().contains("Host.C")); + } + + // Event C + Set computedFromC = new HashSet(); + Event evC1 = new Event("Host", "C"); + assertEquals(depDetector.getWaitRecommendations(evC1, computedFromC).size(), 0); + computedFromC.add("Host.A"); + Event evC2 = new Event("Host", "C"); + assertEquals(depDetector.getWaitRecommendations(evC2, computedFromC).size(), 1); + for (WaitRecommendations wr : depDetector.getWaitRecommendations(evC2, computedFromC)) { + assertTrue(wr.getExpression().equals("Host.D")); + assertEquals(wr.getRecommendations().size(), 1); + assertTrue(wr.getRecommendations().contains("Host.B")); + } + + // Event D + Set computedFromD = new HashSet(); + Event evD = new Event("Host", "D"); + assertEquals(depDetector.getWaitRecommendations(evD, computedFromD).size(), 0); + } + + @Test + public void basicDualCycleTest2() { + // B = f(A) + // C = f(A) + // D = f(C) + // E = f(B, D) + DependencyDetector depDetector = new DependencyDetector(); + + Subscription subA = new Subscription("Host", "A", UUID.randomUUID()); + Subscription subB = new Subscription("Host", "B", UUID.randomUUID()); + Subscription subC = new Subscription("Host", "C", UUID.randomUUID()); + Subscription subD = new Subscription("Host", "D", UUID.randomUUID()); + + Advertisement advB = new Advertisement("Host", "B"); + Advertisement advC = new Advertisement("Host", "C"); + Advertisement advD = new Advertisement("Host", "D"); + Advertisement advE = new Advertisement("Host", "E"); + + // Subscription to A (A generates B) + Set subsB = new HashSet(); + subsB.add(subA); + AdvertisementPacket advPktA = new AdvertisementPacket(advB, AdvType.ADV, subsB, true); + depDetector.processAdvertisementPacket(advPktA); + + // Subscription to A (A generates C) + Set subsC = new HashSet(); + subsC.add(subA); + AdvertisementPacket advPktC = new AdvertisementPacket(advC, AdvType.ADV, subsC, true); + depDetector.processAdvertisementPacket(advPktC); + + // Subscription to C (C generate D) + Set subsD = new HashSet(); + subsD.add(subC); + AdvertisementPacket advPktD = new AdvertisementPacket(advD, AdvType.ADV, subsD, true); + depDetector.processAdvertisementPacket(advPktD); + + // Subscription to B, D (B, D generate E) + Set subsE = new HashSet(); + subsE.add(subB); + subsE.add(subD); + AdvertisementPacket advPktE = new AdvertisementPacket(advE, AdvType.ADV, subsE, true); + depDetector.processAdvertisementPacket(advPktE); + + // Consolidate + depDetector.consolidate(); + + // Event A + Set computedFromA = new HashSet(); + Event evA = new Event("Host", "A"); + assertEquals(depDetector.getWaitRecommendations(evA, computedFromA).size(), 0); + + // Event B + Set computedFromB = new HashSet(); + Event evB1 = new Event("Host", "B"); + assertEquals(depDetector.getWaitRecommendations(evB1, computedFromB).size(), 0); + computedFromB.add("Host.A"); + Event evB2 = new Event("Host", "B"); + assertEquals(depDetector.getWaitRecommendations(evB2, computedFromB).size(), 1); + for (WaitRecommendations wr : depDetector.getWaitRecommendations(evB2, computedFromB)) { + assertTrue(wr.getExpression().equals("Host.E")); + assertEquals(wr.getRecommendations().size(), 1); + assertTrue(wr.getRecommendations().contains("Host.D")); + } + + // Event C + Set computedFromC = new HashSet(); + Event evC1 = new Event("Host", "C"); + assertEquals(depDetector.getWaitRecommendations(evC1, computedFromC).size(), 0); + computedFromC.add("Host.A"); + Event evC2 = new Event("Host", "C"); + assertEquals(depDetector.getWaitRecommendations(evC2, computedFromC).size(), 0); + + // Event D + Set computedFromD = new HashSet(); + Event evD1 = new Event("Host", "D"); + assertEquals(depDetector.getWaitRecommendations(evD1, computedFromD).size(), 0); + computedFromD.add("Host.A"); + Event evD2 = new Event("Host", "D"); + assertEquals(depDetector.getWaitRecommendations(evD2, computedFromC).size(), 1); + for (WaitRecommendations wr : depDetector.getWaitRecommendations(evD2, computedFromC)) { + assertTrue(wr.getExpression().equals("Host.E")); + assertEquals(wr.getRecommendations().size(), 1); + assertTrue(wr.getRecommendations().contains("Host.B")); + } + + // Event E + Set computedFromE = new HashSet(); + Event evE = new Event("E", "Host"); + assertEquals(depDetector.getWaitRecommendations(evE, computedFromE).size(), 0); + } + + @Test + public void basicDualTriangle() { + // B = f(A) + // C = f(B) + // D = f(B, C) + DependencyDetector depDetector = new DependencyDetector(); + + Subscription subA = new Subscription("Host", "A", UUID.randomUUID()); + Subscription subB = new Subscription("Host", "B", UUID.randomUUID()); + Subscription subC = new Subscription("Host", "C", UUID.randomUUID()); + + Advertisement advB = new Advertisement("Host", "B"); + Advertisement advC = new Advertisement("Host", "C"); + Advertisement advD = new Advertisement("Host", "D"); + + // Subscription to A (A generates B) + Set subsB = new HashSet(); + subsB.add(subA); + AdvertisementPacket advPktA = new AdvertisementPacket(advB, AdvType.ADV, subsB, true); + depDetector.processAdvertisementPacket(advPktA); + + // Subscription to B (B generates C) + Set subsC = new HashSet(); + subsC.add(subB); + AdvertisementPacket advPktC = new AdvertisementPacket(advC, AdvType.ADV, subsC, true); + depDetector.processAdvertisementPacket(advPktC); + + // Subscription to B, C (B, C generate D) + Set subsD = new HashSet(); + subsD.add(subB); + subsD.add(subC); + AdvertisementPacket advPktD = new AdvertisementPacket(advD, AdvType.ADV, subsD, true); + depDetector.processAdvertisementPacket(advPktD); + + // Consolidate + depDetector.consolidate(); + + // Event A + Set computedFromA = new HashSet(); + Event evA = new Event("Host", "A"); + assertEquals(depDetector.getWaitRecommendations(evA, computedFromA).size(), 0); + + // Event B + Set computedFromB = new HashSet(); + Event evB1 = new Event("Host", "B"); + assertEquals(depDetector.getWaitRecommendations(evB1, computedFromB).size(), 0); + computedFromB.add("Host.B"); + Event evB2 = new Event("Host", "B"); + assertEquals(depDetector.getWaitRecommendations(evB2, computedFromB).size(), 1); + for (WaitRecommendations wr : depDetector.getWaitRecommendations(evB2, computedFromB)) { + assertTrue(wr.getExpression().equals("Host.D")); + assertEquals(wr.getRecommendations().size(), 1); + assertTrue(wr.getRecommendations().contains("Host.C")); + } + + // Event C + Set computedFromC = new HashSet(); + Event evC1 = new Event("Host", "C"); + assertEquals(depDetector.getWaitRecommendations(evC1, computedFromC).size(), 0); + computedFromC.add("Host.B"); + Event evC2 = new Event("Host", "C"); + assertEquals(depDetector.getWaitRecommendations(evC2, computedFromC).size(), 1); + for (WaitRecommendations wr : depDetector.getWaitRecommendations(evC2, computedFromB)) { + assertTrue(wr.getExpression().equals("Host.D")); + assertEquals(wr.getRecommendations().size(), 1); + assertTrue(wr.getRecommendations().contains("Host.B")); + } + + // Event D + Set computedFromD = new HashSet(); + Event evD1 = new Event("Host", "D"); + assertEquals(depDetector.getWaitRecommendations(evD1, computedFromD).size(), 0); + computedFromD.add("Host.A"); + Event evD2 = new Event("Host", "D"); + assertEquals(depDetector.getWaitRecommendations(evD2, computedFromC).size(), 0); + } + + @Test + public void basicTripleCycleTest() { + // B = f(A) + // C = f(A) + // D = f(A) + // E = f(B, C, D) + DependencyDetector depDetector = new DependencyDetector(); + + Subscription subA = new Subscription("Host", "A", UUID.randomUUID()); + Subscription subB = new Subscription("Host", "B", UUID.randomUUID()); + Subscription subC = new Subscription("Host", "C", UUID.randomUUID()); + Subscription subD = new Subscription("Host", "D", UUID.randomUUID()); + + Advertisement advB = new Advertisement("Host", "B"); + Advertisement advC = new Advertisement("Host", "C"); + Advertisement advD = new Advertisement("Host", "D"); + Advertisement advE = new Advertisement("Host", "E"); + + // Subscription to A (A generates B) + Set subsB = new HashSet(); + subsB.add(subA); + AdvertisementPacket advPktA = new AdvertisementPacket(advB, AdvType.ADV, subsB, true); + depDetector.processAdvertisementPacket(advPktA); + + // Subscription to A (A generates C) + Set subsC = new HashSet(); + subsC.add(subA); + AdvertisementPacket advPktC = new AdvertisementPacket(advC, AdvType.ADV, subsC, true); + depDetector.processAdvertisementPacket(advPktC); + + // Subscription to A (A generates D) + Set subsD = new HashSet(); + subsD.add(subA); + AdvertisementPacket advPktD = new AdvertisementPacket(advD, AdvType.ADV, subsD, true); + depDetector.processAdvertisementPacket(advPktD); + + // Subscription to B, C, D (B, C, D generate E) + Set subsE = new HashSet(); + subsE.add(subB); + subsE.add(subC); + subsE.add(subD); + AdvertisementPacket advPktE = new AdvertisementPacket(advE, AdvType.ADV, subsE, true); + depDetector.processAdvertisementPacket(advPktE); + + // Consolidate + depDetector.consolidate(); + + // Event A + Set computedFromA = new HashSet(); + Event evA = new Event("Host", "A"); + assertEquals(depDetector.getWaitRecommendations(evA, computedFromA).size(), 0); + + // Event B + Set computedFromB = new HashSet(); + Event evB1 = new Event("Host", "B"); + assertEquals(depDetector.getWaitRecommendations(evB1, computedFromB).size(), 0); + computedFromB.add("Host.A"); + Event evB2 = new Event("Host", "B"); + assertEquals(depDetector.getWaitRecommendations(evB2, computedFromB).size(), 1); + for (WaitRecommendations wr : depDetector.getWaitRecommendations(evB2, computedFromB)) { + assertTrue(wr.getExpression().equals("Host.E")); + assertEquals(wr.getRecommendations().size(), 2); + assertTrue(wr.getRecommendations().contains("Host.C")); + assertTrue(wr.getRecommendations().contains("Host.D")); + } + + // Event C + Set computedFromC = new HashSet(); + Event evC1 = new Event("Host", "C"); + assertEquals(depDetector.getWaitRecommendations(evC1, computedFromC).size(), 0); + computedFromC.add("Host.A"); + Event evC2 = new Event("Host", "C"); + assertEquals(depDetector.getWaitRecommendations(evC2, computedFromC).size(), 1); + for (WaitRecommendations wr : depDetector.getWaitRecommendations(evC2, computedFromC)) { + assertTrue(wr.getExpression().equals("Host.E")); + assertEquals(wr.getRecommendations().size(), 2); + assertTrue(wr.getRecommendations().contains("Host.B")); + assertTrue(wr.getRecommendations().contains("Host.D")); + } + + // Event D + Set computedFromD = new HashSet(); + Event evD1 = new Event("Host", "D"); + assertEquals(depDetector.getWaitRecommendations(evD1, computedFromD).size(), 0); + computedFromD.add("Host.A"); + Event evD2 = new Event("Host", "D"); + assertEquals(depDetector.getWaitRecommendations(evD2, computedFromD).size(), 1); + for (WaitRecommendations wr : depDetector.getWaitRecommendations(evD2, computedFromD)) { + assertTrue(wr.getExpression().equals("Host.E")); + assertEquals(wr.getRecommendations().size(), 2); + assertTrue(wr.getRecommendations().contains("Host.B")); + assertTrue(wr.getRecommendations().contains("Host.C")); + } + + // Event E + Set computedFromE = new HashSet(); + Event evE = new Event("Host", "E"); + assertEquals(depDetector.getWaitRecommendations(evE, computedFromE).size(), 0); + } + + @Test + public void dualCyclesTest() { + // B1 = f(A1) + // C1 = f(A1) + // B2 = f(A2) + // C2 = f(A2) + // D = f(B1, C1, B2, C2) + DependencyDetector depDetector = new DependencyDetector(); + + Subscription subA1 = new Subscription("Host", "A1", UUID.randomUUID()); + Subscription subA2 = new Subscription("Host", "A2", UUID.randomUUID()); + Subscription subB1 = new Subscription("Host", "B1", UUID.randomUUID()); + Subscription subB2 = new Subscription("Host", "B2", UUID.randomUUID()); + Subscription subC1 = new Subscription("Host", "C1", UUID.randomUUID()); + Subscription subC2 = new Subscription("Host", "C2", UUID.randomUUID()); + + Advertisement advB1 = new Advertisement("Host", "B1"); + Advertisement advB2 = new Advertisement("Host", "B2"); + Advertisement advC1 = new Advertisement("Host", "C1"); + Advertisement advC2 = new Advertisement("Host", "C2"); + Advertisement advD = new Advertisement("Host", "D"); + + // Subscription to A1 (A1 generates B2) + Set subsB1 = new HashSet(); + subsB1.add(subA1); + AdvertisementPacket advPktA1 = new AdvertisementPacket(advB1, AdvType.ADV, subsB1, true); + depDetector.processAdvertisementPacket(advPktA1); + + // Subscription to A2 (A2 generates B2) + Set subsB2 = new HashSet(); + subsB2.add(subA2); + AdvertisementPacket advPktA2 = new AdvertisementPacket(advB2, AdvType.ADV, subsB2, true); + depDetector.processAdvertisementPacket(advPktA2); + + // Subscription to A1 (A1 generates C1) + Set subsC1 = new HashSet(); + subsC1.add(subA1); + AdvertisementPacket advPktC1 = new AdvertisementPacket(advC1, AdvType.ADV, subsC1, true); + depDetector.processAdvertisementPacket(advPktC1); + + // Subscription to A2 (A2 generates C2) + Set subsC2 = new HashSet(); + subsC2.add(subA2); + AdvertisementPacket advPktC2 = new AdvertisementPacket(advC2, AdvType.ADV, subsC2, true); + depDetector.processAdvertisementPacket(advPktC2); + + // Subscription to B1, B2, C1, C2 (B1, B2, C1, C2 generate D) + Set subsD = new HashSet(); + subsD.add(subB1); + subsD.add(subB2); + subsD.add(subC1); + subsD.add(subC2); + AdvertisementPacket advPktD = new AdvertisementPacket(advD, AdvType.ADV, subsD, true); + depDetector.processAdvertisementPacket(advPktD); + + // Consolidate + depDetector.consolidate(); + + // Event A1 + Set computedFromA1 = new HashSet(); + Event evA1 = new Event("Host", "A1"); + assertEquals(depDetector.getWaitRecommendations(evA1, computedFromA1).size(), 0); + + // Event A2 + Set computedFromA2 = new HashSet(); + Event evA2 = new Event("Host", "A2"); + assertEquals(depDetector.getWaitRecommendations(evA2, computedFromA2).size(), 0); + + // Event B1 + Set computedFromB1 = new HashSet(); + computedFromB1.add("Host.A1"); + Event evB1 = new Event("Host", "B1"); + assertEquals(depDetector.getWaitRecommendations(evB1, computedFromB1).size(), 1); + for (WaitRecommendations wr : depDetector.getWaitRecommendations(evB1, computedFromB1)) { + assertTrue(wr.getExpression().equals("Host.D")); + assertEquals(wr.getRecommendations().size(), 1); + assertTrue(wr.getRecommendations().contains("Host.C1")); + } + + // Event C1 + Set computedFromC1 = new HashSet(); + computedFromC1.add("Host.A1"); + Event evC1 = new Event("Host", "C1"); + assertEquals(depDetector.getWaitRecommendations(evC1, computedFromC1).size(), 1); + for (WaitRecommendations wr : depDetector.getWaitRecommendations(evC1, computedFromC1)) { + assertTrue(wr.getExpression().equals("Host.D")); + assertEquals(wr.getRecommendations().size(), 1); + assertTrue(wr.getRecommendations().contains("Host.B1")); + } + + // Event B2 + Set computedFromB2 = new HashSet(); + computedFromB2.add("Host.A2"); + Event evB2 = new Event("Host", "B2"); + assertEquals(depDetector.getWaitRecommendations(evB2, computedFromB2).size(), 1); + for (WaitRecommendations wr : depDetector.getWaitRecommendations(evB2, computedFromB2)) { + assertTrue(wr.getExpression().equals("Host.D")); + assertEquals(wr.getRecommendations().size(), 1); + assertTrue(wr.getRecommendations().contains("Host.C2")); + } + + // Event C2 + Set computedFromC2 = new HashSet(); + computedFromC2.add("Host.A2"); + Event evC2 = new Event("Host", "C2"); + assertEquals(depDetector.getWaitRecommendations(evC2, computedFromC2).size(), 1); + for (WaitRecommendations wr : depDetector.getWaitRecommendations(evC2, computedFromC2)) { + assertTrue(wr.getExpression().equals("Host.D")); + assertEquals(wr.getRecommendations().size(), 1); + assertTrue(wr.getRecommendations().contains("Host.B2")); + } + + // Event D + Set computedFromD = new HashSet(); + Event evD = new Event("Host", "D"); + assertEquals(depDetector.getWaitRecommendations(evD, computedFromD).size(), 0); + } + + @Test + public void dualDependencyTest() { + // B = f(A1) + // C = f(A1, A2) + // D = f(B, C) + DependencyDetector depDetector = new DependencyDetector(); + + Subscription subA1 = new Subscription("Host", "A1", UUID.randomUUID()); + Subscription subA2 = new Subscription("Host", "A2", UUID.randomUUID()); + Subscription subB = new Subscription("Host", "B", UUID.randomUUID()); + Subscription subC = new Subscription("Host", "C", UUID.randomUUID()); + + Advertisement advB = new Advertisement("Host", "B"); + Advertisement advC = new Advertisement("Host", "C"); + Advertisement advD = new Advertisement("Host", "D"); + + // Subscription to A1 (A1 generates B) + Set subsB = new HashSet(); + subsB.add(subA1); + AdvertisementPacket advPktA = new AdvertisementPacket(advB, AdvType.ADV, subsB, true); + depDetector.processAdvertisementPacket(advPktA); + + // Subscription to A1, A2 (A1, A2 generate C) + Set subsC = new HashSet(); + subsC.add(subA1); + subsC.add(subA2); + AdvertisementPacket advPktC = new AdvertisementPacket(advC, AdvType.ADV, subsC, true); + depDetector.processAdvertisementPacket(advPktC); + + // Subscription to D (B, C generate D) + Set subsD = new HashSet(); + subsD.add(subB); + subsD.add(subC); + AdvertisementPacket advPktD = new AdvertisementPacket(advD, AdvType.ADV, subsD, true); + depDetector.processAdvertisementPacket(advPktD); + + // Consolidate + depDetector.consolidate(); + + // Event A1 + Set computedFromA1 = new HashSet(); + Event evA1 = new Event("Host", "A1"); + assertEquals(depDetector.getWaitRecommendations(evA1, computedFromA1).size(), 0); + + // Event A2 + Set computedFromA2 = new HashSet(); + Event evA2 = new Event("Host", "A2"); + assertEquals(depDetector.getWaitRecommendations(evA2, computedFromA2).size(), 0); + + // Event B from A1 + Set computedFromB = new HashSet(); + Event evB1 = new Event("Host", "B"); + assertEquals(depDetector.getWaitRecommendations(evB1, computedFromB).size(), 0); + computedFromB.add("Host.A1"); + Event evB2 = new Event("Host", "B"); + assertEquals(depDetector.getWaitRecommendations(evB2, computedFromB).size(), 1); + for (WaitRecommendations wr : depDetector.getWaitRecommendations(evB2, computedFromB)) { + assertTrue(wr.getExpression().equals("Host.D")); + assertEquals(wr.getRecommendations().size(), 1); + assertTrue(wr.getRecommendations().contains("Host.C")); + } + + // Event C from A1 + Set computedFromC1 = new HashSet(); + Event evC1_1 = new Event("Host", "C"); + assertEquals(depDetector.getWaitRecommendations(evC1_1, computedFromC1).size(), 0); + computedFromC1.add("Host.A1"); + Event evC1_2 = new Event("Host", "C"); + assertEquals(depDetector.getWaitRecommendations(evC1_2, computedFromC1).size(), 1); + for (WaitRecommendations wr : depDetector.getWaitRecommendations(evC1_2, computedFromC1)) { + assertTrue(wr.getExpression().equals("Host.D")); + assertEquals(wr.getRecommendations().size(), 1); + assertTrue(wr.getRecommendations().contains("Host.B")); + } + + // Event C from A2 + Set computedFromC2 = new HashSet(); + Event evC2_1 = new Event("Host", "C"); + assertEquals(depDetector.getWaitRecommendations(evC2_1, computedFromC2).size(), 0); + computedFromC1.add("Host.A2"); + Event evC2_2 = new Event("Host", "C"); + assertEquals(depDetector.getWaitRecommendations(evC2_2, computedFromC2).size(), 0); + + // Event D + Set computedFromD = new HashSet(); + Event evD = new Event("Host", "D"); + assertEquals(depDetector.getWaitRecommendations(evD, computedFromD).size(), 0); + } + + @Test + public void nestedCyclesTest() { + // B = f(A) + // C = f(A) + // E = f(A) + // G = f(E) + // F = f(E) + // H = f(F, G) + // D = f(B, C, H) + DependencyDetector depDetector = new DependencyDetector(); + + Subscription subA = new Subscription("Host", "A", UUID.randomUUID()); + Subscription subB = new Subscription("Host", "B", UUID.randomUUID()); + Subscription subC = new Subscription("Host", "C", UUID.randomUUID()); + Subscription subE = new Subscription("Host", "E", UUID.randomUUID()); + Subscription subF = new Subscription("Host", "F", UUID.randomUUID()); + Subscription subG = new Subscription("Host", "G", UUID.randomUUID()); + Subscription subH = new Subscription("Host", "H", UUID.randomUUID()); + + Advertisement advB = new Advertisement("Host", "B"); + Advertisement advC = new Advertisement("Host", "C"); + Advertisement advD = new Advertisement("Host", "D"); + Advertisement advE = new Advertisement("Host", "E"); + Advertisement advF = new Advertisement("Host", "F"); + Advertisement advG = new Advertisement("Host", "G"); + Advertisement advH = new Advertisement("Host", "H"); + + // Subscription to A (A generates B) + Set subsB = new HashSet(); + subsB.add(subA); + AdvertisementPacket advPktA = new AdvertisementPacket(advB, AdvType.ADV, subsB, true); + depDetector.processAdvertisementPacket(advPktA); + + // Subscription to A (A generates C) + Set subsC = new HashSet(); + subsC.add(subA); + AdvertisementPacket advPktC = new AdvertisementPacket(advC, AdvType.ADV, subsC, true); + depDetector.processAdvertisementPacket(advPktC); + + // Subscription to A (A generates E) + Set subsE = new HashSet(); + subsE.add(subA); + AdvertisementPacket advPktE = new AdvertisementPacket(advE, AdvType.ADV, subsE, true); + depDetector.processAdvertisementPacket(advPktE); + + // Subscription to E (E generates G) + Set subsG = new HashSet(); + subsG.add(subE); + AdvertisementPacket advPktG = new AdvertisementPacket(advG, AdvType.ADV, subsG, true); + depDetector.processAdvertisementPacket(advPktG); + + // Subscription to E (E generates F) + Set subsF = new HashSet(); + subsF.add(subE); + AdvertisementPacket advPktF = new AdvertisementPacket(advF, AdvType.ADV, subsF, true); + depDetector.processAdvertisementPacket(advPktF); + + // Subscription to F, G (F, G generate H) + Set subsH = new HashSet(); + subsH.add(subF); + subsH.add(subG); + AdvertisementPacket advPktH = new AdvertisementPacket(advH, AdvType.ADV, subsH, true); + depDetector.processAdvertisementPacket(advPktH); + + // Subscription to B, C, H (B, C, H generate D) + Set subsD = new HashSet(); + subsD.add(subB); + subsD.add(subC); + subsD.add(subH); + AdvertisementPacket subPktD = new AdvertisementPacket(advD, AdvType.ADV, subsD, true); + depDetector.processAdvertisementPacket(subPktD); + + // Consolidate + depDetector.consolidate(); + + // Event A + Set computedFromA = new HashSet(); + Event evA = new Event("Host", "A"); + assertEquals(depDetector.getWaitRecommendations(evA, computedFromA).size(), 0); + + // Event B + Set computedFromB = new HashSet(); + Event evB1 = new Event("Host", "B"); + assertEquals(depDetector.getWaitRecommendations(evB1, computedFromB).size(), 0); + computedFromB.add("Host.A"); + Event evB2 = new Event("Host", "B"); + assertEquals(depDetector.getWaitRecommendations(evB2, computedFromB).size(), 1); + for (WaitRecommendations wr : depDetector.getWaitRecommendations(evB2, computedFromB)) { + assertTrue(wr.getExpression().equals("Host.D")); + assertEquals(wr.getRecommendations().size(), 2); + assertTrue(wr.getRecommendations().contains("Host.C")); + assertTrue(wr.getRecommendations().contains("Host.H")); + } + + // Event C + Set computedFromC = new HashSet(); + Event evC1 = new Event("Host", "C"); + assertEquals(depDetector.getWaitRecommendations(evC1, computedFromC).size(), 0); + computedFromC.add("Host.A"); + Event evC2 = new Event("Host", "C"); + assertEquals(depDetector.getWaitRecommendations(evC2, computedFromC).size(), 1); + for (WaitRecommendations wr : depDetector.getWaitRecommendations(evC2, computedFromC)) { + assertTrue(wr.getExpression().equals("Host.D")); + assertEquals(wr.getRecommendations().size(), 2); + assertTrue(wr.getRecommendations().contains("Host.B")); + assertTrue(wr.getRecommendations().contains("Host.H")); + } + + // Event E + Set computedFromE = new HashSet(); + Event evE = new Event("Host", "E"); + assertEquals(depDetector.getWaitRecommendations(evE, computedFromE).size(), 0); + + // Event F + Set computedFromF = new HashSet(); + Event evF1 = new Event("Host", "F"); + assertEquals(depDetector.getWaitRecommendations(evF1, computedFromF).size(), 0); + computedFromF.add("Host.E"); + Event evF2 = new Event("Host", "F"); + assertEquals(depDetector.getWaitRecommendations(evF2, computedFromF).size(), 1); + for (WaitRecommendations wr : depDetector.getWaitRecommendations(evF2, computedFromF)) { + assertTrue(wr.getExpression().equals("Host.H")); + assertEquals(wr.getRecommendations().size(), 1); + assertTrue(wr.getRecommendations().contains("Host.G")); + } + + // Event G + Set computedFromG = new HashSet(); + Event evG1 = new Event("Host", "G"); + assertEquals(depDetector.getWaitRecommendations(evG1, computedFromG).size(), 0); + computedFromG.add("Host.E"); + Event evG2 = new Event("Host", "G"); + assertEquals(depDetector.getWaitRecommendations(evG2, computedFromG).size(), 1); + for (WaitRecommendations wr : depDetector.getWaitRecommendations(evG2, computedFromG)) { + assertTrue(wr.getExpression().equals("Host.H")); + assertEquals(wr.getRecommendations().size(), 1); + assertTrue(wr.getRecommendations().contains("Host.F")); + } + + // Event H + Set computedFromH = new HashSet(); + Event evH1 = new Event("Host", "H"); + assertEquals(depDetector.getWaitRecommendations(evH1, computedFromH).size(), 0); + computedFromH.add("Host.A"); + Event evH2 = new Event("Host", "H"); + assertEquals(depDetector.getWaitRecommendations(evH2, computedFromH).size(), 1); + for (WaitRecommendations wr : depDetector.getWaitRecommendations(evH2, computedFromH)) { + assertTrue(wr.getExpression().equals("Host.D")); + assertEquals(wr.getRecommendations().size(), 2); + assertTrue(wr.getRecommendations().contains("Host.B")); + assertTrue(wr.getRecommendations().contains("Host.C")); + } + + // Event D + Set computedFromD = new HashSet(); + Event evD = new Event("Host", "D"); + assertEquals(depDetector.getWaitRecommendations(evD, computedFromD).size(), 0); + } } \ No newline at end of file diff --git a/Dream2/src/test/java/javareact/token_service/FinalExpressionsDetectorTest.java b/Dream2/src/test/java/javareact/token_service/FinalExpressionsDetectorTest.java index 20857e8..30c4d5f 100755 --- a/Dream2/src/test/java/javareact/token_service/FinalExpressionsDetectorTest.java +++ b/Dream2/src/test/java/javareact/token_service/FinalExpressionsDetectorTest.java @@ -17,251 +17,252 @@ public class FinalExpressionsDetectorTest { - private static final String hostId = "hostId"; - private static final String hostIdDot = hostId + "."; + private static final String hostId = "hostId"; + private static final String hostIdDot = hostId + "."; - @Test - public void singleExpressionTest() { - // A - FinalExpressionsDetector finalExpressionsDetector = new FinalExpressionsDetector(); - generateAdvertisementPacket(finalExpressionsDetector, "A"); - finalExpressionsDetector.consolidate(); + @Test + public void singleExpressionTest() { + // A + FinalExpressionsDetector finalExpressionsDetector = new FinalExpressionsDetector(); + generateAdvertisementPacket(finalExpressionsDetector, "A"); + finalExpressionsDetector.consolidate(); - Map results = finalExpressionsDetector.getFinalExpressionsFor(hostIdDot + "A"); - assertEquals(results.size(), 0); - } + Map results = finalExpressionsDetector.getFinalExpressionsFor(hostIdDot + "A"); + assertEquals(results.size(), 0); + } - @Test - public void simpleDependencyTest() { - // A - // B = f(A) - FinalExpressionsDetector finalExpressionsDetector = new FinalExpressionsDetector(); - generateAdvertisementPacket(finalExpressionsDetector, "A"); - generateAdvertisementPacket(finalExpressionsDetector, "B", "A"); - finalExpressionsDetector.consolidate(); + @Test + public void simpleDependencyTest() { + // A + // B = f(A) + FinalExpressionsDetector finalExpressionsDetector = new FinalExpressionsDetector(); + generateAdvertisementPacket(finalExpressionsDetector, "A"); + generateAdvertisementPacket(finalExpressionsDetector, "B", "A"); + finalExpressionsDetector.consolidate(); - Map results = finalExpressionsDetector.getFinalExpressionsFor(hostIdDot + "A"); - assertEquals(results.size(), 1); - assertTrue(results.containsKey(hostIdDot + "A")); - assertTrue(results.get(hostIdDot + "A") == 1); - } + Map results = finalExpressionsDetector.getFinalExpressionsFor(hostIdDot + "A"); + assertEquals(results.size(), 1); + assertTrue(results.containsKey(hostIdDot + "A")); + assertTrue(results.get(hostIdDot + "A") == 1); + } - @Test - public void simpleDependencyTest2() { - // A - // B = f(A) - // C = f(B) - // D = f(C) - FinalExpressionsDetector finalExpressionsDetector = new FinalExpressionsDetector(); - generateAdvertisementPacket(finalExpressionsDetector, "A"); - generateAdvertisementPacket(finalExpressionsDetector, "B", "A"); - generateAdvertisementPacket(finalExpressionsDetector, "C", "B"); - generateAdvertisementPacket(finalExpressionsDetector, "D", "C"); - finalExpressionsDetector.consolidate(); + @Test + public void simpleDependencyTest2() { + // A + // B = f(A) + // C = f(B) + // D = f(C) + FinalExpressionsDetector finalExpressionsDetector = new FinalExpressionsDetector(); + generateAdvertisementPacket(finalExpressionsDetector, "A"); + generateAdvertisementPacket(finalExpressionsDetector, "B", "A"); + generateAdvertisementPacket(finalExpressionsDetector, "C", "B"); + generateAdvertisementPacket(finalExpressionsDetector, "D", "C"); + finalExpressionsDetector.consolidate(); - Map results = finalExpressionsDetector.getFinalExpressionsFor(hostIdDot + "A"); - assertEquals(results.size(), 1); - assertTrue(results.containsKey(hostIdDot + "C")); - assertTrue(results.get(hostIdDot + "C") == 1); - } + Map results = finalExpressionsDetector.getFinalExpressionsFor(hostIdDot + "A"); + assertEquals(results.size(), 1); + assertTrue(results.containsKey(hostIdDot + "C")); + assertTrue(results.get(hostIdDot + "C") == 1); + } - @Test - public void simpleDependencyTest3() { - // A - // B = f(A) - // C = f(A) - FinalExpressionsDetector finalExpressionsDetector = new FinalExpressionsDetector(); - generateAdvertisementPacket(finalExpressionsDetector, "A"); - generateAdvertisementPacket(finalExpressionsDetector, "B", "A"); - generateAdvertisementPacket(finalExpressionsDetector, "C", "A"); - finalExpressionsDetector.consolidate(); + @Test + public void simpleDependencyTest3() { + // A + // B = f(A) + // C = f(A) + FinalExpressionsDetector finalExpressionsDetector = new FinalExpressionsDetector(); + generateAdvertisementPacket(finalExpressionsDetector, "A"); + generateAdvertisementPacket(finalExpressionsDetector, "B", "A"); + generateAdvertisementPacket(finalExpressionsDetector, "C", "A"); + finalExpressionsDetector.consolidate(); - Map results = finalExpressionsDetector.getFinalExpressionsFor(hostIdDot + "A"); - assertEquals(results.size(), 1); - assertTrue(results.containsKey(hostIdDot + "A")); - assertTrue(results.get(hostIdDot + "A") == 2); - } + Map results = finalExpressionsDetector.getFinalExpressionsFor(hostIdDot + "A"); + assertEquals(results.size(), 1); + assertTrue(results.containsKey(hostIdDot + "A")); + assertTrue(results.get(hostIdDot + "A") == 2); + } - @Test - public void doubleDependencyTest() { - // A - // B = f(A) - // C = f(B) - // D = f(B) - FinalExpressionsDetector finalExpressionsDetector = new FinalExpressionsDetector(); - generateAdvertisementPacket(finalExpressionsDetector, "A"); - generateAdvertisementPacket(finalExpressionsDetector, "B", "A"); - generateAdvertisementPacket(finalExpressionsDetector, "C", "B"); - generateAdvertisementPacket(finalExpressionsDetector, "D", "B"); - finalExpressionsDetector.consolidate(); + @Test + public void doubleDependencyTest() { + // A + // B = f(A) + // C = f(B) + // D = f(B) + FinalExpressionsDetector finalExpressionsDetector = new FinalExpressionsDetector(); + generateAdvertisementPacket(finalExpressionsDetector, "A"); + generateAdvertisementPacket(finalExpressionsDetector, "B", "A"); + generateAdvertisementPacket(finalExpressionsDetector, "C", "B"); + generateAdvertisementPacket(finalExpressionsDetector, "D", "B"); + finalExpressionsDetector.consolidate(); - Map results = finalExpressionsDetector.getFinalExpressionsFor(hostIdDot + "A"); - assertEquals(results.size(), 1); - assertTrue(results.containsKey(hostIdDot + "B")); - assertTrue(results.get(hostIdDot + "B") == 2); - } + Map results = finalExpressionsDetector.getFinalExpressionsFor(hostIdDot + "A"); + assertEquals(results.size(), 1); + assertTrue(results.containsKey(hostIdDot + "B")); + assertTrue(results.get(hostIdDot + "B") == 2); + } - @Test - public void tripleDependencyTest() { - // A - // B = f(A) - // C = f(B) - // D = f(B) - // E = f(D) - FinalExpressionsDetector finalExpressionsDetector = new FinalExpressionsDetector(); - generateAdvertisementPacket(finalExpressionsDetector, "A"); - generateAdvertisementPacket(finalExpressionsDetector, "B", "A"); - generateAdvertisementPacket(finalExpressionsDetector, "C", "B"); - generateAdvertisementPacket(finalExpressionsDetector, "D", "B"); - generateAdvertisementPacket(finalExpressionsDetector, "E", "D"); - finalExpressionsDetector.consolidate(); + @Test + public void tripleDependencyTest() { + // A + // B = f(A) + // C = f(B) + // D = f(B) + // E = f(D) + FinalExpressionsDetector finalExpressionsDetector = new FinalExpressionsDetector(); + generateAdvertisementPacket(finalExpressionsDetector, "A"); + generateAdvertisementPacket(finalExpressionsDetector, "B", "A"); + generateAdvertisementPacket(finalExpressionsDetector, "C", "B"); + generateAdvertisementPacket(finalExpressionsDetector, "D", "B"); + generateAdvertisementPacket(finalExpressionsDetector, "E", "D"); + finalExpressionsDetector.consolidate(); - Map results = finalExpressionsDetector.getFinalExpressionsFor(hostIdDot + "A"); - assertEquals(results.size(), 1); - assertTrue(results.containsKey(hostIdDot + "D")); - assertTrue(results.get(hostIdDot + "D") == 1); - } + Map results = finalExpressionsDetector.getFinalExpressionsFor(hostIdDot + "A"); + assertEquals(results.size(), 1); + assertTrue(results.containsKey(hostIdDot + "D")); + assertTrue(results.get(hostIdDot + "D") == 1); + } - @Test - public void treeDependencyTest() { - // A - // B = f(A) - // C = f(A) - // D = f(B) - // E = f(B) - // F = f(C) - // G = f(C) - FinalExpressionsDetector finalExpressionsDetector = new FinalExpressionsDetector(); - generateAdvertisementPacket(finalExpressionsDetector, "A"); - generateAdvertisementPacket(finalExpressionsDetector, "B", "A"); - generateAdvertisementPacket(finalExpressionsDetector, "C", "A"); - generateAdvertisementPacket(finalExpressionsDetector, "D", "B"); - generateAdvertisementPacket(finalExpressionsDetector, "E", "B"); - generateAdvertisementPacket(finalExpressionsDetector, "F", "C"); - generateAdvertisementPacket(finalExpressionsDetector, "G", "C"); - finalExpressionsDetector.consolidate(); + @Test + public void treeDependencyTest() { + // A + // B = f(A) + // C = f(A) + // D = f(B) + // E = f(B) + // F = f(C) + // G = f(C) + FinalExpressionsDetector finalExpressionsDetector = new FinalExpressionsDetector(); + generateAdvertisementPacket(finalExpressionsDetector, "A"); + generateAdvertisementPacket(finalExpressionsDetector, "B", "A"); + generateAdvertisementPacket(finalExpressionsDetector, "C", "A"); + generateAdvertisementPacket(finalExpressionsDetector, "D", "B"); + generateAdvertisementPacket(finalExpressionsDetector, "E", "B"); + generateAdvertisementPacket(finalExpressionsDetector, "F", "C"); + generateAdvertisementPacket(finalExpressionsDetector, "G", "C"); + finalExpressionsDetector.consolidate(); - Map results = finalExpressionsDetector.getFinalExpressionsFor(hostIdDot + "A"); - assertEquals(results.size(), 2); - assertTrue(results.containsKey(hostIdDot + "B")); - assertTrue(results.containsKey(hostIdDot + "C")); - assertTrue(results.get(hostIdDot + "B") == 2); - assertTrue(results.get(hostIdDot + "C") == 2); - } + Map results = finalExpressionsDetector.getFinalExpressionsFor(hostIdDot + "A"); + assertEquals(results.size(), 2); + assertTrue(results.containsKey(hostIdDot + "B")); + assertTrue(results.containsKey(hostIdDot + "C")); + assertTrue(results.get(hostIdDot + "B") == 2); + assertTrue(results.get(hostIdDot + "C") == 2); + } - @Test - public void treeDependencyTest2() { - // A - // B = f(A) - // C = f(A) - // D = f(B) - // E = f(B) - // F = f(C) - // G = f(C) - // H = f(G) - FinalExpressionsDetector finalExpressionsDetector = new FinalExpressionsDetector(); - generateAdvertisementPacket(finalExpressionsDetector, "A"); - generateAdvertisementPacket(finalExpressionsDetector, "B", "A"); - generateAdvertisementPacket(finalExpressionsDetector, "C", "A"); - generateAdvertisementPacket(finalExpressionsDetector, "D", "B"); - generateAdvertisementPacket(finalExpressionsDetector, "E", "B"); - generateAdvertisementPacket(finalExpressionsDetector, "F", "C"); - generateAdvertisementPacket(finalExpressionsDetector, "G", "C"); - generateAdvertisementPacket(finalExpressionsDetector, "H", "G"); - finalExpressionsDetector.consolidate(); + @Test + public void treeDependencyTest2() { + // A + // B = f(A) + // C = f(A) + // D = f(B) + // E = f(B) + // F = f(C) + // G = f(C) + // H = f(G) + FinalExpressionsDetector finalExpressionsDetector = new FinalExpressionsDetector(); + generateAdvertisementPacket(finalExpressionsDetector, "A"); + generateAdvertisementPacket(finalExpressionsDetector, "B", "A"); + generateAdvertisementPacket(finalExpressionsDetector, "C", "A"); + generateAdvertisementPacket(finalExpressionsDetector, "D", "B"); + generateAdvertisementPacket(finalExpressionsDetector, "E", "B"); + generateAdvertisementPacket(finalExpressionsDetector, "F", "C"); + generateAdvertisementPacket(finalExpressionsDetector, "G", "C"); + generateAdvertisementPacket(finalExpressionsDetector, "H", "G"); + finalExpressionsDetector.consolidate(); - Map results = finalExpressionsDetector.getFinalExpressionsFor(hostIdDot + "A"); - assertEquals(results.size(), 2); - assertTrue(results.containsKey(hostIdDot + "B")); - assertTrue(results.containsKey(hostIdDot + "G")); - assertTrue(results.get(hostIdDot + "B") == 2); - assertTrue(results.get(hostIdDot + "G") == 1); - } + Map results = finalExpressionsDetector.getFinalExpressionsFor(hostIdDot + "A"); + assertEquals(results.size(), 2); + assertTrue(results.containsKey(hostIdDot + "B")); + assertTrue(results.containsKey(hostIdDot + "G")); + assertTrue(results.get(hostIdDot + "B") == 2); + assertTrue(results.get(hostIdDot + "G") == 1); + } - @Test - public void graphDependencyTest() { - // A - // B - // C = f(A, B) - // D = f(A, B) - // E = f(C, D) - FinalExpressionsDetector finalExpressionsDetector = new FinalExpressionsDetector(); - generateAdvertisementPacket(finalExpressionsDetector, "A"); - generateAdvertisementPacket(finalExpressionsDetector, "B"); - generateAdvertisementPacket(finalExpressionsDetector, "C", "A", "B"); - generateAdvertisementPacket(finalExpressionsDetector, "D", "A", "B"); - generateAdvertisementPacket(finalExpressionsDetector, "E", "C", "D"); - finalExpressionsDetector.consolidate(); + @Test + public void graphDependencyTest() { + // A + // B + // C = f(A, B) + // D = f(A, B) + // E = f(C, D) + FinalExpressionsDetector finalExpressionsDetector = new FinalExpressionsDetector(); + generateAdvertisementPacket(finalExpressionsDetector, "A"); + generateAdvertisementPacket(finalExpressionsDetector, "B"); + generateAdvertisementPacket(finalExpressionsDetector, "C", "A", "B"); + generateAdvertisementPacket(finalExpressionsDetector, "D", "A", "B"); + generateAdvertisementPacket(finalExpressionsDetector, "E", "C", "D"); + finalExpressionsDetector.consolidate(); - Map resultsA = finalExpressionsDetector.getFinalExpressionsFor(hostIdDot + "A"); - assertEquals(resultsA.size(), 2); - assertTrue(resultsA.containsKey(hostIdDot + "C")); - assertTrue(resultsA.containsKey(hostIdDot + "D")); - assertTrue(resultsA.get(hostIdDot + "C") == 1); - assertTrue(resultsA.get(hostIdDot + "D") == 1); + Map resultsA = finalExpressionsDetector.getFinalExpressionsFor(hostIdDot + "A"); + assertEquals(resultsA.size(), 2); + assertTrue(resultsA.containsKey(hostIdDot + "C")); + assertTrue(resultsA.containsKey(hostIdDot + "D")); + assertTrue(resultsA.get(hostIdDot + "C") == 1); + assertTrue(resultsA.get(hostIdDot + "D") == 1); - Map resultsB = finalExpressionsDetector.getFinalExpressionsFor(hostIdDot + "B"); - assertEquals(resultsB.size(), 2); - assertTrue(resultsB.containsKey(hostIdDot + "C")); - assertTrue(resultsB.containsKey(hostIdDot + "D")); - assertTrue(resultsB.get(hostIdDot + "C") == 1); - assertTrue(resultsB.get(hostIdDot + "D") == 1); - } + Map resultsB = finalExpressionsDetector.getFinalExpressionsFor(hostIdDot + "B"); + assertEquals(resultsB.size(), 2); + assertTrue(resultsB.containsKey(hostIdDot + "C")); + assertTrue(resultsB.containsKey(hostIdDot + "D")); + assertTrue(resultsB.get(hostIdDot + "C") == 1); + assertTrue(resultsB.get(hostIdDot + "D") == 1); + } - @Test - public void triangularDependencyTest() { - // A - // B = f(A) - // C = f(A, B) - FinalExpressionsDetector finalExpressionsDetector = new FinalExpressionsDetector(); - generateAdvertisementPacket(finalExpressionsDetector, "A"); - generateAdvertisementPacket(finalExpressionsDetector, "B", "A"); - generateAdvertisementPacket(finalExpressionsDetector, "C", "A", "B"); - finalExpressionsDetector.consolidate(); + @Test + public void triangularDependencyTest() { + // A + // B = f(A) + // C = f(A, B) + FinalExpressionsDetector finalExpressionsDetector = new FinalExpressionsDetector(); + generateAdvertisementPacket(finalExpressionsDetector, "A"); + generateAdvertisementPacket(finalExpressionsDetector, "B", "A"); + generateAdvertisementPacket(finalExpressionsDetector, "C", "A", "B"); + finalExpressionsDetector.consolidate(); - Map results = finalExpressionsDetector.getFinalExpressionsFor(hostIdDot + "A"); - assertEquals(results.size(), 1); - assertTrue(results.containsKey(hostIdDot + "B")); - assertTrue(results.get(hostIdDot + "B") == 1); - } + Map results = finalExpressionsDetector.getFinalExpressionsFor(hostIdDot + "A"); + assertEquals(results.size(), 1); + assertTrue(results.containsKey(hostIdDot + "B")); + assertTrue(results.get(hostIdDot + "B") == 1); + } - @Test - public void cycleDependencyTest() { - // A - // B = f(A) - // C = f(A) - // D = f(B, C) - FinalExpressionsDetector finalExpressionsDetector = new FinalExpressionsDetector(); - generateAdvertisementPacket(finalExpressionsDetector, "A"); - generateAdvertisementPacket(finalExpressionsDetector, "B", "A"); - generateAdvertisementPacket(finalExpressionsDetector, "C", "A"); - generateAdvertisementPacket(finalExpressionsDetector, "D", "B", "C"); - finalExpressionsDetector.consolidate(); + @Test + public void cycleDependencyTest() { + // A + // B = f(A) + // C = f(A) + // D = f(B, C) + FinalExpressionsDetector finalExpressionsDetector = new FinalExpressionsDetector(); + generateAdvertisementPacket(finalExpressionsDetector, "A"); + generateAdvertisementPacket(finalExpressionsDetector, "B", "A"); + generateAdvertisementPacket(finalExpressionsDetector, "C", "A"); + generateAdvertisementPacket(finalExpressionsDetector, "D", "B", "C"); + finalExpressionsDetector.consolidate(); - Map results = finalExpressionsDetector.getFinalExpressionsFor(hostIdDot + "A"); - assertEquals(results.size(), 2); - assertTrue(results.containsKey(hostIdDot + "B")); - assertTrue(results.containsKey(hostIdDot + "C")); - assertTrue(results.get(hostIdDot + "B") == 1); - assertTrue(results.get(hostIdDot + "C") == 1); - } + Map results = finalExpressionsDetector.getFinalExpressionsFor(hostIdDot + "A"); + assertEquals(results.size(), 2); + assertTrue(results.containsKey(hostIdDot + "B")); + assertTrue(results.containsKey(hostIdDot + "C")); + assertTrue(results.get(hostIdDot + "B") == 1); + assertTrue(results.get(hostIdDot + "C") == 1); + } - private final void generateAdvertisementPacket(FinalExpressionsDetector finalExpressionsDetector, String name, String... subsNames) { - AdvertisementPacket advPkt = generateAdvertisementPacket(name, subsNames); - finalExpressionsDetector.processAdvertisementPacket(advPkt); - } + private final void generateAdvertisementPacket(FinalExpressionsDetector finalExpressionsDetector, String name, + String... subsNames) { + AdvertisementPacket advPkt = generateAdvertisementPacket(name, subsNames); + finalExpressionsDetector.processAdvertisementPacket(advPkt); + } - private final AdvertisementPacket generateAdvertisementPacket(String name, String... subsNames) { - Advertisement adv = new Advertisement(hostId, name); - Set subscriptions = new HashSet(); - for (String subName : subsNames) { - Subscription sub = new Subscription(hostId, subName, UUID.randomUUID()); - subscriptions.add(sub); - } - if (subscriptions.isEmpty()) { - return new AdvertisementPacket(adv, AdvType.ADV, true); - } else { - return new AdvertisementPacket(adv, AdvType.ADV, subscriptions, true); - } - } + private final AdvertisementPacket generateAdvertisementPacket(String name, String... subsNames) { + Advertisement adv = new Advertisement(hostId, name); + Set subscriptions = new HashSet(); + for (String subName : subsNames) { + Subscription sub = new Subscription(hostId, subName, UUID.randomUUID()); + subscriptions.add(sub); + } + if (subscriptions.isEmpty()) { + return new AdvertisementPacket(adv, AdvType.ADV, true); + } else { + return new AdvertisementPacket(adv, AdvType.ADV, subscriptions, true); + } + } } From 6f291c1f5ff3838494a718c750b312cbc58087ff Mon Sep 17 00:00:00 2001 From: Alessandro Margara Date: Fri, 29 Jan 2016 11:13:45 +0100 Subject: [PATCH 014/161] Small fix in scripts --- DreamSim/scripts/summarize.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/DreamSim/scripts/summarize.py b/DreamSim/scripts/summarize.py index 68f0ac3..b4940e9 100755 --- a/DreamSim/scripts/summarize.py +++ b/DreamSim/scripts/summarize.py @@ -138,13 +138,13 @@ def avgTraffic(filename, suffix, numRepetitions): meanLockGrant = sumLockGrant/count meanTotal = sumTotal/count - sampleStdDevEvents = sqrt((sumEventsSquare/count - meanEvents**2)*count/(count-1)) - sampleStdDevSubscriptions = sqrt((sumSubscriptionsSquare/count - meanSubscriptions**2)*count/(count-1)) - sampleStdDevAdvertisements = sqrt((sumAdvertisementsSquare/count - meanAdvertisements**2)*count/(count-1)) - sampleStdDevLockRelease = sqrt((sumLockReleaseSquare/count - meanLockRelease**2)*count/(count-1)) - sampleStdDevLockRequest = sqrt((sumLockRequestSquare/count - meanLockRequest**2)*count/(count-1)) - sampleStdDevLockGrant = sqrt((sumLockGrantSquare/count - meanLockGrant**2)*count/(count-1)) - sampleStdDevTotal = sqrt((sumTotalSquare/count - meanTotal**2)*count/(count-1)) + sampleStdDevEvents = sqrt(abs(sumEventsSquare/count - meanEvents**2)*count/(count-1)) + sampleStdDevSubscriptions = sqrt(abs(sumSubscriptionsSquare/count - meanSubscriptions**2)*count/(count-1)) + sampleStdDevAdvertisements = sqrt(abs(sumAdvertisementsSquare/count - meanAdvertisements**2)*count/(count-1)) + sampleStdDevLockRelease = sqrt(abs(sumLockReleaseSquare/count - meanLockRelease**2)*count/(count-1)) + sampleStdDevLockRequest = sqrt(abs(sumLockRequestSquare/count - meanLockRequest**2)*count/(count-1)) + sampleStdDevLockGrant = sqrt(abs(sumLockGrantSquare/count - meanLockGrant**2)*count/(count-1)) + sampleStdDevTotal = sqrt(abs(sumTotalSquare/count - meanTotal**2)*count/(count-1)) deltaEvents = 0 deltaSubscriptions = 0 From b01973ce8d013a920ea3b34f65e6df44f5663764 Mon Sep 17 00:00:00 2001 From: Tobias Becker Date: Thu, 11 Feb 2016 21:24:35 +0100 Subject: [PATCH 015/161] re-formatted all code and added automatic code formatting --- Dream2/.settings/org.eclipse.jdt.core.prefs | 285 +++ Dream2/.settings/org.eclipse.jdt.ui.prefs | 62 + .../java/dream/examples/StartLockManager.java | 6 +- .../java/dream/examples/StartServer.java | 6 +- .../examples/financial/FinancialApp.java | 90 +- .../dream/examples/financial/InputModel.java | 94 +- .../java/dream/examples/financial/Model1.java | 19 +- .../java/dream/examples/financial/Model2.java | 13 +- .../java/dream/examples/financial/Model3.java | 15 +- .../java/dream/examples/local/Example1.java | 65 +- .../dream/examples/local/ExampleFilter.java | 58 +- .../dream/examples/local/ExampleGlitch.java | 80 +- .../dream/examples/local/ExampleList.java | 54 +- .../examples/remote/RemoteSignalExample.java | 71 +- .../examples/remote/RemoteVarExample.java | 54 +- .../dream/client/ClientEventForwarder.java | 582 +++--- .../dream/client/ClientSubscriptionTable.java | 100 +- .../java/dream/client/ConnectionManager.java | 309 +-- .../main/java/dream/client/DreamClient.java | 22 +- .../java/dream/client/EventProducerPair.java | 34 +- .../dream/client/FilteredUpdateProducer.java | 88 +- .../main/java/dream/client/LockApplicant.java | 14 +- .../main/java/dream/client/QueueManager.java | 130 +- .../src/main/java/dream/client/RemoteVar.java | 220 +-- Dream2/src/main/java/dream/client/Signal.java | 448 ++--- .../main/java/dream/client/Subscriber.java | 14 +- .../java/dream/client/TimeChangingValue.java | 38 +- .../java/dream/client/UpdateConsumer.java | 18 +- .../java/dream/client/UpdateProducer.java | 94 +- .../dream/client/ValueChangeListener.java | 2 +- Dream2/src/main/java/dream/client/Var.java | 257 +-- .../java/dream/common/ConsistencyType.java | 48 +- Dream2/src/main/java/dream/common/Consts.java | 109 +- .../common/packets/AdvertisementPacket.java | 151 +- .../dream/common/packets/EventPacket.java | 94 +- .../common/packets/SubscriptionPacket.java | 100 +- .../dream/common/packets/content/AdvType.java | 2 +- .../common/packets/content/Advertisement.java | 46 +- .../dream/common/packets/content/Event.java | 64 +- .../dream/common/packets/content/SubType.java | 2 +- .../common/packets/content/Subscription.java | 90 +- .../discovery/LockManagerHelloPacket.java | 4 +- .../packets/discovery/ServerHelloPacket.java | 4 +- .../packets/locking/LockGrantPacket.java | 26 +- .../packets/locking/LockReleasePacket.java | 26 +- .../packets/locking/LockRequestPacket.java | 94 +- .../common/packets/locking/LockType.java | 2 +- .../utils/AtomicDependencyDetector.java | 16 +- .../CompleteGlitchFreeDependencyDetector.java | 76 +- .../common/utils/DependencyDetector.java | 10 +- .../dream/common/utils/DependencyGraph.java | 82 +- .../common/utils/DependencyGraphUtils.java | 192 +- .../common/utils/FinalNodesDetector.java | 70 +- .../utils/InterSourceDependencyDetector.java | 32 +- .../utils/IntraSourceDependencyDetector.java | 120 +- .../common/utils/WaitRecommendations.java | 59 +- .../main/java/dream/locking/LockManager.java | 341 ++-- .../dream/locking/LockManagerForwarder.java | 73 +- .../dream/locking/LockManagerLauncher.java | 84 +- .../java/dream/server/AdvertisementTable.java | 66 +- .../dream/server/ServerEventForwarder.java | 247 +-- .../java/dream/server/ServerLauncher.java | 88 +- .../java/dream/server/SubscriptionTable.java | 89 +- Dream2/src/test/java/dream/LocalTest.java | 126 +- .../src/test/java/dream/RegressionTests.java | 14 +- .../utils/AtomicDependencyDetectorTest.java | 186 +- ...pleteGlitchFreeDependencyDetectorTest.java | 270 +-- .../utils/DependencyGraphUtilsTest.java | 438 ++--- .../common/utils/FinalNodesDetectorTest.java | 507 ++--- .../IntraSourceDependencyDetectorTest.java | 1732 ++++++++--------- .../java/dream/locking/LockManagerTest.java | 240 +-- 71 files changed, 4887 insertions(+), 4475 deletions(-) create mode 100644 Dream2/.settings/org.eclipse.jdt.ui.prefs diff --git a/Dream2/.settings/org.eclipse.jdt.core.prefs b/Dream2/.settings/org.eclipse.jdt.core.prefs index 714351a..0d59eef 100644 --- a/Dream2/.settings/org.eclipse.jdt.core.prefs +++ b/Dream2/.settings/org.eclipse.jdt.core.prefs @@ -3,3 +3,288 @@ org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 org.eclipse.jdt.core.compiler.compliance=1.8 org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning org.eclipse.jdt.core.compiler.source=1.8 +org.eclipse.jdt.core.formatter.align_type_members_on_columns=false +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=0 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16 +org.eclipse.jdt.core.formatter.alignment_for_assignment=0 +org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16 +org.eclipse.jdt.core.formatter.alignment_for_compact_if=16 +org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80 +org.eclipse.jdt.core.formatter.alignment_for_enum_constants=0 +org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16 +org.eclipse.jdt.core.formatter.alignment_for_method_declaration=0 +org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16 +org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_resources_in_try=80 +org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16 +org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch=16 +org.eclipse.jdt.core.formatter.blank_lines_after_imports=1 +org.eclipse.jdt.core.formatter.blank_lines_after_package=1 +org.eclipse.jdt.core.formatter.blank_lines_before_field=0 +org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0 +org.eclipse.jdt.core.formatter.blank_lines_before_imports=1 +org.eclipse.jdt.core.formatter.blank_lines_before_member_type=1 +org.eclipse.jdt.core.formatter.blank_lines_before_method=1 +org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1 +org.eclipse.jdt.core.formatter.blank_lines_before_package=0 +org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1 +org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=1 +org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_lambda_body=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line +org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false +org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false +org.eclipse.jdt.core.formatter.comment.format_block_comments=true +org.eclipse.jdt.core.formatter.comment.format_header=false +org.eclipse.jdt.core.formatter.comment.format_html=true +org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true +org.eclipse.jdt.core.formatter.comment.format_line_comments=true +org.eclipse.jdt.core.formatter.comment.format_source_code=true +org.eclipse.jdt.core.formatter.comment.indent_parameter_description=true +org.eclipse.jdt.core.formatter.comment.indent_root_tags=true +org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert +org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=insert +org.eclipse.jdt.core.formatter.comment.line_length=80 +org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries=true +org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries=true +org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments=false +org.eclipse.jdt.core.formatter.compact_else_if=true +org.eclipse.jdt.core.formatter.continuation_indentation=2 +org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2 +org.eclipse.jdt.core.formatter.disabling_tag=@formatter\:off +org.eclipse.jdt.core.formatter.enabling_tag=@formatter\:on +org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false +org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true +org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true +org.eclipse.jdt.core.formatter.indent_empty_lines=false +org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true +org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true +org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true +org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=false +org.eclipse.jdt.core.formatter.indentation.size=4 +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_label=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_after_type_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert +org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert +org.eclipse.jdt.core.formatter.insert_space_after_lambda_arrow=insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources=insert +org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert +org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_lambda_arrow=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert +org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert +org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert +org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.join_lines_in_comments=true +org.eclipse.jdt.core.formatter.join_wrapped_lines=true +org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false +org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false +org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false +org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false +org.eclipse.jdt.core.formatter.lineSplit=120 +org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false +org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false +org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0 +org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1 +org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=true +org.eclipse.jdt.core.formatter.tabulation.char=tab +org.eclipse.jdt.core.formatter.tabulation.size=4 +org.eclipse.jdt.core.formatter.use_on_off_tags=false +org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false +org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true +org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch=true +org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested=true +org.eclipse.jdt.core.javaFormatter=org.eclipse.jdt.core.defaultJavaFormatter diff --git a/Dream2/.settings/org.eclipse.jdt.ui.prefs b/Dream2/.settings/org.eclipse.jdt.ui.prefs new file mode 100644 index 0000000..07ccfe5 --- /dev/null +++ b/Dream2/.settings/org.eclipse.jdt.ui.prefs @@ -0,0 +1,62 @@ +eclipse.preferences.version=1 +editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true +formatter_profile=_Dream +formatter_settings_version=12 +sp_cleanup.add_default_serial_version_id=true +sp_cleanup.add_generated_serial_version_id=false +sp_cleanup.add_missing_annotations=true +sp_cleanup.add_missing_deprecated_annotations=true +sp_cleanup.add_missing_methods=false +sp_cleanup.add_missing_nls_tags=false +sp_cleanup.add_missing_override_annotations=true +sp_cleanup.add_missing_override_annotations_interface_methods=true +sp_cleanup.add_serial_version_id=false +sp_cleanup.always_use_blocks=true +sp_cleanup.always_use_parentheses_in_expressions=false +sp_cleanup.always_use_this_for_non_static_field_access=false +sp_cleanup.always_use_this_for_non_static_method_access=false +sp_cleanup.convert_functional_interfaces=false +sp_cleanup.convert_to_enhanced_for_loop=false +sp_cleanup.correct_indentation=false +sp_cleanup.format_source_code=true +sp_cleanup.format_source_code_changes_only=false +sp_cleanup.insert_inferred_type_arguments=false +sp_cleanup.make_local_variable_final=true +sp_cleanup.make_parameters_final=false +sp_cleanup.make_private_fields_final=true +sp_cleanup.make_type_abstract_if_missing_method=false +sp_cleanup.make_variable_declarations_final=false +sp_cleanup.never_use_blocks=false +sp_cleanup.never_use_parentheses_in_expressions=true +sp_cleanup.on_save_use_additional_actions=false +sp_cleanup.organize_imports=true +sp_cleanup.qualify_static_field_accesses_with_declaring_class=false +sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true +sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true +sp_cleanup.qualify_static_member_accesses_with_declaring_class=false +sp_cleanup.qualify_static_method_accesses_with_declaring_class=false +sp_cleanup.remove_private_constructors=true +sp_cleanup.remove_redundant_type_arguments=true +sp_cleanup.remove_trailing_whitespaces=false +sp_cleanup.remove_trailing_whitespaces_all=true +sp_cleanup.remove_trailing_whitespaces_ignore_empty=false +sp_cleanup.remove_unnecessary_casts=true +sp_cleanup.remove_unnecessary_nls_tags=false +sp_cleanup.remove_unused_imports=false +sp_cleanup.remove_unused_local_variables=false +sp_cleanup.remove_unused_private_fields=true +sp_cleanup.remove_unused_private_members=false +sp_cleanup.remove_unused_private_methods=true +sp_cleanup.remove_unused_private_types=true +sp_cleanup.sort_members=false +sp_cleanup.sort_members_all=false +sp_cleanup.use_anonymous_class_creation=false +sp_cleanup.use_blocks=false +sp_cleanup.use_blocks_only_for_return_and_throw=false +sp_cleanup.use_lambda=true +sp_cleanup.use_parentheses_in_expressions=false +sp_cleanup.use_this_for_non_static_field_access=false +sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true +sp_cleanup.use_this_for_non_static_method_access=false +sp_cleanup.use_this_for_non_static_method_access_only_if_necessary=true +sp_cleanup.use_type_arguments=false diff --git a/Dream2/src/examples/java/dream/examples/StartLockManager.java b/Dream2/src/examples/java/dream/examples/StartLockManager.java index 0396296..62dc661 100755 --- a/Dream2/src/examples/java/dream/examples/StartLockManager.java +++ b/Dream2/src/examples/java/dream/examples/StartLockManager.java @@ -3,8 +3,8 @@ import dream.locking.LockManagerLauncher; public class StartLockManager { - public static void main(String[] args) { - LockManagerLauncher.start(); - } + public static void main(String[] args) { + LockManagerLauncher.start(); + } } diff --git a/Dream2/src/examples/java/dream/examples/StartServer.java b/Dream2/src/examples/java/dream/examples/StartServer.java index ed7cbad..056fbf9 100755 --- a/Dream2/src/examples/java/dream/examples/StartServer.java +++ b/Dream2/src/examples/java/dream/examples/StartServer.java @@ -4,8 +4,8 @@ public class StartServer { - public static void main(String[] args) { - ServerLauncher.start(); - } + public static void main(String[] args) { + ServerLauncher.start(); + } } diff --git a/Dream2/src/examples/java/dream/examples/financial/FinancialApp.java b/Dream2/src/examples/java/dream/examples/financial/FinancialApp.java index 450dc9c..4ddf40a 100644 --- a/Dream2/src/examples/java/dream/examples/financial/FinancialApp.java +++ b/Dream2/src/examples/java/dream/examples/financial/FinancialApp.java @@ -6,62 +6,62 @@ import dream.common.Consts; public class FinancialApp implements ValueChangeListener { - private Signal f1Signal; - private Signal f2Signal; - private Signal f3Signal; + private Signal f1Signal; + private Signal f2Signal; + private Signal f3Signal; - private RemoteVar f1; - private RemoteVar f2; - private RemoteVar f3; + private RemoteVar f1; + private RemoteVar f2; + private RemoteVar f3; - public static void main(String[] args) { - new FinancialApp().start(); - } + public static void main(String[] args) { + new FinancialApp().start(); + } - public void start() { - Consts.hostName = "Local"; + public void start() { + Consts.hostName = "Local"; - try { - Thread.sleep(2000); - } catch (final InterruptedException e) { - e.printStackTrace(); - } + try { + Thread.sleep(2000); + } catch (final InterruptedException e) { + e.printStackTrace(); + } - f1 = new RemoteVar<>("f1@Model1"); - f2 = new RemoteVar<>("f2@Model2"); - f3 = new RemoteVar<>("f3@Model3"); + f1 = new RemoteVar<>("f1@Model1"); + f2 = new RemoteVar<>("f2@Model2"); + f3 = new RemoteVar<>("f3@Model3"); - final RemoteVar model1 = new RemoteVar<>("Model1", "model1"); + final RemoteVar model1 = new RemoteVar<>("Model1", "model1"); - f1Signal = new Signal<>("f1Signal", () -> f1.get(), f1); - f2Signal = new Signal<>("f2Signal", () -> f2.get(), f2); - f3Signal = new Signal<>("f3Signal", () -> f3.get(), f3); + f1Signal = new Signal<>("f1Signal", () -> f1.get(), f1); + f2Signal = new Signal<>("f2Signal", () -> f2.get(), f2); + f3Signal = new Signal<>("f3Signal", () -> f3.get(), f3); - f1Signal.addValueChangeListener(this); - f2Signal.addValueChangeListener(this); - f3Signal.addValueChangeListener(this); + f1Signal.addValueChangeListener(this); + f2Signal.addValueChangeListener(this); + f3Signal.addValueChangeListener(this); - try { - Thread.sleep(2000); - } catch (final InterruptedException e) { - e.printStackTrace(); - } + try { + Thread.sleep(2000); + } catch (final InterruptedException e) { + e.printStackTrace(); + } - System.out.println(f1.get()); - System.out.println(f2.get()); - System.out.println(f3.get()); + System.out.println(f1.get()); + System.out.println(f2.get()); + System.out.println(f3.get()); - System.out.println(model1.get()); - } + System.out.println(model1.get()); + } - @Override - public void notifyValueChanged(Integer newValue) { - System.out.println("Value changed"); + @Override + public void notifyValueChanged(Integer newValue) { + System.out.println("Value changed"); - if (f1.get() != null && f2.get() != null && f3.get() != null) { - if ((f1.get() + f2.get() + f3.get()) / 3.0 > 150) { - System.out.println("Financial Alert!"); - } - } - } + if (f1.get() != null && f2.get() != null && f3.get() != null) { + if ((f1.get() + f2.get() + f3.get()) / 3.0 > 150) { + System.out.println("Financial Alert!"); + } + } + } } diff --git a/Dream2/src/examples/java/dream/examples/financial/InputModel.java b/Dream2/src/examples/java/dream/examples/financial/InputModel.java index b8b4a67..dcb9fc1 100644 --- a/Dream2/src/examples/java/dream/examples/financial/InputModel.java +++ b/Dream2/src/examples/java/dream/examples/financial/InputModel.java @@ -8,61 +8,61 @@ import dream.server.ServerLauncher; public class InputModel { - private boolean serverStarted = false; - private boolean lockManagerStarted = false; + private boolean serverStarted = false; + private boolean lockManagerStarted = false; - public static void main(String[] args) { - new InputModel().start(); - } + public static void main(String[] args) { + new InputModel().start(); + } - public void start() { - startServerIfNeeded(); - startLockManagerIfNeeded(); + public void start() { + startServerIfNeeded(); + startLockManagerIfNeeded(); - Consts.hostName = "InputModel"; + Consts.hostName = "InputModel"; - final Var marketIndex = new Var<>("marketIndex", 1); - final Var stockOpts = new Var<>("stockOpts", 1); - final Var news = new Var<>("news", 1); + final Var marketIndex = new Var<>("marketIndex", 1); + final Var stockOpts = new Var<>("stockOpts", 1); + final Var news = new Var<>("news", 1); - final Random random = new Random(); + final Random random = new Random(); - while (true) { - marketIndex.set(random.nextInt(100)); - stockOpts.set(random.nextInt(100)); - news.set(random.nextInt(100)); + while (true) { + marketIndex.set(random.nextInt(100)); + stockOpts.set(random.nextInt(100)); + news.set(random.nextInt(100)); - System.out.println("New values: " + marketIndex.get() + ", " + stockOpts.get() + ", " + news.get()); + System.out.println("New values: " + marketIndex.get() + ", " + stockOpts.get() + ", " + news.get()); - try { - Thread.sleep(1000); - } catch (final InterruptedException e) { - e.printStackTrace(); - } - } - } + try { + Thread.sleep(1000); + } catch (final InterruptedException e) { + e.printStackTrace(); + } + } + } - private final void startServerIfNeeded() { - if (!serverStarted) { - ServerLauncher.start(); - serverStarted = true; - } - try { - Thread.sleep(500); - } catch (final InterruptedException e) { - e.printStackTrace(); - } - } + private final void startServerIfNeeded() { + if (!serverStarted) { + ServerLauncher.start(); + serverStarted = true; + } + try { + Thread.sleep(500); + } catch (final InterruptedException e) { + e.printStackTrace(); + } + } - private final void startLockManagerIfNeeded() { - if (!lockManagerStarted) { - LockManagerLauncher.start(); - lockManagerStarted = true; - } - try { - Thread.sleep(500); - } catch (final InterruptedException e) { - e.printStackTrace(); - } - } + private final void startLockManagerIfNeeded() { + if (!lockManagerStarted) { + LockManagerLauncher.start(); + lockManagerStarted = true; + } + try { + Thread.sleep(500); + } catch (final InterruptedException e) { + e.printStackTrace(); + } + } } diff --git a/Dream2/src/examples/java/dream/examples/financial/Model1.java b/Dream2/src/examples/java/dream/examples/financial/Model1.java index f6689d8..6dc26ab 100644 --- a/Dream2/src/examples/java/dream/examples/financial/Model1.java +++ b/Dream2/src/examples/java/dream/examples/financial/Model1.java @@ -9,17 +9,18 @@ public class Model1 implements ValueChangeListener { public void start() { Consts.hostName = "Model1"; - + RemoteVar marketIndex = new RemoteVar<>("InputModel", "marketIndex"); RemoteVar stockOpts = new RemoteVar<>("InputModel", "stockOpts"); - - - + Signal f1 = new Signal<>("f1", () -> { - if (marketIndex.get() == null || stockOpts.get() == null) { return null; } - else { return marketIndex.get() * 2 + stockOpts.get(); - } }, marketIndex, stockOpts); - + if (marketIndex.get() == null || stockOpts.get() == null) { + return null; + } else { + return marketIndex.get() * 2 + stockOpts.get(); + } + } , marketIndex, stockOpts); + f1.addValueChangeListener(this); } @@ -27,7 +28,7 @@ public void start() { public void notifyValueChanged(Integer newValue) { System.out.println("New value for f1: " + newValue); } - + public static void main(String[] args) { new Model1().start(); } diff --git a/Dream2/src/examples/java/dream/examples/financial/Model2.java b/Dream2/src/examples/java/dream/examples/financial/Model2.java index 7676696..c49fac6 100644 --- a/Dream2/src/examples/java/dream/examples/financial/Model2.java +++ b/Dream2/src/examples/java/dream/examples/financial/Model2.java @@ -13,10 +13,13 @@ public void start() { RemoteVar stockOpts = new RemoteVar<>("InputModel", "stockOpts"); Signal f2 = new Signal<>("f2", () -> { - if (marketIndex.get() == null || stockOpts.get() == null) { return null; } - else { return marketIndex.get() + stockOpts.get() * 2; } - }, marketIndex, stockOpts); - + if (marketIndex.get() == null || stockOpts.get() == null) { + return null; + } else { + return marketIndex.get() + stockOpts.get() * 2; + } + } , marketIndex, stockOpts); + f2.addValueChangeListener(this); } @@ -24,7 +27,7 @@ public void start() { public void notifyValueChanged(Integer newValue) { System.out.println("New value for f2: " + newValue); } - + public static void main(String[] args) { new Model2().start(); } diff --git a/Dream2/src/examples/java/dream/examples/financial/Model3.java b/Dream2/src/examples/java/dream/examples/financial/Model3.java index 0151fdf..6a06cde 100644 --- a/Dream2/src/examples/java/dream/examples/financial/Model3.java +++ b/Dream2/src/examples/java/dream/examples/financial/Model3.java @@ -8,15 +8,18 @@ public class Model3 implements ValueChangeListener { public void start() { Consts.hostName = "Model3"; - + RemoteVar marketIndex = new RemoteVar<>("InputModel", "marketIndex"); RemoteVar news = new RemoteVar<>("InputModel", "news"); Signal f3 = new Signal<>("f3", () -> { - if (marketIndex.get() == null || news.get() == null) { return null; } - else { return marketIndex.get() + news.get(); } - }, marketIndex, news); - + if (marketIndex.get() == null || news.get() == null) { + return null; + } else { + return marketIndex.get() + news.get(); + } + } , marketIndex, news); + f3.addValueChangeListener(this); } @@ -24,7 +27,7 @@ public void start() { public void notifyValueChanged(Integer newValue) { System.out.println("New value for f3: " + newValue); } - + public static void main(String[] args) { new Model3().start(); } diff --git a/Dream2/src/examples/java/dream/examples/local/Example1.java b/Dream2/src/examples/java/dream/examples/local/Example1.java index 77d7626..21f0d5e 100755 --- a/Dream2/src/examples/java/dream/examples/local/Example1.java +++ b/Dream2/src/examples/java/dream/examples/local/Example1.java @@ -5,34 +5,39 @@ public class Example1 { - public static void main(String args[]) { - final Var varInt = new Var<>("varInt", 1); - final Var varDouble = new Var<>("varDouble", 1.0); - final Var varBool = new Var<>("varBool", false); - final Var varString1 = new Var<>("varString1", ""); - final Var varString2 = new Var<>("varString2", ""); - - final Signal signalInt = new Signal("signalInt", () -> 10 - 2 + (varInt.get() * 2 + varInt.get()) / 2, varInt); - final Signal signalDouble = new Signal("signalDouble", () -> varDouble.get() + varDouble.get() * 2, varDouble); - final Signal signalBool = new Signal("signalBool", () -> !varBool.get(), varBool); - final Signal signalString = new Signal("signalString", () -> varString1.get() + varString2.get(), varString1, varString2); - - signalInt.addValueChangeListener(val -> System.out.println("signalInt: " + val + " (correct value: 158)")); - signalDouble.addValueChangeListener(val -> System.out.println("signalDouble: " + val + " (correct value: 4.8)")); - signalBool.addValueChangeListener(val -> System.out.println("signalBool: " + val + " (correct value: false)")); - signalString.addValueChangeListener(val -> System.out.println("signalString: " + val + " (correct value: Hello World!)")); - - try { - Thread.sleep(500); - } catch (final InterruptedException e) { - e.printStackTrace(); - } - - varInt.set(100); - varDouble.set(1.6); - varBool.set(true); - varString1.set("Hello "); - varString2.set("World!"); - - } + public static void main(String args[]) { + final Var varInt = new Var<>("varInt", 1); + final Var varDouble = new Var<>("varDouble", 1.0); + final Var varBool = new Var<>("varBool", false); + final Var varString1 = new Var<>("varString1", ""); + final Var varString2 = new Var<>("varString2", ""); + + final Signal signalInt = new Signal("signalInt", + () -> 10 - 2 + (varInt.get() * 2 + varInt.get()) / 2, varInt); + final Signal signalDouble = new Signal("signalDouble", + () -> varDouble.get() + varDouble.get() * 2, varDouble); + final Signal signalBool = new Signal("signalBool", () -> !varBool.get(), varBool); + final Signal signalString = new Signal("signalString", + () -> varString1.get() + varString2.get(), varString1, varString2); + + signalInt.addValueChangeListener(val -> System.out.println("signalInt: " + val + " (correct value: 158)")); + signalDouble + .addValueChangeListener(val -> System.out.println("signalDouble: " + val + " (correct value: 4.8)")); + signalBool.addValueChangeListener(val -> System.out.println("signalBool: " + val + " (correct value: false)")); + signalString.addValueChangeListener( + val -> System.out.println("signalString: " + val + " (correct value: Hello World!)")); + + try { + Thread.sleep(500); + } catch (final InterruptedException e) { + e.printStackTrace(); + } + + varInt.set(100); + varDouble.set(1.6); + varBool.set(true); + varString1.set("Hello "); + varString2.set("World!"); + + } } diff --git a/Dream2/src/examples/java/dream/examples/local/ExampleFilter.java b/Dream2/src/examples/java/dream/examples/local/ExampleFilter.java index e3ff1f5..30c7464 100644 --- a/Dream2/src/examples/java/dream/examples/local/ExampleFilter.java +++ b/Dream2/src/examples/java/dream/examples/local/ExampleFilter.java @@ -4,32 +4,34 @@ import dream.client.Var; public class ExampleFilter { - public static void main(String args[]) { - - final Var varInt = new Var<>("varInt", 1); - final Signal signalInt = new Signal<>("signalInt", () -> varInt.get() + 1, varInt.filter(val -> val > 10)); - final Signal signalInt2 = new Signal<>("signalInt2", () -> signalInt.get() + 1, signalInt.filter(val -> val > 20)); - - signalInt.addValueChangeListener(val -> System.out.println("SignalInt: " + val)); - signalInt2.addValueChangeListener(val -> System.out.println("SignalInt2: " + val)); - - try { - Thread.sleep(500); - } catch (final InterruptedException e) { - e.printStackTrace(); - } - - System.out.println("Expected results:"); - System.out.println("SignalInt: 12"); - System.out.println("SignalInt: 21"); - System.out.println("SignalInt2: 22"); - System.out.println(); - - varInt.set(1); - varInt.set(2); - varInt.set(11); - varInt.set(20); - varInt.set(10); - - } + public static void main(String args[]) { + + final Var varInt = new Var<>("varInt", 1); + final Signal signalInt = new Signal<>("signalInt", () -> varInt.get() + 1, + varInt.filter(val -> val > 10)); + final Signal signalInt2 = new Signal<>("signalInt2", () -> signalInt.get() + 1, + signalInt.filter(val -> val > 20)); + + signalInt.addValueChangeListener(val -> System.out.println("SignalInt: " + val)); + signalInt2.addValueChangeListener(val -> System.out.println("SignalInt2: " + val)); + + try { + Thread.sleep(500); + } catch (final InterruptedException e) { + e.printStackTrace(); + } + + System.out.println("Expected results:"); + System.out.println("SignalInt: 12"); + System.out.println("SignalInt: 21"); + System.out.println("SignalInt2: 22"); + System.out.println(); + + varInt.set(1); + varInt.set(2); + varInt.set(11); + varInt.set(20); + varInt.set(10); + + } } diff --git a/Dream2/src/examples/java/dream/examples/local/ExampleGlitch.java b/Dream2/src/examples/java/dream/examples/local/ExampleGlitch.java index c050628..0e63610 100755 --- a/Dream2/src/examples/java/dream/examples/local/ExampleGlitch.java +++ b/Dream2/src/examples/java/dream/examples/local/ExampleGlitch.java @@ -5,45 +5,45 @@ public class ExampleGlitch { - public static void main(String args[]) { - final ExampleGlitch example = new ExampleGlitch(); - example.launch(); - } - - public void launch() { - final Var var = new Var<>("var", 1.0); - - final Signal mid1 = new Signal<>("mid1", () -> var.get() * 2, var); - final Signal mid2 = new Signal<>("mid2", () -> var.get() * 3, var); - - final Signal finalResult = new Signal<>("final", () -> mid1.get() + mid2.get(), mid1, mid2); - finalResult.addValueChangeListener(System.out::println); - - try { - Thread.sleep(500); - } catch (final InterruptedException e) { - e.printStackTrace(); - } - - final double v1 = 10; - final double v2 = 20; - final double v3 = 30; - - System.out.println("Expected values (with glitch freedom): "); - System.out.println("1) " + (v1 * 2 + v1 * 3)); - System.out.println("2) " + (v2 * 2 + v2 * 3)); - System.out.println("3) " + (v3 * 2 + v3 * 3)); - - try { - Thread.sleep(500); - } catch (final InterruptedException e) { - e.printStackTrace(); - } - - var.set(v1); - var.set(v2); - var.set(v3); - - } + public static void main(String args[]) { + final ExampleGlitch example = new ExampleGlitch(); + example.launch(); + } + + public void launch() { + final Var var = new Var<>("var", 1.0); + + final Signal mid1 = new Signal<>("mid1", () -> var.get() * 2, var); + final Signal mid2 = new Signal<>("mid2", () -> var.get() * 3, var); + + final Signal finalResult = new Signal<>("final", () -> mid1.get() + mid2.get(), mid1, mid2); + finalResult.addValueChangeListener(System.out::println); + + try { + Thread.sleep(500); + } catch (final InterruptedException e) { + e.printStackTrace(); + } + + final double v1 = 10; + final double v2 = 20; + final double v3 = 30; + + System.out.println("Expected values (with glitch freedom): "); + System.out.println("1) " + (v1 * 2 + v1 * 3)); + System.out.println("2) " + (v2 * 2 + v2 * 3)); + System.out.println("3) " + (v3 * 2 + v3 * 3)); + + try { + Thread.sleep(500); + } catch (final InterruptedException e) { + e.printStackTrace(); + } + + var.set(v1); + var.set(v2); + var.set(v3); + + } } diff --git a/Dream2/src/examples/java/dream/examples/local/ExampleList.java b/Dream2/src/examples/java/dream/examples/local/ExampleList.java index cd934ed..fa65fc3 100755 --- a/Dream2/src/examples/java/dream/examples/local/ExampleList.java +++ b/Dream2/src/examples/java/dream/examples/local/ExampleList.java @@ -7,32 +7,32 @@ public class ExampleList { - public static void main(String args[]) { - final Var> varList = new Var<>("varList", new ArrayList()); - final Signal signalInt = new Signal("signalInt", () -> 1000 + varList.get().size(), varList); - - signalInt.addValueChangeListener(System.out::println); - - System.out.println("Expected results: "); - System.out.println(1001); - System.out.println(1002); - System.out.println(1003); - System.out.println(1002); - System.out.println(1000); - System.out.println(); - - try { - Thread.sleep(500); - } catch (final InterruptedException e) { - e.printStackTrace(); - } - - varList.modify(self -> self.add(10)); - varList.modify(self -> self.add(20)); - varList.modify(self -> self.add(30)); - varList.modify(self -> self.remove(1)); - varList.modify(self -> self.clear()); - - } + public static void main(String args[]) { + final Var> varList = new Var<>("varList", new ArrayList()); + final Signal signalInt = new Signal("signalInt", () -> 1000 + varList.get().size(), varList); + + signalInt.addValueChangeListener(System.out::println); + + System.out.println("Expected results: "); + System.out.println(1001); + System.out.println(1002); + System.out.println(1003); + System.out.println(1002); + System.out.println(1000); + System.out.println(); + + try { + Thread.sleep(500); + } catch (final InterruptedException e) { + e.printStackTrace(); + } + + varList.modify(self -> self.add(10)); + varList.modify(self -> self.add(20)); + varList.modify(self -> self.add(30)); + varList.modify(self -> self.remove(1)); + varList.modify(self -> self.clear()); + + } } diff --git a/Dream2/src/examples/java/dream/examples/remote/RemoteSignalExample.java b/Dream2/src/examples/java/dream/examples/remote/RemoteSignalExample.java index ffbf3fb..036710f 100755 --- a/Dream2/src/examples/java/dream/examples/remote/RemoteSignalExample.java +++ b/Dream2/src/examples/java/dream/examples/remote/RemoteSignalExample.java @@ -11,38 +11,41 @@ public class RemoteSignalExample { - public static void main(String args[]) { - Consts.hostName = "Signal"; - - final DreamClient client = DreamClient.instance; - client.connect(); - - final Set relevantRemoteVars = new HashSet<>(); - relevantRemoteVars.add("remoteInt@Remote"); - relevantRemoteVars.add("remoteString1@Remote"); - relevantRemoteVars.add("remoteString2@Remote"); - relevantRemoteVars.add("remoteList@Remote"); - while (!client.listVariables().containsAll(relevantRemoteVars)) { - try { - Thread.sleep(100); - } catch (final InterruptedException e) { - e.printStackTrace(); - } - } - - final RemoteVar remoteInt = new RemoteVar("Remote", "remoteInt"); - final RemoteVar remoteString1 = new RemoteVar("Remote", "remoteString1"); - final RemoteVar remoteString2 = new RemoteVar("Remote", "remoteString2"); - final RemoteVar> remoteList = new RemoteVar>("Remote", "remoteList"); - - final Signal signal1 = new Signal("signal1", () -> remoteInt.get() + remoteString1.get().length(), remoteInt, remoteString1); - final Signal signal2 = new Signal("signal2", () -> remoteInt.get(), remoteInt); - final Signal signal3 = new Signal("signal3", () -> remoteString1.get() + remoteString2.get(), remoteString1, remoteString2); - final Signal signal4 = new Signal("signal4", () -> remoteString1.get().length() + remoteList.get().size(), remoteString1, remoteList); - - signal1.addValueChangeListener(val -> System.out.println("Signal1: " + val)); - signal2.addValueChangeListener(val -> System.out.println("Signal2: " + val)); - signal3.addValueChangeListener(val -> System.out.println("Signal3: " + val)); - signal4.addValueChangeListener(val -> System.out.println("Signal4: " + val)); - } + public static void main(String args[]) { + Consts.hostName = "Signal"; + + final DreamClient client = DreamClient.instance; + client.connect(); + + final Set relevantRemoteVars = new HashSet<>(); + relevantRemoteVars.add("remoteInt@Remote"); + relevantRemoteVars.add("remoteString1@Remote"); + relevantRemoteVars.add("remoteString2@Remote"); + relevantRemoteVars.add("remoteList@Remote"); + while (!client.listVariables().containsAll(relevantRemoteVars)) { + try { + Thread.sleep(100); + } catch (final InterruptedException e) { + e.printStackTrace(); + } + } + + final RemoteVar remoteInt = new RemoteVar("Remote", "remoteInt"); + final RemoteVar remoteString1 = new RemoteVar("Remote", "remoteString1"); + final RemoteVar remoteString2 = new RemoteVar("Remote", "remoteString2"); + final RemoteVar> remoteList = new RemoteVar>("Remote", "remoteList"); + + final Signal signal1 = new Signal("signal1", + () -> remoteInt.get() + remoteString1.get().length(), remoteInt, remoteString1); + final Signal signal2 = new Signal("signal2", () -> remoteInt.get(), remoteInt); + final Signal signal3 = new Signal("signal3", () -> remoteString1.get() + remoteString2.get(), + remoteString1, remoteString2); + final Signal signal4 = new Signal("signal4", + () -> remoteString1.get().length() + remoteList.get().size(), remoteString1, remoteList); + + signal1.addValueChangeListener(val -> System.out.println("Signal1: " + val)); + signal2.addValueChangeListener(val -> System.out.println("Signal2: " + val)); + signal3.addValueChangeListener(val -> System.out.println("Signal3: " + val)); + signal4.addValueChangeListener(val -> System.out.println("Signal4: " + val)); + } } diff --git a/Dream2/src/examples/java/dream/examples/remote/RemoteVarExample.java b/Dream2/src/examples/java/dream/examples/remote/RemoteVarExample.java index b984be7..2facf23 100755 --- a/Dream2/src/examples/java/dream/examples/remote/RemoteVarExample.java +++ b/Dream2/src/examples/java/dream/examples/remote/RemoteVarExample.java @@ -8,32 +8,32 @@ public class RemoteVarExample { - public static void main(String args[]) { - Consts.hostName = "Remote"; - final Var remoteInt = new Var("remoteInt", 1); - final Var remoteString1 = new Var("remoteString1", "a"); - final Var remoteString2 = new Var("remoteString2", "b"); - final Var> remoteList = new Var>("remoteList", new ArrayList()); - final Random random = new Random(); - - try { - Thread.sleep(5000); - } catch (final InterruptedException e) { - e.printStackTrace(); - } - - while (true) { - remoteInt.set(random.nextInt(1000)); - remoteString1.set(String.valueOf(random.nextInt(10)) + " "); - remoteString2.set(String.valueOf(random.nextInt(10)) + "!"); - remoteList.modify(t -> t.add(random.nextInt(1000))); - try { - Thread.sleep(2000); - } catch (final InterruptedException e) { - e.printStackTrace(); - } - } - - } + public static void main(String args[]) { + Consts.hostName = "Remote"; + final Var remoteInt = new Var("remoteInt", 1); + final Var remoteString1 = new Var("remoteString1", "a"); + final Var remoteString2 = new Var("remoteString2", "b"); + final Var> remoteList = new Var>("remoteList", new ArrayList()); + final Random random = new Random(); + + try { + Thread.sleep(5000); + } catch (final InterruptedException e) { + e.printStackTrace(); + } + + while (true) { + remoteInt.set(random.nextInt(1000)); + remoteString1.set(String.valueOf(random.nextInt(10)) + " "); + remoteString2.set(String.valueOf(random.nextInt(10)) + "!"); + remoteList.modify(t -> t.add(random.nextInt(1000))); + try { + Thread.sleep(2000); + } catch (final InterruptedException e) { + e.printStackTrace(); + } + } + + } } diff --git a/Dream2/src/main/java/dream/client/ClientEventForwarder.java b/Dream2/src/main/java/dream/client/ClientEventForwarder.java index 0db6df8..91ed80f 100755 --- a/Dream2/src/main/java/dream/client/ClientEventForwarder.java +++ b/Dream2/src/main/java/dream/client/ClientEventForwarder.java @@ -33,294 +33,298 @@ import polimi.reds.broker.routing.PacketForwarder; class ClientEventForwarder implements PacketForwarder { - private static ClientEventForwarder self = null; - - private final ConnectionManager connectionManager; - private final ClientSubscriptionTable subTable; - - // Dependency graph - private final DependencyGraph dependencyGraph = DependencyGraph.instance; - - // Dependency detectors - private final IntraSourceDependencyDetector intraDepDetector = IntraSourceDependencyDetector.instance; - private final InterSourceDependencyDetector interDepDetector = // - Consts.consistencyType == ConsistencyType.ATOMIC // - ? new AtomicDependencyDetector() // - : new CompleteGlitchFreeDependencyDetector(); - private final FinalNodesDetector finalNodesDetector = new FinalNodesDetector(); - - // Lock applicants waiting for a grant - private final Map lockApplicants = new HashMap<>(); - - private final Logger logger = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME); - - static final ClientEventForwarder get() { - if (self == null) { - self = new ClientEventForwarder(); - } - return self; - } - - static final void stop() { - if (self != null) { - self.stopClient(); - self = null; - } - } - - private final void stopClient() { - connectionManager.stop(); - } - - private ClientEventForwarder() { - connectionManager = new ConnectionManager(); - subTable = new ClientSubscriptionTable(); - connectionManager.registerForwarder(this, AdvertisementPacket.subject); - connectionManager.registerForwarder(this, SubscriptionPacket.subject); - connectionManager.registerForwarder(this, EventPacket.subject); - connectionManager.registerForwarder(this, LockGrantPacket.subject); - } - - @Override - public Collection forwardPacket(String subject, NodeDescriptor sender, Serializable packet, Collection neighbors, Outbox outbox) { - final Collection result = new ArrayList(); - if (subject.equals(AdvertisementPacket.subject)) { - assert packet instanceof AdvertisementPacket; - logger.finer("Received an advertisement packet " + packet); - processAdvertisementFromServer((AdvertisementPacket) packet); - } else if (subject.equals(SubscriptionPacket.subject)) { - assert packet instanceof SubscriptionPacket; - logger.fine("Received a subscription packet " + packet); - processSubscriptionFromServer((SubscriptionPacket) packet); - } else if (subject.equals(EventPacket.subject)) { - assert packet instanceof EventPacket; - logger.finer("Received an event packet " + packet); - processEventFromServer((EventPacket) packet); - } else if (subject.equals(LockGrantPacket.subject)) { - assert packet instanceof LockGrantPacket; - logger.finer("Received lock grant packet " + packet); - processLockGrant((LockGrantPacket) packet); - } else { - assert false : subject; - } - return result; - } - - final void sendEvent(UUID id, Event ev, String initialVar) { - logger.finer("Sending an event " + ev); - Set lockReleaseNodes; - switch (Consts.consistencyType) { - case COMPLETE_GLITCH_FREE: - lockReleaseNodes = interDepDetector.getNodesToLockFor(initialVar); - break; - case ATOMIC: - lockReleaseNodes = finalNodesDetector.getFinalNodesFor(initialVar); - break; - default: - lockReleaseNodes = new HashSet<>(); - } - - if (subTable.needsToDeliverToServer(ev)) { - connectionManager.sendEvent(id, ev, initialVar, lockReleaseNodes); - } - } - - /** - * Return false if the lock request is not needed - */ - final void sendReadOnlyLockRequest(String node, LockApplicant applicant) { - if (Consts.consistencyType != ConsistencyType.ATOMIC) { - assert false : Consts.consistencyType; - logger.warning("Invoked sendReadOnlyLockRequest() even if the consistency level does not require it."); - return; - } - - logger.finer("Invoked sendReadOnlyLockRequest for node " + node); - final Set nodesToLock = new HashSet<>(); - nodesToLock.add(node); - - final LockRequestPacket reqPkt = new LockRequestPacket(connectionManager.getNodeDescriptor(), nodesToLock, nodesToLock, LockType.READ_ONLY); - final UUID lockId = reqPkt.getLockID(); - lockApplicants.put(lockId, applicant); - - connectionManager.sendLockRequest(reqPkt); - } - - /** - * Return false if the lock request is not needed - */ - final boolean sendReadWriteLockRequest(String source, LockApplicant applicant) { - if (Consts.consistencyType != ConsistencyType.COMPLETE_GLITCH_FREE && // - Consts.consistencyType != ConsistencyType.ATOMIC) { - assert false : Consts.consistencyType; - logger.warning("Invoked sendReadWriteLockRequest() even if the consistency level does not require it."); - return false; - } - - logger.finer("Invoked sendReadWriteLockRequest for source " + source); - final Set nodesToLock = interDepDetector.getNodesToLockFor(source); - final Set releaseNodes = getLockReleaseNodesFor(source); - - if (nodesToLock.isEmpty()) { - return false; - } - - final LockRequestPacket reqPkt = new LockRequestPacket(connectionManager.getNodeDescriptor(), nodesToLock, releaseNodes, LockType.READ_WRITE); - final UUID lockId = reqPkt.getLockID(); - lockApplicants.put(lockId, applicant); - - connectionManager.sendLockRequest(reqPkt); - return true; - } - - final Set getLockReleaseNodesFor(String source) { - switch (Consts.consistencyType) { - case COMPLETE_GLITCH_FREE: - return interDepDetector.getNodesToLockFor(source); - case ATOMIC: - return finalNodesDetector.getFinalNodesFor(source); - default: - return new HashSet<>(); - } - } - - final void sendLockRelease(UUID lockID) { - if (Consts.consistencyType != ConsistencyType.COMPLETE_GLITCH_FREE && // - Consts.consistencyType != ConsistencyType.ATOMIC) { - assert false : Consts.consistencyType; - logger.warning("Invoked sendLockRelease() even if the consistency level does not require it."); - return; - } - - connectionManager.sendLockRelease(new LockReleasePacket(lockID)); - } - - final void advertise(Advertisement adv, boolean isPublic) { - logger.fine("Sending advertisement " + adv); - if (Consts.consistencyType == ConsistencyType.SINGLE_SOURCE_GLITCH_FREE || // - Consts.consistencyType == ConsistencyType.COMPLETE_GLITCH_FREE || // - Consts.consistencyType == ConsistencyType.ATOMIC) { - dependencyGraph.processAdv(adv); - updateDetectors(); - } - connectionManager.sendAdvertisement(adv, isPublic); - } - - final void unadvertise(Advertisement adv, boolean isPublic) { - logger.fine("Sending unadvertisement " + adv); - if (Consts.consistencyType == ConsistencyType.SINGLE_SOURCE_GLITCH_FREE || // - Consts.consistencyType == ConsistencyType.COMPLETE_GLITCH_FREE || // - Consts.consistencyType == ConsistencyType.ATOMIC) { - dependencyGraph.processUnAdv(adv); - updateDetectors(); - } - connectionManager.sendUnadvertisement(adv, isPublic); - } - - final void advertise(Advertisement adv, Set subs, boolean isPublic) { - logger.fine("Sending advertisement " + adv + " with subscriptions " + subs); - if (Consts.consistencyType == ConsistencyType.SINGLE_SOURCE_GLITCH_FREE || // - Consts.consistencyType == ConsistencyType.COMPLETE_GLITCH_FREE || // - Consts.consistencyType == ConsistencyType.ATOMIC) { - dependencyGraph.processAdv(adv, subs); - updateDetectors(); - } - connectionManager.sendAdvertisement(adv, subs, isPublic); - } - - final void unadvertise(Advertisement adv, Set subs, boolean isPublic) { - logger.fine("Sending unadvertisement " + adv + " with subscriptions " + subs); - if (Consts.consistencyType == ConsistencyType.SINGLE_SOURCE_GLITCH_FREE || // - Consts.consistencyType == ConsistencyType.COMPLETE_GLITCH_FREE || // - Consts.consistencyType == ConsistencyType.ATOMIC) { - dependencyGraph.processUnAdv(adv, subs); - updateDetectors(); - } - connectionManager.sendUnadvertisement(adv, isPublic); - } - - final void addSubscription(Subscriber subscriber, Subscription subscription) { - logger.fine("Adding subscription " + subscription); - subTable.addSubscription(subscriber, subscription); - if (needToSendToServer(subscription)) { - connectionManager.sendSubscription(subscription); - } - } - - final void removeSubscription(Subscriber subscriber, Subscription subscription) { - logger.fine("Removing subscription " + subscription); - subTable.addSubscription(subscriber, subscription); - if (needToSendToServer(subscription)) { - connectionManager.sendSubscription(subscription); - } - } - - private final boolean needToSendToServer(Subscription sub) { - return !isLocal(sub); - } - - private final boolean isLocal(Subscription sub) { - return sub.getHostId().equals(Consts.hostName); - } - - private final void processEventFromServer(EventPacket evPkt) { - subTable.getMatchingSubscribers(evPkt.getEvent()).forEach(sub -> sub.notifyEventReceived(evPkt)); - if (Consts.consistencyType == ConsistencyType.SINGLE_SOURCE_GLITCH_FREE || // - Consts.consistencyType == ConsistencyType.COMPLETE_GLITCH_FREE || // - Consts.consistencyType == ConsistencyType.ATOMIC) { - subTable.getSignatureOnlyMatchingSubscribers(evPkt.getEvent()).forEach(sub -> sub.notifyEventReceived(evPkt)); - } - } - - private final void processAdvertisementFromServer(AdvertisementPacket advPkt) { - if (Consts.consistencyType == ConsistencyType.SINGLE_SOURCE_GLITCH_FREE || // - Consts.consistencyType == ConsistencyType.COMPLETE_GLITCH_FREE || // - Consts.consistencyType == ConsistencyType.ATOMIC) { - final Set subs = advPkt.getSubscriptions(); - switch (advPkt.getAdvType()) { - case ADV: - if (subs.isEmpty()) { - dependencyGraph.processAdv(advPkt.getAdvertisement()); - } else { - dependencyGraph.processAdv(advPkt.getAdvertisement(), subs); - } - break; - case UNADV: - if (subs.isEmpty()) { - dependencyGraph.processUnAdv(advPkt.getAdvertisement()); - } else { - dependencyGraph.processUnAdv(advPkt.getAdvertisement(), subs); - } - break; - } - updateDetectors(); - } - } - - private final void processSubscriptionFromServer(SubscriptionPacket subPkt) { - switch (subPkt.getSubType()) { - case SUB: - subTable.addServerSubscription(subPkt.getSubscription()); - break; - case UNSUB: - subTable.removeServerSubscription(subPkt.getSubscription()); - break; - default: - assert false : subPkt.getSubType(); - } - } - - private final void processLockGrant(LockGrantPacket lockGrant) { - final UUID lockID = lockGrant.getLockID(); - assert lockApplicants.containsKey(lockID); - final LockApplicant applicant = lockApplicants.remove(lockID); - applicant.notifyLockGranted(lockGrant); - } - - private final void updateDetectors() { - finalNodesDetector.consolidate(); - intraDepDetector.consolidate(); - interDepDetector.consolidate(); - } + private static ClientEventForwarder self = null; + + private final ConnectionManager connectionManager; + private final ClientSubscriptionTable subTable; + + // Dependency graph + private final DependencyGraph dependencyGraph = DependencyGraph.instance; + + // Dependency detectors + private final IntraSourceDependencyDetector intraDepDetector = IntraSourceDependencyDetector.instance; + private final InterSourceDependencyDetector interDepDetector = // + Consts.consistencyType == ConsistencyType.ATOMIC // + ? new AtomicDependencyDetector() // + : new CompleteGlitchFreeDependencyDetector(); + private final FinalNodesDetector finalNodesDetector = new FinalNodesDetector(); + + // Lock applicants waiting for a grant + private final Map lockApplicants = new HashMap<>(); + + private final Logger logger = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME); + + static final ClientEventForwarder get() { + if (self == null) { + self = new ClientEventForwarder(); + } + return self; + } + + static final void stop() { + if (self != null) { + self.stopClient(); + self = null; + } + } + + private final void stopClient() { + connectionManager.stop(); + } + + private ClientEventForwarder() { + connectionManager = new ConnectionManager(); + subTable = new ClientSubscriptionTable(); + connectionManager.registerForwarder(this, AdvertisementPacket.subject); + connectionManager.registerForwarder(this, SubscriptionPacket.subject); + connectionManager.registerForwarder(this, EventPacket.subject); + connectionManager.registerForwarder(this, LockGrantPacket.subject); + } + + @Override + public Collection forwardPacket(String subject, NodeDescriptor sender, Serializable packet, + Collection neighbors, Outbox outbox) { + final Collection result = new ArrayList(); + if (subject.equals(AdvertisementPacket.subject)) { + assert packet instanceof AdvertisementPacket; + logger.finer("Received an advertisement packet " + packet); + processAdvertisementFromServer((AdvertisementPacket) packet); + } else if (subject.equals(SubscriptionPacket.subject)) { + assert packet instanceof SubscriptionPacket; + logger.fine("Received a subscription packet " + packet); + processSubscriptionFromServer((SubscriptionPacket) packet); + } else if (subject.equals(EventPacket.subject)) { + assert packet instanceof EventPacket; + logger.finer("Received an event packet " + packet); + processEventFromServer((EventPacket) packet); + } else if (subject.equals(LockGrantPacket.subject)) { + assert packet instanceof LockGrantPacket; + logger.finer("Received lock grant packet " + packet); + processLockGrant((LockGrantPacket) packet); + } else { + assert false : subject; + } + return result; + } + + final void sendEvent(UUID id, Event ev, String initialVar) { + logger.finer("Sending an event " + ev); + Set lockReleaseNodes; + switch (Consts.consistencyType) { + case COMPLETE_GLITCH_FREE: + lockReleaseNodes = interDepDetector.getNodesToLockFor(initialVar); + break; + case ATOMIC: + lockReleaseNodes = finalNodesDetector.getFinalNodesFor(initialVar); + break; + default: + lockReleaseNodes = new HashSet<>(); + } + + if (subTable.needsToDeliverToServer(ev)) { + connectionManager.sendEvent(id, ev, initialVar, lockReleaseNodes); + } + } + + /** + * Return false if the lock request is not needed + */ + final void sendReadOnlyLockRequest(String node, LockApplicant applicant) { + if (Consts.consistencyType != ConsistencyType.ATOMIC) { + assert false : Consts.consistencyType; + logger.warning("Invoked sendReadOnlyLockRequest() even if the consistency level does not require it."); + return; + } + + logger.finer("Invoked sendReadOnlyLockRequest for node " + node); + final Set nodesToLock = new HashSet<>(); + nodesToLock.add(node); + + final LockRequestPacket reqPkt = new LockRequestPacket(connectionManager.getNodeDescriptor(), nodesToLock, + nodesToLock, LockType.READ_ONLY); + final UUID lockId = reqPkt.getLockID(); + lockApplicants.put(lockId, applicant); + + connectionManager.sendLockRequest(reqPkt); + } + + /** + * Return false if the lock request is not needed + */ + final boolean sendReadWriteLockRequest(String source, LockApplicant applicant) { + if (Consts.consistencyType != ConsistencyType.COMPLETE_GLITCH_FREE && // + Consts.consistencyType != ConsistencyType.ATOMIC) { + assert false : Consts.consistencyType; + logger.warning("Invoked sendReadWriteLockRequest() even if the consistency level does not require it."); + return false; + } + + logger.finer("Invoked sendReadWriteLockRequest for source " + source); + final Set nodesToLock = interDepDetector.getNodesToLockFor(source); + final Set releaseNodes = getLockReleaseNodesFor(source); + + if (nodesToLock.isEmpty()) { + return false; + } + + final LockRequestPacket reqPkt = new LockRequestPacket(connectionManager.getNodeDescriptor(), nodesToLock, + releaseNodes, LockType.READ_WRITE); + final UUID lockId = reqPkt.getLockID(); + lockApplicants.put(lockId, applicant); + + connectionManager.sendLockRequest(reqPkt); + return true; + } + + final Set getLockReleaseNodesFor(String source) { + switch (Consts.consistencyType) { + case COMPLETE_GLITCH_FREE: + return interDepDetector.getNodesToLockFor(source); + case ATOMIC: + return finalNodesDetector.getFinalNodesFor(source); + default: + return new HashSet<>(); + } + } + + final void sendLockRelease(UUID lockID) { + if (Consts.consistencyType != ConsistencyType.COMPLETE_GLITCH_FREE && // + Consts.consistencyType != ConsistencyType.ATOMIC) { + assert false : Consts.consistencyType; + logger.warning("Invoked sendLockRelease() even if the consistency level does not require it."); + return; + } + + connectionManager.sendLockRelease(new LockReleasePacket(lockID)); + } + + final void advertise(Advertisement adv, boolean isPublic) { + logger.fine("Sending advertisement " + adv); + if (Consts.consistencyType == ConsistencyType.SINGLE_SOURCE_GLITCH_FREE || // + Consts.consistencyType == ConsistencyType.COMPLETE_GLITCH_FREE || // + Consts.consistencyType == ConsistencyType.ATOMIC) { + dependencyGraph.processAdv(adv); + updateDetectors(); + } + connectionManager.sendAdvertisement(adv, isPublic); + } + + final void unadvertise(Advertisement adv, boolean isPublic) { + logger.fine("Sending unadvertisement " + adv); + if (Consts.consistencyType == ConsistencyType.SINGLE_SOURCE_GLITCH_FREE || // + Consts.consistencyType == ConsistencyType.COMPLETE_GLITCH_FREE || // + Consts.consistencyType == ConsistencyType.ATOMIC) { + dependencyGraph.processUnAdv(adv); + updateDetectors(); + } + connectionManager.sendUnadvertisement(adv, isPublic); + } + + final void advertise(Advertisement adv, Set subs, boolean isPublic) { + logger.fine("Sending advertisement " + adv + " with subscriptions " + subs); + if (Consts.consistencyType == ConsistencyType.SINGLE_SOURCE_GLITCH_FREE || // + Consts.consistencyType == ConsistencyType.COMPLETE_GLITCH_FREE || // + Consts.consistencyType == ConsistencyType.ATOMIC) { + dependencyGraph.processAdv(adv, subs); + updateDetectors(); + } + connectionManager.sendAdvertisement(adv, subs, isPublic); + } + + final void unadvertise(Advertisement adv, Set subs, boolean isPublic) { + logger.fine("Sending unadvertisement " + adv + " with subscriptions " + subs); + if (Consts.consistencyType == ConsistencyType.SINGLE_SOURCE_GLITCH_FREE || // + Consts.consistencyType == ConsistencyType.COMPLETE_GLITCH_FREE || // + Consts.consistencyType == ConsistencyType.ATOMIC) { + dependencyGraph.processUnAdv(adv, subs); + updateDetectors(); + } + connectionManager.sendUnadvertisement(adv, isPublic); + } + + final void addSubscription(Subscriber subscriber, Subscription subscription) { + logger.fine("Adding subscription " + subscription); + subTable.addSubscription(subscriber, subscription); + if (needToSendToServer(subscription)) { + connectionManager.sendSubscription(subscription); + } + } + + final void removeSubscription(Subscriber subscriber, Subscription subscription) { + logger.fine("Removing subscription " + subscription); + subTable.addSubscription(subscriber, subscription); + if (needToSendToServer(subscription)) { + connectionManager.sendSubscription(subscription); + } + } + + private final boolean needToSendToServer(Subscription sub) { + return !isLocal(sub); + } + + private final boolean isLocal(Subscription sub) { + return sub.getHostId().equals(Consts.hostName); + } + + private final void processEventFromServer(EventPacket evPkt) { + subTable.getMatchingSubscribers(evPkt.getEvent()).forEach(sub -> sub.notifyEventReceived(evPkt)); + if (Consts.consistencyType == ConsistencyType.SINGLE_SOURCE_GLITCH_FREE || // + Consts.consistencyType == ConsistencyType.COMPLETE_GLITCH_FREE || // + Consts.consistencyType == ConsistencyType.ATOMIC) { + subTable.getSignatureOnlyMatchingSubscribers(evPkt.getEvent()) + .forEach(sub -> sub.notifyEventReceived(evPkt)); + } + } + + private final void processAdvertisementFromServer(AdvertisementPacket advPkt) { + if (Consts.consistencyType == ConsistencyType.SINGLE_SOURCE_GLITCH_FREE || // + Consts.consistencyType == ConsistencyType.COMPLETE_GLITCH_FREE || // + Consts.consistencyType == ConsistencyType.ATOMIC) { + final Set subs = advPkt.getSubscriptions(); + switch (advPkt.getAdvType()) { + case ADV: + if (subs.isEmpty()) { + dependencyGraph.processAdv(advPkt.getAdvertisement()); + } else { + dependencyGraph.processAdv(advPkt.getAdvertisement(), subs); + } + break; + case UNADV: + if (subs.isEmpty()) { + dependencyGraph.processUnAdv(advPkt.getAdvertisement()); + } else { + dependencyGraph.processUnAdv(advPkt.getAdvertisement(), subs); + } + break; + } + updateDetectors(); + } + } + + private final void processSubscriptionFromServer(SubscriptionPacket subPkt) { + switch (subPkt.getSubType()) { + case SUB: + subTable.addServerSubscription(subPkt.getSubscription()); + break; + case UNSUB: + subTable.removeServerSubscription(subPkt.getSubscription()); + break; + default: + assert false : subPkt.getSubType(); + } + } + + private final void processLockGrant(LockGrantPacket lockGrant) { + final UUID lockID = lockGrant.getLockID(); + assert lockApplicants.containsKey(lockID); + final LockApplicant applicant = lockApplicants.remove(lockID); + applicant.notifyLockGranted(lockGrant); + } + + private final void updateDetectors() { + finalNodesDetector.consolidate(); + intraDepDetector.consolidate(); + interDepDetector.consolidate(); + } } diff --git a/Dream2/src/main/java/dream/client/ClientSubscriptionTable.java b/Dream2/src/main/java/dream/client/ClientSubscriptionTable.java index 26938da..3119af8 100755 --- a/Dream2/src/main/java/dream/client/ClientSubscriptionTable.java +++ b/Dream2/src/main/java/dream/client/ClientSubscriptionTable.java @@ -12,65 +12,67 @@ import dream.common.packets.content.Subscription; final class ClientSubscriptionTable { - private final Map> subs = new HashMap>(); - private final Collection serverSubscriptions = new ArrayList(); + private final Map> subs = new HashMap>(); + private final Collection serverSubscriptions = new ArrayList(); - final void addSubscription(Subscriber subscriber, Subscription sub) { - Collection subsList = subs.get(subscriber); - if (subsList == null) { - subsList = new ArrayList(); - subs.put(subscriber, subsList); - } - subsList.add(sub); - } + final void addSubscription(Subscriber subscriber, Subscription sub) { + Collection subsList = subs.get(subscriber); + if (subsList == null) { + subsList = new ArrayList(); + subs.put(subscriber, subsList); + } + subsList.add(sub); + } - final void addSubscriptions(Subscriber subscriber, Collection subs) { - subs.forEach(sub -> addSubscription(subscriber, sub)); - } + final void addSubscriptions(Subscriber subscriber, Collection subs) { + subs.forEach(sub -> addSubscription(subscriber, sub)); + } - final void addServerSubscription(Subscription sub) { - serverSubscriptions.add(sub); - } + final void addServerSubscription(Subscription sub) { + serverSubscriptions.add(sub); + } - final void removeSubscription(Subscriber subscriber, Subscription sub) { - final Collection subscriptions = subs.get(subscriber); - if (subscriptions == null) { - return; - } - subscriptions.remove(sub); - if (subscriptions.isEmpty()) { - subs.remove(subscriber); - } - } + final void removeSubscription(Subscriber subscriber, Subscription sub) { + final Collection subscriptions = subs.get(subscriber); + if (subscriptions == null) { + return; + } + subscriptions.remove(sub); + if (subscriptions.isEmpty()) { + subs.remove(subscriber); + } + } - final void removeSubscriptions(Subscriber subscriber, Collection subs) { - subs.forEach(sub -> removeSubscription(subscriber, sub)); - } + final void removeSubscriptions(Subscriber subscriber, Collection subs) { + subs.forEach(sub -> removeSubscription(subscriber, sub)); + } - final void removeServerSubscription(Subscription sub) { - serverSubscriptions.remove(sub); - } + final void removeServerSubscription(Subscription sub) { + serverSubscriptions.remove(sub); + } - final Set getMatchingSubscribers(Event ev) { - return getSubscribersWithAnySubscriptionMatching(sub -> sub.isSatisfiedBy(ev)); - } + final Set getMatchingSubscribers(Event ev) { + return getSubscribersWithAnySubscriptionMatching(sub -> sub.isSatisfiedBy(ev)); + } - final Set getSignatureOnlyMatchingSubscribers(Event ev) { - return getSubscribersWithAnySubscriptionMatching(sub -> sub.matchesOnlySignatureOf(ev) && !sub.isSatisfiedBy(ev)); - } + final Set getSignatureOnlyMatchingSubscribers(Event ev) { + return getSubscribersWithAnySubscriptionMatching( + sub -> sub.matchesOnlySignatureOf(ev) && !sub.isSatisfiedBy(ev)); + } - private final Set getSubscribersWithAnySubscriptionMatching(Predicate predicate) { - final Predicate hasSubscriptionMatchingOnlySignature = subscriber -> subs.get(subscriber).stream().anyMatch(predicate); - return subs.keySet().stream().filter(hasSubscriptionMatchingOnlySignature).collect(Collectors.toSet()); - } + private final Set getSubscribersWithAnySubscriptionMatching(Predicate predicate) { + final Predicate hasSubscriptionMatchingOnlySignature = subscriber -> subs.get(subscriber).stream() + .anyMatch(predicate); + return subs.keySet().stream().filter(hasSubscriptionMatchingOnlySignature).collect(Collectors.toSet()); + } - final boolean needsToDeliverToServer(Event ev) { - return serverSubscriptions.stream().anyMatch(sub -> sub.isSatisfiedBy(ev)); - } + final boolean needsToDeliverToServer(Event ev) { + return serverSubscriptions.stream().anyMatch(sub -> sub.isSatisfiedBy(ev)); + } - @Override - public String toString() { - return "ClientSubscriptionTable [\n subs=" + subs + "\n serverSubscriptions=" + serverSubscriptions + "\n]"; - } + @Override + public String toString() { + return "ClientSubscriptionTable [\n subs=" + subs + "\n serverSubscriptions=" + serverSubscriptions + "\n]"; + } } diff --git a/Dream2/src/main/java/dream/client/ConnectionManager.java b/Dream2/src/main/java/dream/client/ConnectionManager.java index d57b7cb..b3c91bb 100755 --- a/Dream2/src/main/java/dream/client/ConnectionManager.java +++ b/Dream2/src/main/java/dream/client/ConnectionManager.java @@ -39,158 +39,161 @@ import polimi.reds.broker.routing.PacketForwarder; class ConnectionManager implements PacketForwarder { - private final Overlay overlay; - private final GenericRouter router; - - private NodeDescriptor server = null; - private NodeDescriptor lockManager = null; - - private final Queue serverQueue = new LinkedList<>(); - private final Queue lockManagerQueue = new LinkedList<>(); - - private final Logger logger = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME); - - ConnectionManager() { - Transport tr = null; - try { - tr = new TCPTransport(); - } catch (final IOException e) { - e.printStackTrace(); - } - final TopologyManager tm = new SimpleTopologyManager(); - overlay = new GenericOverlay(tm, tr, false); - router = new GenericRouter(overlay); - router.setPacketForwarder(ServerHelloPacket.subject, this); - router.setPacketForwarder(LockManagerHelloPacket.subject, this); - overlay.start(); - try { - overlay.addNeighbor(Consts.serverAddr); - if (Consts.consistencyType == ConsistencyType.COMPLETE_GLITCH_FREE || // - Consts.consistencyType == ConsistencyType.ATOMIC) { - overlay.addNeighbor(Consts.lockManagerAddr); - } - } catch (ConnectException | MalformedURLException | NotRunningException e) { - e.printStackTrace(); - } - } - - final NodeDescriptor getNodeDescriptor() { - return overlay.getNodeDescriptor(); - } - - final void sendEvent(UUID id, Event event, String initialVar, Set lockReleaseNodes) { - final EventPacket pkt = new EventPacket(event, id, initialVar); - pkt.setLockReleaseNodes(lockReleaseNodes); - sendToServer(EventPacket.subject, pkt); - } - - final void sendSubscription(Subscription sub) { - final SubscriptionPacket pkt = new SubscriptionPacket(sub, SubType.SUB); - sendToServer(SubscriptionPacket.subject, pkt); - } - - final void sendUnsubscription(Subscription sub) { - final SubscriptionPacket pkt = new SubscriptionPacket(sub, SubType.UNSUB); - sendToServer(SubscriptionPacket.subject, pkt); - } - - final void sendAdvertisement(Advertisement adv, boolean isPublic) { - sendAdvertisement(adv, AdvType.ADV, null, isPublic); - } - - final void sendAdvertisement(Advertisement adv, Set subs, boolean isPublic) { - sendAdvertisement(adv, AdvType.ADV, subs, isPublic); - } - - final void sendUnadvertisement(Advertisement adv, boolean isPublic) { - sendAdvertisement(adv, AdvType.UNADV, null, isPublic); - } - - final void sendUnadvertisement(Advertisement adv, Set subs, boolean isPublic) { - sendAdvertisement(adv, AdvType.UNADV, subs, isPublic); - } - - final void sendLockRequest(LockRequestPacket req) { - sendToLockManager(LockRequestPacket.subject, req); - } - - final void sendLockRelease(LockReleasePacket rel) { - sendToLockManager(LockReleasePacket.subject, rel); - } - - private final void sendAdvertisement(Advertisement adv, AdvType advType, Set subs, boolean isPublic) { - final AdvertisementPacket pkt = subs != null ? new AdvertisementPacket(adv, advType, subs, isPublic) : new AdvertisementPacket(adv, advType, isPublic); - sendToServer(AdvertisementPacket.subject, pkt); - } - - final void registerForwarder(PacketForwarder forwarder, String subject) { - router.setPacketForwarder(subject, forwarder); - } - - final void stop() { - overlay.stop(); - } - - private final void sendToServer(String subject, Serializable packet) { - if (server == null) { - serverQueue.add(new PacketSubjectPair(subject, packet)); - } else { - try { - overlay.send(subject, packet, server); - } catch (IOException | NotRunningException e) { - e.printStackTrace(); - } - } - } - - private final void sendToLockManager(String subject, Serializable packet) { - if (lockManager == null) { - lockManagerQueue.add(new PacketSubjectPair(subject, packet)); - } else { - try { - overlay.send(subject, packet, lockManager); - } catch (IOException | NotRunningException e) { - e.printStackTrace(); - } - } - } - - @Override - public final Collection forwardPacket(String subject, NodeDescriptor sender, Serializable packet, Collection neighbors, Outbox outbox) { - if (subject.equals(ServerHelloPacket.subject)) { - logger.info("Received server hello packet"); - assert packet instanceof ServerHelloPacket; - server = sender; - serverQueue.forEach(p -> sendToServer(p.getSubject(), p.getPacket())); - } else if (subject.equals(LockManagerHelloPacket.subject)) { - logger.info("Received lock manager hello packet"); - assert packet instanceof LockManagerHelloPacket; - lockManager = sender; - lockManagerQueue.forEach(p -> sendToLockManager(p.getSubject(), p.getPacket())); - } else { - assert false : subject; - } - return new ArrayList<>(); - } - - private class PacketSubjectPair { - private final String subject; - private final Serializable packet; - - public PacketSubjectPair(String subject, Serializable packet) { - super(); - this.subject = subject; - this.packet = packet; - } - - final String getSubject() { - return subject; - } - - final Serializable getPacket() { - return packet; - } - - } + private final Overlay overlay; + private final GenericRouter router; + + private NodeDescriptor server = null; + private NodeDescriptor lockManager = null; + + private final Queue serverQueue = new LinkedList<>(); + private final Queue lockManagerQueue = new LinkedList<>(); + + private final Logger logger = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME); + + ConnectionManager() { + Transport tr = null; + try { + tr = new TCPTransport(); + } catch (final IOException e) { + e.printStackTrace(); + } + final TopologyManager tm = new SimpleTopologyManager(); + overlay = new GenericOverlay(tm, tr, false); + router = new GenericRouter(overlay); + router.setPacketForwarder(ServerHelloPacket.subject, this); + router.setPacketForwarder(LockManagerHelloPacket.subject, this); + overlay.start(); + try { + overlay.addNeighbor(Consts.serverAddr); + if (Consts.consistencyType == ConsistencyType.COMPLETE_GLITCH_FREE || // + Consts.consistencyType == ConsistencyType.ATOMIC) { + overlay.addNeighbor(Consts.lockManagerAddr); + } + } catch (ConnectException | MalformedURLException | NotRunningException e) { + e.printStackTrace(); + } + } + + final NodeDescriptor getNodeDescriptor() { + return overlay.getNodeDescriptor(); + } + + final void sendEvent(UUID id, Event event, String initialVar, + Set lockReleaseNodes) { + final EventPacket pkt = new EventPacket(event, id, initialVar); + pkt.setLockReleaseNodes(lockReleaseNodes); + sendToServer(EventPacket.subject, pkt); + } + + final void sendSubscription(Subscription sub) { + final SubscriptionPacket pkt = new SubscriptionPacket(sub, SubType.SUB); + sendToServer(SubscriptionPacket.subject, pkt); + } + + final void sendUnsubscription(Subscription sub) { + final SubscriptionPacket pkt = new SubscriptionPacket(sub, SubType.UNSUB); + sendToServer(SubscriptionPacket.subject, pkt); + } + + final void sendAdvertisement(Advertisement adv, boolean isPublic) { + sendAdvertisement(adv, AdvType.ADV, null, isPublic); + } + + final void sendAdvertisement(Advertisement adv, Set subs, boolean isPublic) { + sendAdvertisement(adv, AdvType.ADV, subs, isPublic); + } + + final void sendUnadvertisement(Advertisement adv, boolean isPublic) { + sendAdvertisement(adv, AdvType.UNADV, null, isPublic); + } + + final void sendUnadvertisement(Advertisement adv, Set subs, boolean isPublic) { + sendAdvertisement(adv, AdvType.UNADV, subs, isPublic); + } + + final void sendLockRequest(LockRequestPacket req) { + sendToLockManager(LockRequestPacket.subject, req); + } + + final void sendLockRelease(LockReleasePacket rel) { + sendToLockManager(LockReleasePacket.subject, rel); + } + + private final void sendAdvertisement(Advertisement adv, AdvType advType, Set subs, boolean isPublic) { + final AdvertisementPacket pkt = subs != null ? new AdvertisementPacket(adv, advType, subs, isPublic) + : new AdvertisementPacket(adv, advType, isPublic); + sendToServer(AdvertisementPacket.subject, pkt); + } + + final void registerForwarder(PacketForwarder forwarder, String subject) { + router.setPacketForwarder(subject, forwarder); + } + + final void stop() { + overlay.stop(); + } + + private final void sendToServer(String subject, Serializable packet) { + if (server == null) { + serverQueue.add(new PacketSubjectPair(subject, packet)); + } else { + try { + overlay.send(subject, packet, server); + } catch (IOException | NotRunningException e) { + e.printStackTrace(); + } + } + } + + private final void sendToLockManager(String subject, Serializable packet) { + if (lockManager == null) { + lockManagerQueue.add(new PacketSubjectPair(subject, packet)); + } else { + try { + overlay.send(subject, packet, lockManager); + } catch (IOException | NotRunningException e) { + e.printStackTrace(); + } + } + } + + @Override + public final Collection forwardPacket(String subject, NodeDescriptor sender, Serializable packet, + Collection neighbors, Outbox outbox) { + if (subject.equals(ServerHelloPacket.subject)) { + logger.info("Received server hello packet"); + assert packet instanceof ServerHelloPacket; + server = sender; + serverQueue.forEach(p -> sendToServer(p.getSubject(), p.getPacket())); + } else if (subject.equals(LockManagerHelloPacket.subject)) { + logger.info("Received lock manager hello packet"); + assert packet instanceof LockManagerHelloPacket; + lockManager = sender; + lockManagerQueue.forEach(p -> sendToLockManager(p.getSubject(), p.getPacket())); + } else { + assert false : subject; + } + return new ArrayList<>(); + } + + private class PacketSubjectPair { + private final String subject; + private final Serializable packet; + + public PacketSubjectPair(String subject, Serializable packet) { + super(); + this.subject = subject; + this.packet = packet; + } + + final String getSubject() { + return subject; + } + + final Serializable getPacket() { + return packet; + } + + } } diff --git a/Dream2/src/main/java/dream/client/DreamClient.java b/Dream2/src/main/java/dream/client/DreamClient.java index 83a9b14..6a6e8e3 100644 --- a/Dream2/src/main/java/dream/client/DreamClient.java +++ b/Dream2/src/main/java/dream/client/DreamClient.java @@ -10,19 +10,19 @@ * to obtain information about the dependency graph. */ public enum DreamClient { - instance; + instance; - private final DependencyGraph depGraph = DependencyGraph.instance; + private final DependencyGraph depGraph = DependencyGraph.instance; - public final void connect() { - ClientEventForwarder.get(); - } + public final void connect() { + ClientEventForwarder.get(); + } - public final Set listVariables() { - final Set result = new HashSet<>(); - result.addAll(depGraph.getGraph().keySet()); - result.addAll(depGraph.getSources()); - return result; - } + public final Set listVariables() { + final Set result = new HashSet<>(); + result.addAll(depGraph.getGraph().keySet()); + result.addAll(depGraph.getSources()); + return result; + } } diff --git a/Dream2/src/main/java/dream/client/EventProducerPair.java b/Dream2/src/main/java/dream/client/EventProducerPair.java index c8ec835..f44eda5 100755 --- a/Dream2/src/main/java/dream/client/EventProducerPair.java +++ b/Dream2/src/main/java/dream/client/EventProducerPair.java @@ -3,26 +3,26 @@ import dream.common.packets.EventPacket; public class EventProducerPair { - private final EventPacket eventPacket; - private final UpdateProducer producer; + private final EventPacket eventPacket; + private final UpdateProducer producer; - public EventProducerPair(EventPacket eventPacket, UpdateProducer producer) { - super(); - this.eventPacket = eventPacket; - this.producer = producer; - } + public EventProducerPair(EventPacket eventPacket, UpdateProducer producer) { + super(); + this.eventPacket = eventPacket; + this.producer = producer; + } - public final EventPacket getEventPacket() { - return eventPacket; - } + public final EventPacket getEventPacket() { + return eventPacket; + } - public final UpdateProducer getUpdateProducer() { - return producer; - } + public final UpdateProducer getUpdateProducer() { + return producer; + } - @Override - public String toString() { - return "ComputationHandler [eventPacket=" + eventPacket + ", producer=" + producer + "]"; - } + @Override + public String toString() { + return "ComputationHandler [eventPacket=" + eventPacket + ", producer=" + producer + "]"; + } } diff --git a/Dream2/src/main/java/dream/client/FilteredUpdateProducer.java b/Dream2/src/main/java/dream/client/FilteredUpdateProducer.java index 69a6368..689fec5 100644 --- a/Dream2/src/main/java/dream/client/FilteredUpdateProducer.java +++ b/Dream2/src/main/java/dream/client/FilteredUpdateProducer.java @@ -6,49 +6,49 @@ class FilteredUpdateProducer implements UpdateProducer { - private final UpdateProducer producer; - private final List constraints; - - public FilteredUpdateProducer(UpdateProducer producer, List constraints) { - super(); - this.producer = producer; - this.constraints = constraints; - } - - @Override - public void notifyUpdateFinished() { - producer.notifyUpdateFinished(); - } - - @Override - public void registerUpdateConsumer(UpdateConsumer consumer, List constraints) { - producer.registerUpdateConsumer(consumer, constraints); - } - - @Override - public void unregisterUpdateConsumer(UpdateConsumer consumer) { - producer.unregisterUpdateConsumer(consumer); - } - - @Override - public UpdateProducer filter(SerializablePredicate constraint) { - constraints.add(constraint); - return this; - } - - @Override - public String getHost() { - return producer.getHost(); - } - - @Override - public String getObject() { - return producer.getObject(); - } - - @Override - public List getConstraints() { - return constraints; - } + private final UpdateProducer producer; + private final List constraints; + + public FilteredUpdateProducer(UpdateProducer producer, List constraints) { + super(); + this.producer = producer; + this.constraints = constraints; + } + + @Override + public void notifyUpdateFinished() { + producer.notifyUpdateFinished(); + } + + @Override + public void registerUpdateConsumer(UpdateConsumer consumer, List constraints) { + producer.registerUpdateConsumer(consumer, constraints); + } + + @Override + public void unregisterUpdateConsumer(UpdateConsumer consumer) { + producer.unregisterUpdateConsumer(consumer); + } + + @Override + public UpdateProducer filter(SerializablePredicate constraint) { + constraints.add(constraint); + return this; + } + + @Override + public String getHost() { + return producer.getHost(); + } + + @Override + public String getObject() { + return producer.getObject(); + } + + @Override + public List getConstraints() { + return constraints; + } } diff --git a/Dream2/src/main/java/dream/client/LockApplicant.java b/Dream2/src/main/java/dream/client/LockApplicant.java index d891785..9651a59 100644 --- a/Dream2/src/main/java/dream/client/LockApplicant.java +++ b/Dream2/src/main/java/dream/client/LockApplicant.java @@ -7,12 +7,12 @@ */ interface LockApplicant { - /** - * Method invoked when a lock is granted. - * - * @param lockGrant - * the granted lock. - */ - void notifyLockGranted(LockGrantPacket lockGrant); + /** + * Method invoked when a lock is granted. + * + * @param lockGrant + * the granted lock. + */ + void notifyLockGranted(LockGrantPacket lockGrant); } diff --git a/Dream2/src/main/java/dream/client/QueueManager.java b/Dream2/src/main/java/dream/client/QueueManager.java index 4d87a2a..6b5f947 100755 --- a/Dream2/src/main/java/dream/client/QueueManager.java +++ b/Dream2/src/main/java/dream/client/QueueManager.java @@ -18,77 +18,79 @@ * to make sure that no glitch can occur. */ class QueueManager { - // WaitingElements, partitioned by id - private final Map waitingElements = new HashMap(); - // Candidate results to deliver (they can be effectively delivered only there - // are no waiting elements). - private final Set pendingResults = new HashSet<>(); + // WaitingElements, partitioned by id + private final Map waitingElements = new HashMap(); + // Candidate results to deliver (they can be effectively delivered only + // there + // are no waiting elements). + private final Set pendingResults = new HashSet<>(); - final List processEventPacket(EventProducerPair event, String expression) { - final EventPacket evPkt = event.getEventPacket(); - final UUID id = evPkt.getId(); - final Set waitingRecommendations = // - IntraSourceDependencyDetector.instance.// - getWaitRecommendations(evPkt.getEvent(), evPkt.getSource()).// - stream().filter(wr -> wr.getExpression().equals(expression)).// - collect(Collectors.toSet()); + final List processEventPacket(EventProducerPair event, String expression) { + final EventPacket evPkt = event.getEventPacket(); + final UUID id = evPkt.getId(); + final Set waitingRecommendations = // + IntraSourceDependencyDetector.instance.// + getWaitRecommendations(evPkt.getEvent(), evPkt.getSource()).// + stream().filter(wr -> wr.getExpression().equals(expression)).// + collect(Collectors.toSet()); - if (waitingElements.containsKey(id)) { - final WaitingElement elem = waitingElements.get(id); - elem.processEvent(event); - if (elem.hasFinishedWaiting()) { - elem.getReceivedEvents().forEach(pendingResults::add); - waitingElements.remove(id); - } - } else { - final Set expressionsToWaitFrom = getExpressionsToWaitFrom(waitingRecommendations); - if (!expressionsToWaitFrom.isEmpty()) { - final WaitingElement elem = new WaitingElement(expressionsToWaitFrom, event); - waitingElements.put(id, elem); - } else { - pendingResults.add(event); - } - } + if (waitingElements.containsKey(id)) { + final WaitingElement elem = waitingElements.get(id); + elem.processEvent(event); + if (elem.hasFinishedWaiting()) { + elem.getReceivedEvents().forEach(pendingResults::add); + waitingElements.remove(id); + } + } else { + final Set expressionsToWaitFrom = getExpressionsToWaitFrom(waitingRecommendations); + if (!expressionsToWaitFrom.isEmpty()) { + final WaitingElement elem = new WaitingElement(expressionsToWaitFrom, event); + waitingElements.put(id, elem); + } else { + pendingResults.add(event); + } + } - final List result = new ArrayList<>(); - if (!waitingElements.containsKey(id)) { - result.addAll(pendingResults); - pendingResults.clear(); - } - return result; - } + final List result = new ArrayList<>(); + if (!waitingElements.containsKey(id)) { + result.addAll(pendingResults); + pendingResults.clear(); + } + return result; + } - private final Set getExpressionsToWaitFrom(Set recommendations) { - return recommendations.stream().// - map(wr -> wr.getRecommendations()).// - collect(HashSet::new, HashSet::addAll, HashSet::addAll); - } + private final Set getExpressionsToWaitFrom(Set recommendations) { + return recommendations.stream().// + map(wr -> wr.getRecommendations()).// + collect(HashSet::new, HashSet::addAll, HashSet::addAll); + } - private class WaitingElement { - // Set of expressions we are waiting for before delivering the events with - // the given id - private final Set waitingFor = new HashSet<>(); - // Set of events received with the given id - private final Set receivedEvents = new HashSet<>(); + private class WaitingElement { + // Set of expressions we are waiting for before delivering the events + // with + // the given id + private final Set waitingFor = new HashSet<>(); + // Set of events received with the given id + private final Set receivedEvents = new HashSet<>(); - WaitingElement(Set waitingFor, EventProducerPair event) { - this.waitingFor.addAll(waitingFor); - receivedEvents.add(event); - } + WaitingElement(Set waitingFor, EventProducerPair event) { + this.waitingFor.addAll(waitingFor); + receivedEvents.add(event); + } - final void processEvent(EventProducerPair event) { - final String signature = event.getEventPacket().getEvent().getSignature(); - assert waitingFor.contains(signature); - waitingFor.remove(signature); - receivedEvents.add(event); - } + final void processEvent(EventProducerPair event) { + final String signature = event.getEventPacket().getEvent().getSignature(); + assert waitingFor.contains(signature); + waitingFor.remove(signature); + receivedEvents.add(event); + } - final boolean hasFinishedWaiting() { - return waitingFor.isEmpty(); - } + final boolean hasFinishedWaiting() { + return waitingFor.isEmpty(); + } - final Set getReceivedEvents() { - return receivedEvents; - } - } + final Set getReceivedEvents() { + return receivedEvents; + } + } } diff --git a/Dream2/src/main/java/dream/client/RemoteVar.java b/Dream2/src/main/java/dream/client/RemoteVar.java index 83add61..9611504 100755 --- a/Dream2/src/main/java/dream/client/RemoteVar.java +++ b/Dream2/src/main/java/dream/client/RemoteVar.java @@ -16,114 +16,116 @@ import dream.common.packets.content.Subscription; public class RemoteVar implements Subscriber, UpdateProducer { - private T val; - - private final ClientEventForwarder forwarder; - private final Map> consumers = new HashMap<>(); - - private final Queue eventsQueue = new ArrayDeque<>(); - private int pendingAcks = 0; - - private final List constraints = new ArrayList<>(); - - private final Logger logger = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME); - - private final String host; - private final String object; - - public RemoteVar(String host, String object, List constraints) { - this.host = host; - this.object = object; - - final Subscription sub = new Subscription(host, object, constraints); - forwarder = ClientEventForwarder.get(); - forwarder.addSubscription(this, sub); - } - - public RemoteVar(String object, List constraints) { - this(Consts.hostName, object, constraints); - } - - public RemoteVar(String host, String object) { - this(host, object, new ArrayList()); - } - - public RemoteVar(String object) { - this(Consts.hostName, object); - } - - public final synchronized T get() { - return val; - } - - @Override - public synchronized void notifyEventReceived(EventPacket evPkt) { - eventsQueue.add(evPkt); - logger.finest("Received event packet " + evPkt + ". Added to the queue."); - if (eventsQueue.size() == 1) { - logger.finest("The element is the only one in the queue. Let's process it."); - processNextEvent(); - } - } - - @Override - public final synchronized void notifyUpdateFinished() { - pendingAcks--; - processNextEvent(); - } - - private void processNextEvent() { - if (pendingAcks == 0 && !eventsQueue.isEmpty()) { - final EventPacket nextPkt = eventsQueue.poll(); - val = (T) nextPkt.getEvent().getVal(); - sendEventPacketToListeners(nextPkt); - } - } - - private final void sendEventPacketToListeners(EventPacket evPkt) { - if (!consumers.isEmpty()) { - final Set satConsumers = // - consumers.entrySet().stream().filter(e -> e.getValue().stream().allMatch(constr -> ((SerializablePredicate) constr).test(val)))// - .map(e -> e.getKey())// - .collect(Collectors.toSet()); - - pendingAcks = satConsumers.size(); - satConsumers.forEach(c -> c.updateFromProducer(evPkt, this)); - } else { - processNextEvent(); - } - } - - @Override - public final String getHost() { - return host; - } - - @Override - public final String getObject() { - return object; - } - - @Override - public final List getConstraints() { - return constraints; - } - - @Override - public final void registerUpdateConsumer(UpdateConsumer consumer, List constraints) { - consumers.put(consumer, constraints); - } - - @Override - public final void unregisterUpdateConsumer(UpdateConsumer consumer) { - consumers.remove(consumer); - } - - @Override - public UpdateProducer filter(SerializablePredicate constraint) { - final List constrList = new ArrayList<>(); - constrList.add(constraint); - return new FilteredUpdateProducer<>(this, constrList); - } + private T val; + + private final ClientEventForwarder forwarder; + private final Map> consumers = new HashMap<>(); + + private final Queue eventsQueue = new ArrayDeque<>(); + private int pendingAcks = 0; + + private final List constraints = new ArrayList<>(); + + private final Logger logger = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME); + + private final String host; + private final String object; + + public RemoteVar(String host, String object, List constraints) { + this.host = host; + this.object = object; + + final Subscription sub = new Subscription(host, object, constraints); + forwarder = ClientEventForwarder.get(); + forwarder.addSubscription(this, sub); + } + + public RemoteVar(String object, List constraints) { + this(Consts.hostName, object, constraints); + } + + public RemoteVar(String host, String object) { + this(host, object, new ArrayList()); + } + + public RemoteVar(String object) { + this(Consts.hostName, object); + } + + public final synchronized T get() { + return val; + } + + @Override + public synchronized void notifyEventReceived(EventPacket evPkt) { + eventsQueue.add(evPkt); + logger.finest("Received event packet " + evPkt + ". Added to the queue."); + if (eventsQueue.size() == 1) { + logger.finest("The element is the only one in the queue. Let's process it."); + processNextEvent(); + } + } + + @Override + public final synchronized void notifyUpdateFinished() { + pendingAcks--; + processNextEvent(); + } + + private void processNextEvent() { + if (pendingAcks == 0 && !eventsQueue.isEmpty()) { + final EventPacket nextPkt = eventsQueue.poll(); + val = (T) nextPkt.getEvent().getVal(); + sendEventPacketToListeners(nextPkt); + } + } + + private final void sendEventPacketToListeners(EventPacket evPkt) { + if (!consumers.isEmpty()) { + final Set satConsumers = // + consumers.entrySet().stream() + .filter(e -> e.getValue().stream() + .allMatch(constr -> ((SerializablePredicate) constr).test(val)))// + .map(e -> e.getKey())// + .collect(Collectors.toSet()); + + pendingAcks = satConsumers.size(); + satConsumers.forEach(c -> c.updateFromProducer(evPkt, this)); + } else { + processNextEvent(); + } + } + + @Override + public final String getHost() { + return host; + } + + @Override + public final String getObject() { + return object; + } + + @Override + public final List getConstraints() { + return constraints; + } + + @Override + public final void registerUpdateConsumer(UpdateConsumer consumer, List constraints) { + consumers.put(consumer, constraints); + } + + @Override + public final void unregisterUpdateConsumer(UpdateConsumer consumer) { + consumers.remove(consumer); + } + + @Override + public UpdateProducer filter(SerializablePredicate constraint) { + final List constrList = new ArrayList<>(); + constrList.add(constraint); + return new FilteredUpdateProducer<>(this, constrList); + } } diff --git a/Dream2/src/main/java/dream/client/Signal.java b/Dream2/src/main/java/dream/client/Signal.java index 020e3fe..f664414 100755 --- a/Dream2/src/main/java/dream/client/Signal.java +++ b/Dream2/src/main/java/dream/client/Signal.java @@ -23,227 +23,231 @@ import dream.common.packets.content.Subscription; import dream.common.packets.locking.LockGrantPacket; -public class Signal implements TimeChangingValue, UpdateProducer, UpdateConsumer, LockApplicant { - private final Set> valueChangeListeners = new HashSet<>(); - - // Management of local subscribers - private final Map> consumers = new HashMap<>(); - private final Queue eventQueue = new ArrayDeque<>(); - private int pendingAcks = 0; - private final Set waitingProducers = new HashSet<>(); - - private final ClientEventForwarder clientEventForwarder; - private final QueueManager queueManager = new QueueManager(); - - private final String host; - private final String object; - private final List constraints = new ArrayList<>(); - - private final Supplier evaluation; - - private UUID lockID = null; - - private T val; - - private final Logger logger = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME); - - public Signal(String object, Supplier evaluation, UpdateProducer... prods) { - this.host = Consts.hostName; - this.object = object; - this.evaluation = evaluation; - - final Set subs = new HashSet<>(); - for (final UpdateProducer prod : prods) { - prod.registerUpdateConsumer(this, prod.getConstraints()); - subs.add(new Subscription(prod.getHost(), prod.getObject(), prod.getConstraints())); - } - - clientEventForwarder = ClientEventForwarder.get(); - clientEventForwarder.advertise(new Advertisement(Consts.hostName, object), subs, true); - } - - private final synchronized void processNextUpdate() { - if (pendingAcks == 0) { - // Notify that the previous update has finished - if (!waitingProducers.isEmpty()) { - waitingProducers.forEach(prod -> prod.notifyUpdateFinished()); - waitingProducers.clear(); - } - // Process the next packet, if any - if (!eventQueue.isEmpty()) { - processUpdate(eventQueue.poll()); - } - } - } - - private final void processUpdate(EventProducerPair update) { - logger.finest("processTask method invoked with " + update); - final List pairs = queueManager.processEventPacket(update, object + "@" + Consts.hostName); - logger.finest("The queueManager returned the following pairs " + pairs); - - if (!pairs.isEmpty()) { - logger.finest("Actual update"); - // Extract information from any of the packets - final EventPacket anyPkt = pairs.stream().findAny().get().getEventPacket(); - - // Compute the new value - try { - val = evaluate(); - logger.finest("New value computed for the reactive object: " + val); - } catch (final Exception e) { - logger.info("Exception during the evaluation of the expression. Acknowledging the producers, releasing the locks, and returning."); - pairs.forEach(pair -> pair.getUpdateProducer().notifyUpdateFinished()); - // Release locks, if needed - if ((Consts.consistencyType == ConsistencyType.COMPLETE_GLITCH_FREE || // - Consts.consistencyType == ConsistencyType.ATOMIC) && // - anyPkt.getLockReleaseNodes().contains(object + "@" + host)) { - clientEventForwarder.sendLockRelease(anyPkt.getId()); - } - return; - } - - // Notify value change listeners - logger.finest("Notifying registered listeners of the change."); - valueChangeListeners.forEach(l -> l.notifyValueChanged(val)); - - // Notify local and remote dependent objects - logger.finest("Sending event to dependent objects."); - final Event event = new Event(Consts.hostName, object, val); - // Notify remote subscribers - clientEventForwarder.sendEvent(anyPkt.getId(), event, anyPkt.getSource()); - - final Set satConsumers = // - consumers.entrySet().stream().filter(e -> e.getValue().stream().allMatch(constr -> ((SerializablePredicate) constr).test(val)))// - .map(e -> e.getKey())// - .collect(Collectors.toSet()); - // Notify local subscribers - if (!satConsumers.isEmpty()) { - pairs.forEach(pair -> waitingProducers.add(pair.getUpdateProducer())); - final EventPacket newEvPkt = new EventPacket(event, anyPkt.getId(), anyPkt.getSource()); - newEvPkt.setLockReleaseNodes(anyPkt.getLockReleaseNodes()); - pendingAcks = satConsumers.size(); - satConsumers.forEach(c -> c.updateFromProducer(newEvPkt, this)); - } else { - // Acknowledge the producers if there are no pending acks - logger.finest("Acknowledging the producers."); - pairs.forEach(pair -> pair.getUpdateProducer().notifyUpdateFinished()); - } - - // Release locks, if needed - if ((Consts.consistencyType == ConsistencyType.COMPLETE_GLITCH_FREE || // - Consts.consistencyType == ConsistencyType.ATOMIC) && // - anyPkt.getLockReleaseNodes().contains(object + "@" + host)) { - clientEventForwarder.sendLockRelease(anyPkt.getId()); - } - - } else { - logger.finest(object + ": update call but waiting: " + update); - } - } - - @Override - public void addValueChangeListener(ValueChangeListener listener) { - valueChangeListeners.add(listener); - } - - @Override - public void removeValueChangeListener(ValueChangeListener listener) { - valueChangeListeners.remove(listener); - } - - @Override - public final synchronized T evaluate() { - return evaluation.get(); - } - - public T get() { - return val; - } - - public T atomicGet() { - acquireLock(); - // TODO: this should actually be a copy of the object - final T currentVal = val; - releaseLock(); - return currentVal; - } - - private final synchronized void acquireLock() { - if (Consts.consistencyType != ConsistencyType.ATOMIC) { - return; - } - clientEventForwarder.sendReadOnlyLockRequest(object + "@" + host, this); - while (lockID == null) { - try { - wait(); - } catch (final InterruptedException e) { - e.printStackTrace(); - } - } - } - - private final synchronized void releaseLock() { - if (Consts.consistencyType != ConsistencyType.ATOMIC) { - return; - } - clientEventForwarder.sendLockRelease(lockID); - lockID = null; - } - - @Override - public UpdateProducer filter(SerializablePredicate constraint) { - final List constrList = new ArrayList<>(); - constrList.add(constraint); - return new FilteredUpdateProducer<>(this, constrList); - } - - @Override - public final synchronized void updateFromProducer(EventPacket packet, UpdateProducer producer) { - final EventProducerPair pair = new EventProducerPair(packet, producer); - eventQueue.add(pair); - logger.finest("Method update called for event " + pair + ". Added to the queue."); - if (eventQueue.size() == 1) { - logger.finest("The element is the only one in the queue. Let's process it."); - processNextUpdate(); - } - - } - - @Override - public final synchronized void notifyUpdateFinished() { - pendingAcks--; - processNextUpdate(); - } - - @Override - public void registerUpdateConsumer(UpdateConsumer consumer, List constraints) { - consumers.put(consumer, constraints); - } - - @Override - public void unregisterUpdateConsumer(UpdateConsumer consumer) { - consumers.remove(consumer); - } - - @Override - public String getHost() { - return host; - } - - @Override - public String getObject() { - return object; - } - - @Override - public List getConstraints() { - return constraints; - } - - @Override - public final synchronized void notifyLockGranted(LockGrantPacket lockGrant) { - lockID = lockGrant.getLockID(); - notifyAll(); - } +public class Signal + implements TimeChangingValue, UpdateProducer, UpdateConsumer, LockApplicant { + private final Set> valueChangeListeners = new HashSet<>(); + + // Management of local subscribers + private final Map> consumers = new HashMap<>(); + private final Queue eventQueue = new ArrayDeque<>(); + private int pendingAcks = 0; + private final Set waitingProducers = new HashSet<>(); + + private final ClientEventForwarder clientEventForwarder; + private final QueueManager queueManager = new QueueManager(); + + private final String host; + private final String object; + private final List constraints = new ArrayList<>(); + + private final Supplier evaluation; + + private UUID lockID = null; + + private T val; + + private final Logger logger = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME); + + public Signal(String object, Supplier evaluation, UpdateProducer... prods) { + this.host = Consts.hostName; + this.object = object; + this.evaluation = evaluation; + + final Set subs = new HashSet<>(); + for (final UpdateProducer prod : prods) { + prod.registerUpdateConsumer(this, prod.getConstraints()); + subs.add(new Subscription(prod.getHost(), prod.getObject(), prod.getConstraints())); + } + + clientEventForwarder = ClientEventForwarder.get(); + clientEventForwarder.advertise(new Advertisement(Consts.hostName, object), subs, true); + } + + private final synchronized void processNextUpdate() { + if (pendingAcks == 0) { + // Notify that the previous update has finished + if (!waitingProducers.isEmpty()) { + waitingProducers.forEach(prod -> prod.notifyUpdateFinished()); + waitingProducers.clear(); + } + // Process the next packet, if any + if (!eventQueue.isEmpty()) { + processUpdate(eventQueue.poll()); + } + } + } + + private final void processUpdate(EventProducerPair update) { + logger.finest("processTask method invoked with " + update); + final List pairs = queueManager.processEventPacket(update, object + "@" + Consts.hostName); + logger.finest("The queueManager returned the following pairs " + pairs); + + if (!pairs.isEmpty()) { + logger.finest("Actual update"); + // Extract information from any of the packets + final EventPacket anyPkt = pairs.stream().findAny().get().getEventPacket(); + + // Compute the new value + try { + val = evaluate(); + logger.finest("New value computed for the reactive object: " + val); + } catch (final Exception e) { + logger.info( + "Exception during the evaluation of the expression. Acknowledging the producers, releasing the locks, and returning."); + pairs.forEach(pair -> pair.getUpdateProducer().notifyUpdateFinished()); + // Release locks, if needed + if ((Consts.consistencyType == ConsistencyType.COMPLETE_GLITCH_FREE || // + Consts.consistencyType == ConsistencyType.ATOMIC) && // + anyPkt.getLockReleaseNodes().contains(object + "@" + host)) { + clientEventForwarder.sendLockRelease(anyPkt.getId()); + } + return; + } + + // Notify value change listeners + logger.finest("Notifying registered listeners of the change."); + valueChangeListeners.forEach(l -> l.notifyValueChanged(val)); + + // Notify local and remote dependent objects + logger.finest("Sending event to dependent objects."); + final Event event = new Event(Consts.hostName, object, val); + // Notify remote subscribers + clientEventForwarder.sendEvent(anyPkt.getId(), event, anyPkt.getSource()); + + final Set satConsumers = // + consumers.entrySet().stream() + .filter(e -> e.getValue().stream() + .allMatch(constr -> ((SerializablePredicate) constr).test(val)))// + .map(e -> e.getKey())// + .collect(Collectors.toSet()); + // Notify local subscribers + if (!satConsumers.isEmpty()) { + pairs.forEach(pair -> waitingProducers.add(pair.getUpdateProducer())); + final EventPacket newEvPkt = new EventPacket(event, anyPkt.getId(), anyPkt.getSource()); + newEvPkt.setLockReleaseNodes(anyPkt.getLockReleaseNodes()); + pendingAcks = satConsumers.size(); + satConsumers.forEach(c -> c.updateFromProducer(newEvPkt, this)); + } else { + // Acknowledge the producers if there are no pending acks + logger.finest("Acknowledging the producers."); + pairs.forEach(pair -> pair.getUpdateProducer().notifyUpdateFinished()); + } + + // Release locks, if needed + if ((Consts.consistencyType == ConsistencyType.COMPLETE_GLITCH_FREE || // + Consts.consistencyType == ConsistencyType.ATOMIC) && // + anyPkt.getLockReleaseNodes().contains(object + "@" + host)) { + clientEventForwarder.sendLockRelease(anyPkt.getId()); + } + + } else { + logger.finest(object + ": update call but waiting: " + update); + } + } + + @Override + public void addValueChangeListener(ValueChangeListener listener) { + valueChangeListeners.add(listener); + } + + @Override + public void removeValueChangeListener(ValueChangeListener listener) { + valueChangeListeners.remove(listener); + } + + @Override + public final synchronized T evaluate() { + return evaluation.get(); + } + + public T get() { + return val; + } + + public T atomicGet() { + acquireLock(); + // TODO: this should actually be a copy of the object + final T currentVal = val; + releaseLock(); + return currentVal; + } + + private final synchronized void acquireLock() { + if (Consts.consistencyType != ConsistencyType.ATOMIC) { + return; + } + clientEventForwarder.sendReadOnlyLockRequest(object + "@" + host, this); + while (lockID == null) { + try { + wait(); + } catch (final InterruptedException e) { + e.printStackTrace(); + } + } + } + + private final synchronized void releaseLock() { + if (Consts.consistencyType != ConsistencyType.ATOMIC) { + return; + } + clientEventForwarder.sendLockRelease(lockID); + lockID = null; + } + + @Override + public UpdateProducer filter(SerializablePredicate constraint) { + final List constrList = new ArrayList<>(); + constrList.add(constraint); + return new FilteredUpdateProducer<>(this, constrList); + } + + @Override + public final synchronized void updateFromProducer(EventPacket packet, UpdateProducer producer) { + final EventProducerPair pair = new EventProducerPair(packet, producer); + eventQueue.add(pair); + logger.finest("Method update called for event " + pair + ". Added to the queue."); + if (eventQueue.size() == 1) { + logger.finest("The element is the only one in the queue. Let's process it."); + processNextUpdate(); + } + + } + + @Override + public final synchronized void notifyUpdateFinished() { + pendingAcks--; + processNextUpdate(); + } + + @Override + public void registerUpdateConsumer(UpdateConsumer consumer, List constraints) { + consumers.put(consumer, constraints); + } + + @Override + public void unregisterUpdateConsumer(UpdateConsumer consumer) { + consumers.remove(consumer); + } + + @Override + public String getHost() { + return host; + } + + @Override + public String getObject() { + return object; + } + + @Override + public List getConstraints() { + return constraints; + } + + @Override + public final synchronized void notifyLockGranted(LockGrantPacket lockGrant) { + lockID = lockGrant.getLockID(); + notifyAll(); + } } diff --git a/Dream2/src/main/java/dream/client/Subscriber.java b/Dream2/src/main/java/dream/client/Subscriber.java index b2ce0f7..ffa88d8 100755 --- a/Dream2/src/main/java/dream/client/Subscriber.java +++ b/Dream2/src/main/java/dream/client/Subscriber.java @@ -8,12 +8,12 @@ */ public interface Subscriber { - /** - * Notifies the subscriber that the given event has occurred. - * - * @param event - * the occurred event. - */ - public void notifyEventReceived(EventPacket event); + /** + * Notifies the subscriber that the given event has occurred. + * + * @param event + * the occurred event. + */ + public void notifyEventReceived(EventPacket event); } diff --git a/Dream2/src/main/java/dream/client/TimeChangingValue.java b/Dream2/src/main/java/dream/client/TimeChangingValue.java index dc8fe1c..5e853f6 100755 --- a/Dream2/src/main/java/dream/client/TimeChangingValue.java +++ b/Dream2/src/main/java/dream/client/TimeChangingValue.java @@ -11,26 +11,26 @@ */ interface TimeChangingValue { - /** - * The evaluate method is automatically invoked whenever one of the values - * this object depends on changes. - */ - public T evaluate(); + /** + * The evaluate method is automatically invoked whenever one of the values + * this object depends on changes. + */ + public T evaluate(); - /** - * Register a new ValueChangeListener. - * - * @param listener - * the listener to add. - */ - public void addValueChangeListener(ValueChangeListener listener); + /** + * Register a new ValueChangeListener. + * + * @param listener + * the listener to add. + */ + public void addValueChangeListener(ValueChangeListener listener); - /** - * Unregister a ValueChangeListener. - * - * @param listener - * the listener to remove. - */ - public void removeValueChangeListener(ValueChangeListener listener); + /** + * Unregister a ValueChangeListener. + * + * @param listener + * the listener to remove. + */ + public void removeValueChangeListener(ValueChangeListener listener); } diff --git a/Dream2/src/main/java/dream/client/UpdateConsumer.java b/Dream2/src/main/java/dream/client/UpdateConsumer.java index 7e78d6e..fce5aea 100644 --- a/Dream2/src/main/java/dream/client/UpdateConsumer.java +++ b/Dream2/src/main/java/dream/client/UpdateConsumer.java @@ -7,13 +7,13 @@ */ public interface UpdateConsumer { - /** - * Notifies a local update coming from the given signal. - * - * @param packet - * the packet containing the update. - * @param producer - * the producer that generated the update. - */ - void updateFromProducer(EventPacket packet, UpdateProducer producer); + /** + * Notifies a local update coming from the given signal. + * + * @param packet + * the packet containing the update. + * @param producer + * the producer that generated the update. + */ + void updateFromProducer(EventPacket packet, UpdateProducer producer); } diff --git a/Dream2/src/main/java/dream/client/UpdateProducer.java b/Dream2/src/main/java/dream/client/UpdateProducer.java index f0b7f59..e55e498 100644 --- a/Dream2/src/main/java/dream/client/UpdateProducer.java +++ b/Dream2/src/main/java/dream/client/UpdateProducer.java @@ -10,58 +10,58 @@ */ public interface UpdateProducer { - /** - * The method is invoked when an update procedure finishes. - */ - void notifyUpdateFinished(); + /** + * The method is invoked when an update procedure finishes. + */ + void notifyUpdateFinished(); - /** - * Register a new consumer for the updates of this producer. The consumer is - * notified only if the given constraints are safisfied. - * - * @param consumer - * the consumer. - * @param constraints - * the constraints. - */ - void registerUpdateConsumer(UpdateConsumer consumer, List constraints); + /** + * Register a new consumer for the updates of this producer. The consumer is + * notified only if the given constraints are safisfied. + * + * @param consumer + * the consumer. + * @param constraints + * the constraints. + */ + void registerUpdateConsumer(UpdateConsumer consumer, List constraints); - /** - * Unregister the consumer from the updates of this producer. - * - * @param consumer - * the consumer. - */ - void unregisterUpdateConsumer(UpdateConsumer consumer); + /** + * Unregister the consumer from the updates of this producer. + * + * @param consumer + * the consumer. + */ + void unregisterUpdateConsumer(UpdateConsumer consumer); - /** - * Return an UpdateProducer with the given filter. - * - * @param constraint - * the constraint used to filter. - * @return an UpdateProducer for the filtered object. - */ - public UpdateProducer filter(SerializablePredicate constraint); + /** + * Return an UpdateProducer with the given filter. + * + * @param constraint + * the constraint used to filter. + * @return an UpdateProducer for the filtered object. + */ + public UpdateProducer filter(SerializablePredicate constraint); - /** - * Return the host of the object the producer refers to. - * - * @return the host of the object. - */ - String getHost(); + /** + * Return the host of the object the producer refers to. + * + * @return the host of the object. + */ + String getHost(); - /** - * Return the name of the object the producer refers to. - * - * @return the name of the object. - */ - String getObject(); + /** + * Return the name of the object the producer refers to. + * + * @return the name of the object. + */ + String getObject(); - /** - * Returns the constraints of the producer. - * - * @return the constraints of the producer. - */ - List getConstraints(); + /** + * Returns the constraints of the producer. + * + * @return the constraints of the producer. + */ + List getConstraints(); } diff --git a/Dream2/src/main/java/dream/client/ValueChangeListener.java b/Dream2/src/main/java/dream/client/ValueChangeListener.java index c1452f2..9a7921d 100755 --- a/Dream2/src/main/java/dream/client/ValueChangeListener.java +++ b/Dream2/src/main/java/dream/client/ValueChangeListener.java @@ -6,6 +6,6 @@ */ public interface ValueChangeListener { - public void notifyValueChanged(T newValue); + public void notifyValueChanged(T newValue); } diff --git a/Dream2/src/main/java/dream/client/Var.java b/Dream2/src/main/java/dream/client/Var.java index d48aedc..2f41515 100755 --- a/Dream2/src/main/java/dream/client/Var.java +++ b/Dream2/src/main/java/dream/client/Var.java @@ -22,133 +22,134 @@ import dream.common.packets.locking.LockGrantPacket; public class Var implements UpdateProducer, LockApplicant { - private final ClientEventForwarder forwarder; - - private final String host; - private final String object; - private final List constraints = new ArrayList(); - - private final Map> consumers = new HashMap<>(); - private final Queue waitingModifications = new ArrayDeque<>(); - private int pendingAcks = 0; - - private T val; - - public Var(String object, T val) { - this.forwarder = ClientEventForwarder.get(); - this.host = Consts.hostName; - this.object = object; - this.val = val; - forwarder.advertise(new Advertisement(Consts.hostName, object), true); - } - - public final synchronized void set(T val) { - final Supplier supplier = () -> val; - waitingModifications.add(supplier); - if (waitingModifications.size() == 1) { - tryToProcessNextUpdate(); - } - } - - public final synchronized void modify(Consumer modification) { - waitingModifications.add(modification); - if (waitingModifications.size() == 1) { - tryToProcessNextUpdate(); - } - } - - public final synchronized T get() { - assert val != null; - return val; - } - - private final void tryToProcessNextUpdate() { - if (pendingAcks == 0 && !waitingModifications.isEmpty()) { - // In the case of complete glitch freedom or atomic consistency, we - // possibly need to acquire a lock before processing the update - if (Consts.consistencyType == ConsistencyType.COMPLETE_GLITCH_FREE || // - Consts.consistencyType == ConsistencyType.ATOMIC) { - final boolean lockRequired = forwarder.sendReadWriteLockRequest(object + "@" + host, this); - if (!lockRequired) { - processNextUpdate(UUID.randomUUID()); - } - } - // Otherwise the update can be immediately processed - else { - processNextUpdate(UUID.randomUUID()); - } - } - } - - private final void processNextUpdate(UUID eventId) { - final Object mod = waitingModifications.poll(); - // Apply modification - if (mod instanceof Consumer) { - final Consumer consumer = (Consumer) mod; - consumer.accept(val); - } else if (mod instanceof Supplier) { - final Supplier supplier = (Supplier) mod; - val = supplier.get(); - } - - // Propagate modification to local and remote subscribers - final Event ev = new Event(Consts.hostName, object, val); - final String source = ev.getSignature(); - final EventPacket packet = new EventPacket(ev, eventId, source); - packet.setLockReleaseNodes(forwarder.getLockReleaseNodesFor(source)); - - final Set satConsumers = // - consumers.entrySet().stream().filter(e -> e.getValue().stream().allMatch(constr -> ((SerializablePredicate) constr).test(val)))// - .map(e -> e.getKey())// - .collect(Collectors.toSet()); - - pendingAcks = satConsumers.size(); - satConsumers.forEach(c -> c.updateFromProducer(packet, this)); - - forwarder.sendEvent(eventId, ev, ev.getSignature()); - } - - @Override - public UpdateProducer filter(SerializablePredicate constraint) { - final List constrList = new ArrayList<>(); - constrList.add(constraint); - return new FilteredUpdateProducer<>(this, constrList); - } - - @Override - public final void notifyUpdateFinished() { - pendingAcks--; - tryToProcessNextUpdate(); - } - - @Override - public void registerUpdateConsumer(UpdateConsumer consumer, List constraints) { - consumers.put(consumer, constraints); - } - - @Override - public void unregisterUpdateConsumer(UpdateConsumer consumer) { - consumers.remove(consumer); - } - - @Override - public String getHost() { - return host; - } - - @Override - public String getObject() { - return object; - } - - @Override - public List getConstraints() { - return constraints; - } - - @Override - public void notifyLockGranted(LockGrantPacket lockGrant) { - processNextUpdate(lockGrant.getLockID()); - } + private final ClientEventForwarder forwarder; + + private final String host; + private final String object; + private final List constraints = new ArrayList(); + + private final Map> consumers = new HashMap<>(); + private final Queue waitingModifications = new ArrayDeque<>(); + private int pendingAcks = 0; + + private T val; + + public Var(String object, T val) { + this.forwarder = ClientEventForwarder.get(); + this.host = Consts.hostName; + this.object = object; + this.val = val; + forwarder.advertise(new Advertisement(Consts.hostName, object), true); + } + + public final synchronized void set(T val) { + final Supplier supplier = () -> val; + waitingModifications.add(supplier); + if (waitingModifications.size() == 1) { + tryToProcessNextUpdate(); + } + } + + public final synchronized void modify(Consumer modification) { + waitingModifications.add(modification); + if (waitingModifications.size() == 1) { + tryToProcessNextUpdate(); + } + } + + public final synchronized T get() { + assert val != null; + return val; + } + + private final void tryToProcessNextUpdate() { + if (pendingAcks == 0 && !waitingModifications.isEmpty()) { + // In the case of complete glitch freedom or atomic consistency, we + // possibly need to acquire a lock before processing the update + if (Consts.consistencyType == ConsistencyType.COMPLETE_GLITCH_FREE || // + Consts.consistencyType == ConsistencyType.ATOMIC) { + final boolean lockRequired = forwarder.sendReadWriteLockRequest(object + "@" + host, this); + if (!lockRequired) { + processNextUpdate(UUID.randomUUID()); + } + } + // Otherwise the update can be immediately processed + else { + processNextUpdate(UUID.randomUUID()); + } + } + } + + private final void processNextUpdate(UUID eventId) { + final Object mod = waitingModifications.poll(); + // Apply modification + if (mod instanceof Consumer) { + final Consumer consumer = (Consumer) mod; + consumer.accept(val); + } else if (mod instanceof Supplier) { + final Supplier supplier = (Supplier) mod; + val = supplier.get(); + } + + // Propagate modification to local and remote subscribers + final Event ev = new Event(Consts.hostName, object, val); + final String source = ev.getSignature(); + final EventPacket packet = new EventPacket(ev, eventId, source); + packet.setLockReleaseNodes(forwarder.getLockReleaseNodesFor(source)); + + final Set satConsumers = // + consumers.entrySet().stream() + .filter(e -> e.getValue().stream().allMatch(constr -> ((SerializablePredicate) constr).test(val)))// + .map(e -> e.getKey())// + .collect(Collectors.toSet()); + + pendingAcks = satConsumers.size(); + satConsumers.forEach(c -> c.updateFromProducer(packet, this)); + + forwarder.sendEvent(eventId, ev, ev.getSignature()); + } + + @Override + public UpdateProducer filter(SerializablePredicate constraint) { + final List constrList = new ArrayList<>(); + constrList.add(constraint); + return new FilteredUpdateProducer<>(this, constrList); + } + + @Override + public final void notifyUpdateFinished() { + pendingAcks--; + tryToProcessNextUpdate(); + } + + @Override + public void registerUpdateConsumer(UpdateConsumer consumer, List constraints) { + consumers.put(consumer, constraints); + } + + @Override + public void unregisterUpdateConsumer(UpdateConsumer consumer) { + consumers.remove(consumer); + } + + @Override + public String getHost() { + return host; + } + + @Override + public String getObject() { + return object; + } + + @Override + public List getConstraints() { + return constraints; + } + + @Override + public void notifyLockGranted(LockGrantPacket lockGrant) { + processNextUpdate(lockGrant.getLockID()); + } } diff --git a/Dream2/src/main/java/dream/common/ConsistencyType.java b/Dream2/src/main/java/dream/common/ConsistencyType.java index 1dbfa5e..470691d 100755 --- a/Dream2/src/main/java/dream/common/ConsistencyType.java +++ b/Dream2/src/main/java/dream/common/ConsistencyType.java @@ -1,28 +1,28 @@ package dream.common; public enum ConsistencyType { - CAUSAL { - @Override - public final String toString() { - return "Causal"; - } - }, - SINGLE_SOURCE_GLITCH_FREE { - @Override - public final String toString() { - return "Single source glitch free"; - } - }, - COMPLETE_GLITCH_FREE { - @Override - public final String toString() { - return "Complete glitch free"; - } - }, - ATOMIC { - @Override - public final String toString() { - return "Atomic"; - } - } + CAUSAL { + @Override + public final String toString() { + return "Causal"; + } + }, + SINGLE_SOURCE_GLITCH_FREE { + @Override + public final String toString() { + return "Single source glitch free"; + } + }, + COMPLETE_GLITCH_FREE { + @Override + public final String toString() { + return "Complete glitch free"; + } + }, + ATOMIC { + @Override + public final String toString() { + return "Atomic"; + } + } } diff --git a/Dream2/src/main/java/dream/common/Consts.java b/Dream2/src/main/java/dream/common/Consts.java index 3a85ad8..dd81aef 100755 --- a/Dream2/src/main/java/dream/common/Consts.java +++ b/Dream2/src/main/java/dream/common/Consts.java @@ -9,68 +9,69 @@ import java.util.logging.Logger; public final class Consts { - private static final Properties properties = new Properties(); - private static final String LOGGING_PROPERTIES_FILE_NAME = "logging.properties"; + private static final Properties properties = new Properties(); + private static final String LOGGING_PROPERTIES_FILE_NAME = "logging.properties"; - public static final String serverAddr; - public static final String lockManagerAddr; + public static final String serverAddr; + public static final String lockManagerAddr; - public static final int serverPort; - public static final int lockManagerPort; + public static final int serverPort; + public static final int lockManagerPort; - public static ConsistencyType consistencyType; - public static String hostName; + public static ConsistencyType consistencyType; + public static String hostName; - static { - /** - * Read logging properties - */ - final LogManager manager = LogManager.getLogManager(); - try { - manager.readConfiguration(new FileInputStream(new File(LOGGING_PROPERTIES_FILE_NAME))); - } catch (SecurityException | IOException e) { - e.printStackTrace(); - } - final Logger logger = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME); - final ConsoleHandler consoleHandler = new ConsoleHandler(); - logger.addHandler(consoleHandler); + static { + /** + * Read logging properties + */ + final LogManager manager = LogManager.getLogManager(); + try { + manager.readConfiguration(new FileInputStream(new File(LOGGING_PROPERTIES_FILE_NAME))); + } catch (SecurityException | IOException e) { + e.printStackTrace(); + } + final Logger logger = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME); + final ConsoleHandler consoleHandler = new ConsoleHandler(); + logger.addHandler(consoleHandler); - /** - * Load properties - */ - try { - final FileInputStream input = new FileInputStream("dream.properties"); - properties.load(input); - input.close(); - } catch (final IOException e) { - e.printStackTrace(); - } + /** + * Load properties + */ + try { + final FileInputStream input = new FileInputStream("dream.properties"); + properties.load(input); + input.close(); + } catch (final IOException e) { + e.printStackTrace(); + } - final String serverAddrProperty = properties.getProperty("serverAddr", "localhost"); - final String serverPortProperty = properties.getProperty("serverPort", "9000"); - serverPort = Integer.parseInt(serverPortProperty); - serverAddr = "reds-tcp:" + serverAddrProperty + ":" + serverPort; + final String serverAddrProperty = properties.getProperty("serverAddr", "localhost"); + final String serverPortProperty = properties.getProperty("serverPort", "9000"); + serverPort = Integer.parseInt(serverPortProperty); + serverAddr = "reds-tcp:" + serverAddrProperty + ":" + serverPort; - final String lockManagerAddrProperty = properties.getProperty("lockManagerAddr", "localhost"); - final String lockManagerPortProperty = properties.getProperty("serverManagerPort", "9999"); - lockManagerPort = Integer.parseInt(lockManagerPortProperty); - lockManagerAddr = "reds-tcp:" + lockManagerAddrProperty + ":" + lockManagerPort; + final String lockManagerAddrProperty = properties.getProperty("lockManagerAddr", "localhost"); + final String lockManagerPortProperty = properties.getProperty("serverManagerPort", "9999"); + lockManagerPort = Integer.parseInt(lockManagerPortProperty); + lockManagerAddr = "reds-tcp:" + lockManagerAddrProperty + ":" + lockManagerPort; - final String hostNameProperty = properties.getProperty("hostName", "local"); - hostName = hostNameProperty; + final String hostNameProperty = properties.getProperty("hostName", "local"); + hostName = hostNameProperty; - final String consistencyTypeProperty = properties.getProperty("consistencyType", "single_glitch_free").toLowerCase(); - if (consistencyTypeProperty.equals("causal")) { - consistencyType = ConsistencyType.CAUSAL; - } else if (consistencyTypeProperty.equals("single_glitch_free")) { - consistencyType = ConsistencyType.SINGLE_SOURCE_GLITCH_FREE; - } else if (consistencyTypeProperty.equals("complete_glitch_free")) { - consistencyType = ConsistencyType.COMPLETE_GLITCH_FREE; - } else if (consistencyTypeProperty.equals("atomic")) { - consistencyType = ConsistencyType.ATOMIC; - } else { - logger.warning("Unknown consistency type. Using single source glitch free as default."); - } + final String consistencyTypeProperty = properties.getProperty("consistencyType", "single_glitch_free") + .toLowerCase(); + if (consistencyTypeProperty.equals("causal")) { + consistencyType = ConsistencyType.CAUSAL; + } else if (consistencyTypeProperty.equals("single_glitch_free")) { + consistencyType = ConsistencyType.SINGLE_SOURCE_GLITCH_FREE; + } else if (consistencyTypeProperty.equals("complete_glitch_free")) { + consistencyType = ConsistencyType.COMPLETE_GLITCH_FREE; + } else if (consistencyTypeProperty.equals("atomic")) { + consistencyType = ConsistencyType.ATOMIC; + } else { + logger.warning("Unknown consistency type. Using single source glitch free as default."); + } - } + } } diff --git a/Dream2/src/main/java/dream/common/packets/AdvertisementPacket.java b/Dream2/src/main/java/dream/common/packets/AdvertisementPacket.java index d3bc307..d0de143 100755 --- a/Dream2/src/main/java/dream/common/packets/AdvertisementPacket.java +++ b/Dream2/src/main/java/dream/common/packets/AdvertisementPacket.java @@ -15,90 +15,91 @@ * includes the set of subscriptions that define such dependencies. */ public class AdvertisementPacket implements Serializable { - private static final long serialVersionUID = 5219175796450319466L; - public static final String subject = "__DREAM_ADVERTISEMENT_PACKET_SUBJECT"; + private static final long serialVersionUID = 5219175796450319466L; + public static final String subject = "__DREAM_ADVERTISEMENT_PACKET_SUBJECT"; - private final Advertisement advertisement; - private final AdvType advType; - private final Set subscriptions = new HashSet<>(); - private final boolean isPublic; + private final Advertisement advertisement; + private final AdvType advType; + private final Set subscriptions = new HashSet<>(); + private final boolean isPublic; - public AdvertisementPacket(Advertisement advertisement, AdvType advType, Set subscriptions, boolean isPublic) { - this(advertisement, advType, isPublic); - this.subscriptions.addAll(subscriptions); - } + public AdvertisementPacket(Advertisement advertisement, AdvType advType, Set subscriptions, + boolean isPublic) { + this(advertisement, advType, isPublic); + this.subscriptions.addAll(subscriptions); + } - public AdvertisementPacket(Advertisement advertisement, AdvType advType, boolean isPublic) { - this.advertisement = advertisement; - this.advType = advType; - this.isPublic = isPublic; - } + public AdvertisementPacket(Advertisement advertisement, AdvType advType, boolean isPublic) { + this.advertisement = advertisement; + this.advType = advType; + this.isPublic = isPublic; + } - public Advertisement getAdvertisement() { - return advertisement; - } + public Advertisement getAdvertisement() { + return advertisement; + } - public AdvType getAdvType() { - return advType; - } + public AdvType getAdvType() { + return advType; + } - public final Set getSubscriptions() { - return subscriptions; - } + public final Set getSubscriptions() { + return subscriptions; + } - public final boolean isPublic() { - return isPublic; - } + public final boolean isPublic() { + return isPublic; + } - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + (advType == null ? 0 : advType.hashCode()); - result = prime * result + (advertisement == null ? 0 : advertisement.hashCode()); - result = prime * result + (subscriptions == null ? 0 : subscriptions.hashCode()); - return result; - } + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + (advType == null ? 0 : advType.hashCode()); + result = prime * result + (advertisement == null ? 0 : advertisement.hashCode()); + result = prime * result + (subscriptions == null ? 0 : subscriptions.hashCode()); + return result; + } - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (!(obj instanceof AdvertisementPacket)) { - return false; - } - final AdvertisementPacket other = (AdvertisementPacket) obj; - if (advType != other.advType) { - return false; - } - if (advertisement == null) { - if (other.advertisement != null) { - return false; - } - } else if (!advertisement.equals(other.advertisement)) { - return false; - } - if (subscriptions == null) { - if (other.subscriptions != null) { - return false; - } - } else if (!subscriptions.equals(other.subscriptions)) { - return false; - } - return true; - } + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof AdvertisementPacket)) { + return false; + } + final AdvertisementPacket other = (AdvertisementPacket) obj; + if (advType != other.advType) { + return false; + } + if (advertisement == null) { + if (other.advertisement != null) { + return false; + } + } else if (!advertisement.equals(other.advertisement)) { + return false; + } + if (subscriptions == null) { + if (other.subscriptions != null) { + return false; + } + } else if (!subscriptions.equals(other.subscriptions)) { + return false; + } + return true; + } - @Override - public String toString() { - if (subscriptions != null) { - return advType + ": " + advertisement.toString() + " - Depending on: " + subscriptions; - } else { - return advType + ": " + advertisement.toString(); - } - } + @Override + public String toString() { + if (subscriptions != null) { + return advType + ": " + advertisement.toString() + " - Depending on: " + subscriptions; + } else { + return advType + ": " + advertisement.toString(); + } + } } diff --git a/Dream2/src/main/java/dream/common/packets/EventPacket.java b/Dream2/src/main/java/dream/common/packets/EventPacket.java index 5c3167c..2518d2f 100755 --- a/Dream2/src/main/java/dream/common/packets/EventPacket.java +++ b/Dream2/src/main/java/dream/common/packets/EventPacket.java @@ -11,51 +11,53 @@ * Packet used to deliver events, which notify about some state change. */ public class EventPacket implements Serializable { - private static final long serialVersionUID = 8208653909787190211L; - public static final String subject = "__DREAM_PUBLICATION_PACKET_SUBJECT"; - - private final Event event; - - // Uniquely identifies a propagation. In the case of complete glitch free and - // atomic consistency, it is identical to the id of the read lock associated - // to the propagation. - private final UUID id; - - // Original source of the change - private final String source; - - // Nodes that should release the lock for the propagation, if any - private final Set lockReleaseNodes = new HashSet<>(); - - public EventPacket(Event event, UUID id, String source) { - this.event = event; - this.id = id; - this.source = source; - } - - public final Event getEvent() { - return event; - } - - public final UUID getId() { - return id; - } - - public final String getSource() { - return source; - } - - public final void setLockReleaseNodes(Set lockReleaseNodes) { - this.lockReleaseNodes.addAll(lockReleaseNodes); - } - - public final Set getLockReleaseNodes() { - return lockReleaseNodes; - } - - @Override - public String toString() { - return "EventPacket [event=" + event + ", id=" + id + ", source=" + source + ", lockReleaseNodes=" + lockReleaseNodes + "]"; - } + private static final long serialVersionUID = 8208653909787190211L; + public static final String subject = "__DREAM_PUBLICATION_PACKET_SUBJECT"; + + private final Event event; + + // Uniquely identifies a propagation. In the case of complete glitch free + // and + // atomic consistency, it is identical to the id of the read lock associated + // to the propagation. + private final UUID id; + + // Original source of the change + private final String source; + + // Nodes that should release the lock for the propagation, if any + private final Set lockReleaseNodes = new HashSet<>(); + + public EventPacket(Event event, UUID id, String source) { + this.event = event; + this.id = id; + this.source = source; + } + + public final Event getEvent() { + return event; + } + + public final UUID getId() { + return id; + } + + public final String getSource() { + return source; + } + + public final void setLockReleaseNodes(Set lockReleaseNodes) { + this.lockReleaseNodes.addAll(lockReleaseNodes); + } + + public final Set getLockReleaseNodes() { + return lockReleaseNodes; + } + + @Override + public String toString() { + return "EventPacket [event=" + event + ", id=" + id + ", source=" + source + ", lockReleaseNodes=" + + lockReleaseNodes + "]"; + } } diff --git a/Dream2/src/main/java/dream/common/packets/SubscriptionPacket.java b/Dream2/src/main/java/dream/common/packets/SubscriptionPacket.java index 38cf0de..68230d2 100755 --- a/Dream2/src/main/java/dream/common/packets/SubscriptionPacket.java +++ b/Dream2/src/main/java/dream/common/packets/SubscriptionPacket.java @@ -10,62 +10,62 @@ * specific events. */ public class SubscriptionPacket implements Serializable { - private static final long serialVersionUID = -9026500933220636540L; - public static final String subject = "__DREAM_SUBSCRIPTION_PACKET_SUBJECT"; + private static final long serialVersionUID = -9026500933220636540L; + public static final String subject = "__DREAM_SUBSCRIPTION_PACKET_SUBJECT"; - private final Subscription subscription; - private final SubType subType; + private final Subscription subscription; + private final SubType subType; - public SubscriptionPacket(Subscription subscription, SubType subType) { - this.subscription = subscription; - this.subType = subType; - } + public SubscriptionPacket(Subscription subscription, SubType subType) { + this.subscription = subscription; + this.subType = subType; + } - public final SubType getSubType() { - return subType; - } + public final SubType getSubType() { + return subType; + } - public final Subscription getSubscription() { - return subscription; - } + public final Subscription getSubscription() { + return subscription; + } - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + (subType == null ? 0 : subType.hashCode()); - result = prime * result + (subscription == null ? 0 : subscription.hashCode()); - return result; - } + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + (subType == null ? 0 : subType.hashCode()); + result = prime * result + (subscription == null ? 0 : subscription.hashCode()); + return result; + } - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (!(obj instanceof SubscriptionPacket)) { - return false; - } - final SubscriptionPacket other = (SubscriptionPacket) obj; - if (subType != other.subType) { - return false; - } - if (subscription == null) { - if (other.subscription != null) { - return false; - } - } else if (!subscription.equals(other.subscription)) { - return false; - } - return true; - } + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof SubscriptionPacket)) { + return false; + } + final SubscriptionPacket other = (SubscriptionPacket) obj; + if (subType != other.subType) { + return false; + } + if (subscription == null) { + if (other.subscription != null) { + return false; + } + } else if (!subscription.equals(other.subscription)) { + return false; + } + return true; + } - @Override - public String toString() { - return subType + " " + subscription.toString(); - } + @Override + public String toString() { + return subType + " " + subscription.toString(); + } } diff --git a/Dream2/src/main/java/dream/common/packets/content/AdvType.java b/Dream2/src/main/java/dream/common/packets/content/AdvType.java index 3aa2541..a6653f2 100755 --- a/Dream2/src/main/java/dream/common/packets/content/AdvType.java +++ b/Dream2/src/main/java/dream/common/packets/content/AdvType.java @@ -1,5 +1,5 @@ package dream.common.packets.content; public enum AdvType { - ADV, UNADV + ADV, UNADV } diff --git a/Dream2/src/main/java/dream/common/packets/content/Advertisement.java b/Dream2/src/main/java/dream/common/packets/content/Advertisement.java index 516662e..1dda1ce 100755 --- a/Dream2/src/main/java/dream/common/packets/content/Advertisement.java +++ b/Dream2/src/main/java/dream/common/packets/content/Advertisement.java @@ -3,35 +3,35 @@ import java.io.Serializable; public class Advertisement implements Serializable { - private static final long serialVersionUID = -6636280874981657399L; + private static final long serialVersionUID = -6636280874981657399L; - private final String hostId; - private final String objectId; + private final String hostId; + private final String objectId; - public Advertisement(String hostId, String objectId) { - this.hostId = hostId; - this.objectId = objectId; - } + public Advertisement(String hostId, String objectId) { + this.hostId = hostId; + this.objectId = objectId; + } - public boolean isSatisfiedBy(Subscription sub) { - return hostId.equals(sub.getHostId()) && objectId.equals(sub.getObjectId()); - } + public boolean isSatisfiedBy(Subscription sub) { + return hostId.equals(sub.getHostId()) && objectId.equals(sub.getObjectId()); + } - public final String getObjectId() { - return objectId; - } + public final String getObjectId() { + return objectId; + } - public final String getHostId() { - return hostId; - } + public final String getHostId() { + return hostId; + } - public final String getSignature() { - return objectId + "@" + hostId; - } + public final String getSignature() { + return objectId + "@" + hostId; + } - @Override - public String toString() { - return "Advertisement [" + objectId + "@" + hostId + "]"; - } + @Override + public String toString() { + return "Advertisement [" + objectId + "@" + hostId + "]"; + } } diff --git a/Dream2/src/main/java/dream/common/packets/content/Event.java b/Dream2/src/main/java/dream/common/packets/content/Event.java index e437575..3a8c8e3 100755 --- a/Dream2/src/main/java/dream/common/packets/content/Event.java +++ b/Dream2/src/main/java/dream/common/packets/content/Event.java @@ -3,37 +3,37 @@ import java.io.Serializable; public class Event implements Serializable { - private static final long serialVersionUID = 831217881290695190L; - - private final String objectId; - private final String hostId; - private final T val; - - public Event(String hostId, String objectId, T val) { - this.hostId = hostId; - this.objectId = objectId; - this.val = val; - } - - public final String getObjectId() { - return objectId; - } - - public final String getHostId() { - return hostId; - } - - public final T getVal() { - return val; - } - - public final String getSignature() { - return objectId + "@" + hostId; - } - - @Override - public String toString() { - return objectId + "@" + hostId + "(val = " + val.toString() + ")"; - } + private static final long serialVersionUID = 831217881290695190L; + + private final String objectId; + private final String hostId; + private final T val; + + public Event(String hostId, String objectId, T val) { + this.hostId = hostId; + this.objectId = objectId; + this.val = val; + } + + public final String getObjectId() { + return objectId; + } + + public final String getHostId() { + return hostId; + } + + public final T getVal() { + return val; + } + + public final String getSignature() { + return objectId + "@" + hostId; + } + + @Override + public String toString() { + return objectId + "@" + hostId + "(val = " + val.toString() + ")"; + } } diff --git a/Dream2/src/main/java/dream/common/packets/content/SubType.java b/Dream2/src/main/java/dream/common/packets/content/SubType.java index a328524..53fa264 100755 --- a/Dream2/src/main/java/dream/common/packets/content/SubType.java +++ b/Dream2/src/main/java/dream/common/packets/content/SubType.java @@ -1,5 +1,5 @@ package dream.common.packets.content; public enum SubType { - SUB, UNSUB + SUB, UNSUB } diff --git a/Dream2/src/main/java/dream/common/packets/content/Subscription.java b/Dream2/src/main/java/dream/common/packets/content/Subscription.java index 9f5dcd9..f0c3e51 100755 --- a/Dream2/src/main/java/dream/common/packets/content/Subscription.java +++ b/Dream2/src/main/java/dream/common/packets/content/Subscription.java @@ -8,60 +8,60 @@ import dream.common.SerializablePredicate; public class Subscription implements Serializable { - private static final long serialVersionUID = -3452847781395458670L; + private static final long serialVersionUID = -3452847781395458670L; - private final String objectId; - private final String hostId; - private final UUID subId; - private final List> constraints = new ArrayList>(); + private final String objectId; + private final String hostId; + private final UUID subId; + private final List> constraints = new ArrayList>(); - public Subscription(String hostId, String objectId) { - this.hostId = hostId; - this.objectId = objectId; - this.subId = UUID.randomUUID(); - this.constraints.addAll(constraints); - } + public Subscription(String hostId, String objectId) { + this.hostId = hostId; + this.objectId = objectId; + this.subId = UUID.randomUUID(); + this.constraints.addAll(constraints); + } - public Subscription(String hostId, String objectId, List> constraints) { - this(hostId, objectId); - this.constraints.addAll(constraints); - } + public Subscription(String hostId, String objectId, List> constraints) { + this(hostId, objectId); + this.constraints.addAll(constraints); + } - public final boolean isSatisfiedBy(Event ev) { - @SuppressWarnings("unchecked") - final T val = (T) ev.getVal(); - return hostId.equals(ev.getHostId()) && // - objectId.equals(ev.getObjectId()) && // - constraints.stream().allMatch(c -> c.test(val)); - } + public final boolean isSatisfiedBy(Event ev) { + @SuppressWarnings("unchecked") + final T val = (T) ev.getVal(); + return hostId.equals(ev.getHostId()) && // + objectId.equals(ev.getObjectId()) && // + constraints.stream().allMatch(c -> c.test(val)); + } - public final boolean matchesOnlySignatureOf(Event ev) { - @SuppressWarnings("unchecked") - final T val = (T) ev.getVal(); - return hostId.equals(ev.getHostId()) && // - objectId.equals(ev.getObjectId()) && // - constraints.stream().anyMatch(c -> !c.test(val)); - } + public final boolean matchesOnlySignatureOf(Event ev) { + @SuppressWarnings("unchecked") + final T val = (T) ev.getVal(); + return hostId.equals(ev.getHostId()) && // + objectId.equals(ev.getObjectId()) && // + constraints.stream().anyMatch(c -> !c.test(val)); + } - public final String getObjectId() { - return objectId; - } + public final String getObjectId() { + return objectId; + } - public final String getHostId() { - return hostId; - } + public final String getHostId() { + return hostId; + } - public final UUID getSubId() { - return subId; - } + public final UUID getSubId() { + return subId; + } - public final String getSignature() { - return objectId + "@" + hostId; - } + public final String getSignature() { + return objectId + "@" + hostId; + } - @Override - public String toString() { - return objectId + "@" + hostId + "(" + constraints + ")"; - } + @Override + public String toString() { + return objectId + "@" + hostId + "(" + constraints + ")"; + } } diff --git a/Dream2/src/main/java/dream/common/packets/discovery/LockManagerHelloPacket.java b/Dream2/src/main/java/dream/common/packets/discovery/LockManagerHelloPacket.java index 1f3cd04..9c94dd1 100644 --- a/Dream2/src/main/java/dream/common/packets/discovery/LockManagerHelloPacket.java +++ b/Dream2/src/main/java/dream/common/packets/discovery/LockManagerHelloPacket.java @@ -7,7 +7,7 @@ * connects to it. */ public class LockManagerHelloPacket implements Serializable { - private static final long serialVersionUID = -7563002622651694641L; + private static final long serialVersionUID = -7563002622651694641L; - public static final String subject = "__DREAM_LOCK_MANAGER_HELLO_PACKET_SUBJECT"; + public static final String subject = "__DREAM_LOCK_MANAGER_HELLO_PACKET_SUBJECT"; } diff --git a/Dream2/src/main/java/dream/common/packets/discovery/ServerHelloPacket.java b/Dream2/src/main/java/dream/common/packets/discovery/ServerHelloPacket.java index 6e6b227..35f98f0 100644 --- a/Dream2/src/main/java/dream/common/packets/discovery/ServerHelloPacket.java +++ b/Dream2/src/main/java/dream/common/packets/discovery/ServerHelloPacket.java @@ -7,7 +7,7 @@ * connects to it. */ public class ServerHelloPacket implements Serializable { - private static final long serialVersionUID = 3429557416466895040L; + private static final long serialVersionUID = 3429557416466895040L; - public static final String subject = "__DREAM_SERVER_HELLO_PACKET_SUBJECT"; + public static final String subject = "__DREAM_SERVER_HELLO_PACKET_SUBJECT"; } diff --git a/Dream2/src/main/java/dream/common/packets/locking/LockGrantPacket.java b/Dream2/src/main/java/dream/common/packets/locking/LockGrantPacket.java index 15f1b42..7a073a6 100644 --- a/Dream2/src/main/java/dream/common/packets/locking/LockGrantPacket.java +++ b/Dream2/src/main/java/dream/common/packets/locking/LockGrantPacket.java @@ -4,22 +4,22 @@ import java.util.UUID; public class LockGrantPacket implements Serializable { - private static final long serialVersionUID = -3499224800050816098L; - public static final String subject = "__DREAM_LOCK_GRANT_PACKET_SUBJECT"; + private static final long serialVersionUID = -3499224800050816098L; + public static final String subject = "__DREAM_LOCK_GRANT_PACKET_SUBJECT"; - private final UUID lockID; + private final UUID lockID; - public LockGrantPacket(LockRequestPacket reqPkt) { - this.lockID = reqPkt.getLockID(); - } + public LockGrantPacket(LockRequestPacket reqPkt) { + this.lockID = reqPkt.getLockID(); + } - public final UUID getLockID() { - return lockID; - } + public final UUID getLockID() { + return lockID; + } - @Override - public String toString() { - return "LockGrantPacket [lockID=" + lockID + "]"; - } + @Override + public String toString() { + return "LockGrantPacket [lockID=" + lockID + "]"; + } } diff --git a/Dream2/src/main/java/dream/common/packets/locking/LockReleasePacket.java b/Dream2/src/main/java/dream/common/packets/locking/LockReleasePacket.java index 811a1be..bd7b207 100644 --- a/Dream2/src/main/java/dream/common/packets/locking/LockReleasePacket.java +++ b/Dream2/src/main/java/dream/common/packets/locking/LockReleasePacket.java @@ -8,22 +8,22 @@ * assumes that requests are sequential (e.g., generated from a single server). */ public class LockReleasePacket implements Serializable { - private static final long serialVersionUID = -1523880233653918696L; - public static final String subject = "__DREAM_LOCK_RELEASE_PACKET_SUBJECT"; + private static final long serialVersionUID = -1523880233653918696L; + public static final String subject = "__DREAM_LOCK_RELEASE_PACKET_SUBJECT"; - private final UUID lockID; + private final UUID lockID; - public LockReleasePacket(UUID lockID) { - this.lockID = lockID; - } + public LockReleasePacket(UUID lockID) { + this.lockID = lockID; + } - public final UUID getLockID() { - return lockID; - } + public final UUID getLockID() { + return lockID; + } - @Override - public String toString() { - return "LockReleasePacket [lockID=" + lockID + "]"; - } + @Override + public String toString() { + return "LockReleasePacket [lockID=" + lockID + "]"; + } } diff --git a/Dream2/src/main/java/dream/common/packets/locking/LockRequestPacket.java b/Dream2/src/main/java/dream/common/packets/locking/LockRequestPacket.java index 764966b..7f42071 100644 --- a/Dream2/src/main/java/dream/common/packets/locking/LockRequestPacket.java +++ b/Dream2/src/main/java/dream/common/packets/locking/LockRequestPacket.java @@ -8,52 +8,52 @@ import polimi.reds.NodeDescriptor; public class LockRequestPacket implements Serializable { - private static final long serialVersionUID = -1523880233653918696L; - public static final String subject = "__DREAM_LOCK_REQUEST_PACKET_SUBJECT"; - - /** - * Node that requests the lock. - */ - private final NodeDescriptor applicant; - - /** - * Nodes to lock. - */ - private final Set lockNodes; - - /** - * Nodes that will sent a lock release. - */ - private final Set unlockNodes; - - private final LockType type; - private final UUID lockID = UUID.randomUUID(); - - public LockRequestPacket(NodeDescriptor applicant, Set lockNodes, Set unlockNodes, LockType type) { - this.applicant = applicant; - this.lockNodes = new HashSet<>(lockNodes); - this.unlockNodes = new HashSet<>(unlockNodes); - this.type = type; - } - - public final NodeDescriptor getApplicant() { - return applicant; - } - - public final Set getLockNodes() { - return lockNodes; - } - - public final Set getUnlockNodes() { - return unlockNodes; - } - - public final LockType getType() { - return type; - } - - public final UUID getLockID() { - return lockID; - } + private static final long serialVersionUID = -1523880233653918696L; + public static final String subject = "__DREAM_LOCK_REQUEST_PACKET_SUBJECT"; + + /** + * Node that requests the lock. + */ + private final NodeDescriptor applicant; + + /** + * Nodes to lock. + */ + private final Set lockNodes; + + /** + * Nodes that will sent a lock release. + */ + private final Set unlockNodes; + + private final LockType type; + private final UUID lockID = UUID.randomUUID(); + + public LockRequestPacket(NodeDescriptor applicant, Set lockNodes, Set unlockNodes, LockType type) { + this.applicant = applicant; + this.lockNodes = new HashSet<>(lockNodes); + this.unlockNodes = new HashSet<>(unlockNodes); + this.type = type; + } + + public final NodeDescriptor getApplicant() { + return applicant; + } + + public final Set getLockNodes() { + return lockNodes; + } + + public final Set getUnlockNodes() { + return unlockNodes; + } + + public final LockType getType() { + return type; + } + + public final UUID getLockID() { + return lockID; + } } diff --git a/Dream2/src/main/java/dream/common/packets/locking/LockType.java b/Dream2/src/main/java/dream/common/packets/locking/LockType.java index b5b785d..bc07cdb 100644 --- a/Dream2/src/main/java/dream/common/packets/locking/LockType.java +++ b/Dream2/src/main/java/dream/common/packets/locking/LockType.java @@ -1,5 +1,5 @@ package dream.common.packets.locking; public enum LockType { - READ_WRITE, READ_ONLY + READ_WRITE, READ_ONLY } diff --git a/Dream2/src/main/java/dream/common/utils/AtomicDependencyDetector.java b/Dream2/src/main/java/dream/common/utils/AtomicDependencyDetector.java index e1d56e4..3b1f59e 100644 --- a/Dream2/src/main/java/dream/common/utils/AtomicDependencyDetector.java +++ b/Dream2/src/main/java/dream/common/utils/AtomicDependencyDetector.java @@ -4,14 +4,14 @@ public class AtomicDependencyDetector extends InterSourceDependencyDetector { - @Override - protected void computeDataStructs() { - // Nothing to do - } + @Override + protected void computeDataStructs() { + // Nothing to do + } - @Override - public Set getNodesToLockFor(String source) { - return dependencyClosure.get(source); - } + @Override + public Set getNodesToLockFor(String source) { + return dependencyClosure.get(source); + } } diff --git a/Dream2/src/main/java/dream/common/utils/CompleteGlitchFreeDependencyDetector.java b/Dream2/src/main/java/dream/common/utils/CompleteGlitchFreeDependencyDetector.java index 30023ca..db1eb7e 100644 --- a/Dream2/src/main/java/dream/common/utils/CompleteGlitchFreeDependencyDetector.java +++ b/Dream2/src/main/java/dream/common/utils/CompleteGlitchFreeDependencyDetector.java @@ -17,48 +17,48 @@ * depends on both s and s'. */ public class CompleteGlitchFreeDependencyDetector extends InterSourceDependencyDetector { - private final Map> sharedExpressions = new HashMap<>(); + private final Map> sharedExpressions = new HashMap<>(); - @Override - public final void computeDataStructs() { - computeSharedExpressions(); - } + @Override + public final void computeDataStructs() { + computeSharedExpressions(); + } - @Override - public final synchronized Set getNodesToLockFor(String source) { - return sharedExpressions.containsKey(source) ? // - sharedExpressions.get(source) - : // - new HashSet<>(); - } + @Override + public final synchronized Set getNodesToLockFor(String source) { + return sharedExpressions.containsKey(source) ? // + sharedExpressions.get(source) + : // + new HashSet<>(); + } - private final void computeSharedExpressions() { - graph.getSources().// - forEach(initExp -> sharedExpressions.put(initExp, computeSharedExpressionsFor(initExp))); - } + private final void computeSharedExpressions() { + graph.getSources().// + forEach(initExp -> sharedExpressions.put(initExp, computeSharedExpressionsFor(initExp))); + } - private final Set computeSharedExpressionsFor(String initialDependency) { - final Set result = new HashSet<>(); - final Set depNodes = dependencyClosure.get(initialDependency); - if (depNodes.size() < 2) { - return result; - } - for (final Entry> entry : dependencyClosure.entrySet()) { - if (entry.getKey().equals(initialDependency)) { - continue; - } - final Set intersect = intersect(depNodes, entry.getValue()); - if (intersect.size() >= 2) { - result.addAll(intersect); - } - } - return result; - } + private final Set computeSharedExpressionsFor(String initialDependency) { + final Set result = new HashSet<>(); + final Set depNodes = dependencyClosure.get(initialDependency); + if (depNodes.size() < 2) { + return result; + } + for (final Entry> entry : dependencyClosure.entrySet()) { + if (entry.getKey().equals(initialDependency)) { + continue; + } + final Set intersect = intersect(depNodes, entry.getValue()); + if (intersect.size() >= 2) { + result.addAll(intersect); + } + } + return result; + } - private final Set intersect(Set set1, Set set2) { - return set1.stream().// - filter(elem -> set2.contains(elem)).// - collect(Collectors.toSet()); - } + private final Set intersect(Set set1, Set set2) { + return set1.stream().// + filter(elem -> set2.contains(elem)).// + collect(Collectors.toSet()); + } } diff --git a/Dream2/src/main/java/dream/common/utils/DependencyDetector.java b/Dream2/src/main/java/dream/common/utils/DependencyDetector.java index ddf25af..c95614e 100644 --- a/Dream2/src/main/java/dream/common/utils/DependencyDetector.java +++ b/Dream2/src/main/java/dream/common/utils/DependencyDetector.java @@ -9,10 +9,10 @@ */ public interface DependencyDetector { - /** - * Compute the accessory data structures required to speed-up the query - * process. It needs to be invoked when the dependency graph changes. - */ - public void consolidate(); + /** + * Compute the accessory data structures required to speed-up the query + * process. It needs to be invoked when the dependency graph changes. + */ + public void consolidate(); } diff --git a/Dream2/src/main/java/dream/common/utils/DependencyGraph.java b/Dream2/src/main/java/dream/common/utils/DependencyGraph.java index e4b302d..b5fda6b 100644 --- a/Dream2/src/main/java/dream/common/utils/DependencyGraph.java +++ b/Dream2/src/main/java/dream/common/utils/DependencyGraph.java @@ -11,46 +11,46 @@ import dream.common.packets.content.Subscription; public enum DependencyGraph { - instance; - - // Node -> set of nodes it directly depends on - private final Map> graph = new HashMap<>(); - // Sources - private final Set sources = new HashSet<>(); - - public synchronized final void clear() { - graph.clear(); - sources.clear(); - } - - public synchronized final void processAdv(Advertisement adv) { - final String advSignature = adv.getSignature(); - sources.add(advSignature); - } - - public synchronized final void processAdv(Advertisement adv, Set subs) { - final String advSignature = adv.getSignature(); - assert!subs.isEmpty(); - final Set subSignatures = subs.stream().// - map(sub -> sub.getSignature()).// - collect(Collectors.toSet()); - graph.put(advSignature, subSignatures); - } - - public synchronized final void processUnAdv(Advertisement adv) { - // TODO manage unadvertisements - } - - public synchronized final void processUnAdv(Advertisement adv, Set subs) { - // TODO manage unadvertisements - } - - public synchronized final Map> getGraph() { - return graph; - } - - public synchronized final Set getSources() { - return sources; - } + instance; + + // Node -> set of nodes it directly depends on + private final Map> graph = new HashMap<>(); + // Sources + private final Set sources = new HashSet<>(); + + public synchronized final void clear() { + graph.clear(); + sources.clear(); + } + + public synchronized final void processAdv(Advertisement adv) { + final String advSignature = adv.getSignature(); + sources.add(advSignature); + } + + public synchronized final void processAdv(Advertisement adv, Set subs) { + final String advSignature = adv.getSignature(); + assert!subs.isEmpty(); + final Set subSignatures = subs.stream().// + map(sub -> sub.getSignature()).// + collect(Collectors.toSet()); + graph.put(advSignature, subSignatures); + } + + public synchronized final void processUnAdv(Advertisement adv) { + // TODO manage unadvertisements + } + + public synchronized final void processUnAdv(Advertisement adv, Set subs) { + // TODO manage unadvertisements + } + + public synchronized final Map> getGraph() { + return graph; + } + + public synchronized final Set getSources() { + return sources; + } } diff --git a/Dream2/src/main/java/dream/common/utils/DependencyGraphUtils.java b/Dream2/src/main/java/dream/common/utils/DependencyGraphUtils.java index 72fa660..2d49283 100644 --- a/Dream2/src/main/java/dream/common/utils/DependencyGraphUtils.java +++ b/Dream2/src/main/java/dream/common/utils/DependencyGraphUtils.java @@ -9,111 +9,113 @@ class DependencyGraphUtils { - /** - * Return, for each node, the set of sources it directly or indirectly depends - * on. - * - * @return the set of sources each node directly or indirectly depends on. - */ - static final Map> computeRelevantSources() { - final Map> result = new HashMap<>(); - final DependencyGraph depGraph = DependencyGraph.instance; - depGraph.getSources().forEach(s -> { - final HashSet sources = new HashSet<>(); - sources.add(s); - result.put(s, sources); - }); - depGraph.getGraph().keySet().// - forEach(node -> result.put(node, computeRelevantSourcesFor(node, depGraph))); - return result; - } + /** + * Return, for each node, the set of sources it directly or indirectly + * depends on. + * + * @return the set of sources each node directly or indirectly depends on. + */ + static final Map> computeRelevantSources() { + final Map> result = new HashMap<>(); + final DependencyGraph depGraph = DependencyGraph.instance; + depGraph.getSources().forEach(s -> { + final HashSet sources = new HashSet<>(); + sources.add(s); + result.put(s, sources); + }); + depGraph.getGraph().keySet().// + forEach(node -> result.put(node, computeRelevantSourcesFor(node, depGraph))); + return result; + } - /** - * Return the set of final nodes in the propagation graph, that is to say, all - * the nodes that do not have any other nodes that depends on them. - * - * @return the set of final nodes in the propagation graph. - */ - static final Set computeFinalNodes() { - final Map> graph = DependencyGraph.instance.getGraph(); - final Set sources = DependencyGraph.instance.getSources(); + /** + * Return the set of final nodes in the propagation graph, that is to say, + * all the nodes that do not have any other nodes that depends on them. + * + * @return the set of final nodes in the propagation graph. + */ + static final Set computeFinalNodes() { + final Map> graph = DependencyGraph.instance.getGraph(); + final Set sources = DependencyGraph.instance.getSources(); - final Set allNodes = new HashSet<>(graph.keySet()); - allNodes.addAll(sources); + final Set allNodes = new HashSet<>(graph.keySet()); + allNodes.addAll(sources); - return allNodes.stream()// - .filter(node -> graph.values().stream() // - .allMatch(depNodes -> !depNodes.contains(node))) // - .collect(Collectors.toSet()); - } + return allNodes.stream()// + .filter(node -> graph.values().stream() // + .allMatch(depNodes -> !depNodes.contains(node))) // + .collect(Collectors.toSet()); + } - /** - * Return, for each source, the set of nodes that directly or indirectly - * depend on it. - * - * @return for each source, the set of nodes that directly or indirectly - * depend on it. - */ - static final Map> computeDependencyClosure() { - final Map> result = new HashMap<>(); - final DependencyGraph depGraph = DependencyGraph.instance; - depGraph.getSources().forEach(s -> result.put(s, computeDependencyClosureFor(s, depGraph))); - return result; - } + /** + * Return, for each source, the set of nodes that directly or indirectly + * depend on it. + * + * @return for each source, the set of nodes that directly or indirectly + * depend on it. + */ + static final Map> computeDependencyClosure() { + final Map> result = new HashMap<>(); + final DependencyGraph depGraph = DependencyGraph.instance; + depGraph.getSources().forEach(s -> result.put(s, computeDependencyClosureFor(s, depGraph))); + return result; + } - private static final Set computeDependencyClosureFor(String source, DependencyGraph depGraph) { - final Set result = new HashSet<>(); - final Set newNodes = new HashSet<>(); - result.add(source); - newNodes.add(source); - computeDependencyClosureFor(newNodes, result, depGraph); - return result; - } + private static final Set computeDependencyClosureFor(String source, DependencyGraph depGraph) { + final Set result = new HashSet<>(); + final Set newNodes = new HashSet<>(); + result.add(source); + newNodes.add(source); + computeDependencyClosureFor(newNodes, result, depGraph); + return result; + } - private static final void computeDependencyClosureFor(Set newNodes, Set accumulator, DependencyGraph depGraph) { - final Set newNodesToEvaluate = new HashSet<>(); - newNodes.forEach(n -> { - final Set depNodes = dependentNodesFor(n, depGraph); - depNodes.removeAll(accumulator); - accumulator.addAll(depNodes); - newNodesToEvaluate.addAll(depNodes); - }); - if (!newNodesToEvaluate.isEmpty()) { - computeDependencyClosureFor(newNodesToEvaluate, accumulator, depGraph); - } - } + private static final void computeDependencyClosureFor(Set newNodes, Set accumulator, + DependencyGraph depGraph) { + final Set newNodesToEvaluate = new HashSet<>(); + newNodes.forEach(n -> { + final Set depNodes = dependentNodesFor(n, depGraph); + depNodes.removeAll(accumulator); + accumulator.addAll(depNodes); + newNodesToEvaluate.addAll(depNodes); + }); + if (!newNodesToEvaluate.isEmpty()) { + computeDependencyClosureFor(newNodesToEvaluate, accumulator, depGraph); + } + } - /** - * Return the set of nodes that directly depend on node. - */ - private static Set dependentNodesFor(String node, DependencyGraph depGraph) { - return depGraph.getGraph().entrySet().stream().// - filter(e -> e.getValue().contains(node)).// - map(e -> e.getKey()).// - collect(Collectors.toSet()); - } + /** + * Return the set of nodes that directly depend on node. + */ + private static Set dependentNodesFor(String node, DependencyGraph depGraph) { + return depGraph.getGraph().entrySet().stream().// + filter(e -> e.getValue().contains(node)).// + map(e -> e.getKey()).// + collect(Collectors.toSet()); + } - private static final Set computeRelevantSourcesFor(String node, DependencyGraph depGraph) { - final Set newNodes = new HashSet<>(); - final Set accumulator = new HashSet<>(); - newNodes.add(node); - computeRelevantSourcesFor(newNodes, accumulator, depGraph); - return accumulator; - } + private static final Set computeRelevantSourcesFor(String node, DependencyGraph depGraph) { + final Set newNodes = new HashSet<>(); + final Set accumulator = new HashSet<>(); + newNodes.add(node); + computeRelevantSourcesFor(newNodes, accumulator, depGraph); + return accumulator; + } - private static final void computeRelevantSourcesFor(Set newNodes, Set accumulator, DependencyGraph depGraph) { - newNodes.stream().// - filter(depGraph.getSources()::contains).// - collect(() -> accumulator, Set::add, Set::addAll); + private static final void computeRelevantSourcesFor(Set newNodes, Set accumulator, + DependencyGraph depGraph) { + newNodes.stream().// + filter(depGraph.getSources()::contains).// + collect(() -> accumulator, Set::add, Set::addAll); - final Set newNodesToEvaluate = newNodes.stream().// - filter(expr -> !depGraph.getSources().contains(expr)).// - map(depGraph.getGraph()::get).// - collect(HashSet::new, HashSet::addAll, HashSet::addAll); + final Set newNodesToEvaluate = newNodes.stream().// + filter(expr -> !depGraph.getSources().contains(expr)).// + map(depGraph.getGraph()::get).// + collect(HashSet::new, HashSet::addAll, HashSet::addAll); - if (!newNodesToEvaluate.isEmpty()) { - computeRelevantSourcesFor(newNodesToEvaluate, accumulator, depGraph); - } - } + if (!newNodesToEvaluate.isEmpty()) { + computeRelevantSourcesFor(newNodesToEvaluate, accumulator, depGraph); + } + } } diff --git a/Dream2/src/main/java/dream/common/utils/FinalNodesDetector.java b/Dream2/src/main/java/dream/common/utils/FinalNodesDetector.java index d467823..5f02d0a 100755 --- a/Dream2/src/main/java/dream/common/utils/FinalNodesDetector.java +++ b/Dream2/src/main/java/dream/common/utils/FinalNodesDetector.java @@ -11,40 +11,40 @@ * given source and do not have any other node that deoends on them. */ public final class FinalNodesDetector { - DependencyGraph depGraph = DependencyGraph.instance; - - // Source -> set of final nodes - private final Map> finalNodes = new HashMap<>(); - - public final synchronized void consolidate() { - computeFinalNodes(); - } - - /** - * Return the set of final nodes for the given source. - * - * @param source - * the source. - * @return the set of final nodes for source. - */ - public final synchronized Set getFinalNodesFor(String source) { - return finalNodes.get(source); - } - - private final void computeFinalNodes() { - finalNodes.clear(); - final Map> closure = DependencyGraphUtils.computeDependencyClosure(); - final Set finalNodesSet = DependencyGraphUtils.computeFinalNodes(); - - closure.entrySet().forEach(e -> { - finalNodes.put(e.getKey(), intersect(finalNodesSet, e.getValue())); - }); - } - - private final Set intersect(Set set1, Set set2) { - final Set result = new HashSet<>(set1); - result.retainAll(set2); - return result; - } + DependencyGraph depGraph = DependencyGraph.instance; + + // Source -> set of final nodes + private final Map> finalNodes = new HashMap<>(); + + public final synchronized void consolidate() { + computeFinalNodes(); + } + + /** + * Return the set of final nodes for the given source. + * + * @param source + * the source. + * @return the set of final nodes for source. + */ + public final synchronized Set getFinalNodesFor(String source) { + return finalNodes.get(source); + } + + private final void computeFinalNodes() { + finalNodes.clear(); + final Map> closure = DependencyGraphUtils.computeDependencyClosure(); + final Set finalNodesSet = DependencyGraphUtils.computeFinalNodes(); + + closure.entrySet().forEach(e -> { + finalNodes.put(e.getKey(), intersect(finalNodesSet, e.getValue())); + }); + } + + private final Set intersect(Set set1, Set set2) { + final Set result = new HashSet<>(set1); + result.retainAll(set2); + return result; + } } diff --git a/Dream2/src/main/java/dream/common/utils/InterSourceDependencyDetector.java b/Dream2/src/main/java/dream/common/utils/InterSourceDependencyDetector.java index 288eced..ba7d68f 100644 --- a/Dream2/src/main/java/dream/common/utils/InterSourceDependencyDetector.java +++ b/Dream2/src/main/java/dream/common/utils/InterSourceDependencyDetector.java @@ -15,25 +15,25 @@ * guarantees. */ public abstract class InterSourceDependencyDetector { - protected final DependencyGraph graph = DependencyGraph.instance; + protected final DependencyGraph graph = DependencyGraph.instance; - protected Map> dependencyClosure = new HashMap<>(); + protected Map> dependencyClosure = new HashMap<>(); - public final synchronized void consolidate() { - dependencyClosure = DependencyGraphUtils.computeDependencyClosure(); - computeDataStructs(); - } + public final synchronized void consolidate() { + dependencyClosure = DependencyGraphUtils.computeDependencyClosure(); + computeDataStructs(); + } - protected abstract void computeDataStructs(); + protected abstract void computeDataStructs(); - /** - * Returns the nodes that require to be locked during the propagation of an - * update originated at the given source. - * - * @param source - * the source. - * @return the nodes that need to be locked during the propagation. - */ - public abstract Set getNodesToLockFor(String source); + /** + * Returns the nodes that require to be locked during the propagation of an + * update originated at the given source. + * + * @param source + * the source. + * @return the nodes that need to be locked during the propagation. + */ + public abstract Set getNodesToLockFor(String source); } diff --git a/Dream2/src/main/java/dream/common/utils/IntraSourceDependencyDetector.java b/Dream2/src/main/java/dream/common/utils/IntraSourceDependencyDetector.java index 0020ba2..9e18991 100755 --- a/Dream2/src/main/java/dream/common/utils/IntraSourceDependencyDetector.java +++ b/Dream2/src/main/java/dream/common/utils/IntraSourceDependencyDetector.java @@ -9,73 +9,73 @@ import dream.common.packets.content.Event; public enum IntraSourceDependencyDetector implements DependencyDetector { - instance; + instance; - private final DependencyGraph depGraph = DependencyGraph.instance; - private Map> relevantSources = new HashMap<>(); + private final DependencyGraph depGraph = DependencyGraph.instance; + private Map> relevantSources = new HashMap<>(); - // Stores the dependencies to compute expressions - // Expression Expr -> Initial expression that caused the recomputation -> - // Wait recommendations - private final Map>> recommendations = new HashMap<>(); + // Stores the dependencies to compute expressions + // Expression Expr -> Initial expression that caused the recomputation -> + // Wait recommendations + private final Map>> recommendations = new HashMap<>(); - public synchronized final Set getWaitRecommendations(Event event, String initialVar) { - final Map> innerMap = recommendations.get(event.getSignature()); - if (innerMap == null) { - return new HashSet<>(); - } - return innerMap.containsKey(initialVar) ? innerMap.get(initialVar) : new HashSet<>(); - } + public synchronized final Set getWaitRecommendations(Event event, String initialVar) { + final Map> innerMap = recommendations.get(event.getSignature()); + if (innerMap == null) { + return new HashSet<>(); + } + return innerMap.containsKey(initialVar) ? innerMap.get(initialVar) : new HashSet<>(); + } - @Override - public synchronized final void consolidate() { - recommendations.clear(); - computeRecommendations(); - } + @Override + public synchronized final void consolidate() { + recommendations.clear(); + computeRecommendations(); + } - private final void computeRecommendations() { - recommendations.clear(); - relevantSources = DependencyGraphUtils.computeRelevantSources(); - depGraph.getGraph().keySet().forEach(expr -> { - relevantSources.get(expr).forEach(initialExpr -> storeRecommendationsFor(expr, initialExpr)); - }); - } + private final void computeRecommendations() { + recommendations.clear(); + relevantSources = DependencyGraphUtils.computeRelevantSources(); + depGraph.getGraph().keySet().forEach(expr -> { + relevantSources.get(expr).forEach(initialExpr -> storeRecommendationsFor(expr, initialExpr)); + }); + } - private final void storeRecommendationsFor(String expr, String initialExpr) { - final Set dependentSiblings = computeDependentSiblingsFor(expr, initialExpr); + private final void storeRecommendationsFor(String expr, String initialExpr) { + final Set dependentSiblings = computeDependentSiblingsFor(expr, initialExpr); - if (dependentSiblings.size() > 1) { - dependentSiblings.forEach(sibling -> { - Map> recommendationsMap = recommendations.get(sibling); - if (recommendationsMap == null) { - recommendationsMap = new HashMap<>(); - recommendations.put(sibling, recommendationsMap); - } - Set recommendationsSet = recommendationsMap.get(initialExpr); - if (recommendationsSet == null) { - recommendationsSet = new HashSet<>(); - recommendationsMap.put(initialExpr, recommendationsSet); - } - final WaitRecommendations wr = new WaitRecommendations(expr); - recommendationsSet.add(wr); - dependentSiblings.stream().// - filter(e -> !e.equals(sibling)).// - forEach(wr::addRecommendation); - }); - } - } + if (dependentSiblings.size() > 1) { + dependentSiblings.forEach(sibling -> { + Map> recommendationsMap = recommendations.get(sibling); + if (recommendationsMap == null) { + recommendationsMap = new HashMap<>(); + recommendations.put(sibling, recommendationsMap); + } + Set recommendationsSet = recommendationsMap.get(initialExpr); + if (recommendationsSet == null) { + recommendationsSet = new HashSet<>(); + recommendationsMap.put(initialExpr, recommendationsSet); + } + final WaitRecommendations wr = new WaitRecommendations(expr); + recommendationsSet.add(wr); + dependentSiblings.stream().// + filter(e -> !e.equals(sibling)).// + forEach(wr::addRecommendation); + }); + } + } - /** - * Compute the set of all expressions with the following properties: - * - * 1) expr directly depends on them - * - * 2) they directly or indirectly depend on the initialExpression - */ - private final Set computeDependentSiblingsFor(String expr, String initialExpression) { - return depGraph.getGraph().get(expr).stream().// - filter(dep -> relevantSources.get(dep).contains(initialExpression)).// - collect(Collectors.toSet()); - } + /** + * Compute the set of all expressions with the following properties: + * + * 1) expr directly depends on them + * + * 2) they directly or indirectly depend on the initialExpression + */ + private final Set computeDependentSiblingsFor(String expr, String initialExpression) { + return depGraph.getGraph().get(expr).stream().// + filter(dep -> relevantSources.get(dep).contains(initialExpression)).// + collect(Collectors.toSet()); + } } diff --git a/Dream2/src/main/java/dream/common/utils/WaitRecommendations.java b/Dream2/src/main/java/dream/common/utils/WaitRecommendations.java index 8290ae3..cfdb4c1 100755 --- a/Dream2/src/main/java/dream/common/utils/WaitRecommendations.java +++ b/Dream2/src/main/java/dream/common/utils/WaitRecommendations.java @@ -5,44 +5,45 @@ import java.util.Set; /** - * A WaitRecommendations object is stored inside an event E. It is used by the server to prevent glitches while - * delivering E to the clients. It tells a client C that it must wait for other events before processing E. + * A WaitRecommendations object is stored inside an event E. It is used by the + * server to prevent glitches while delivering E to the clients. It tells a + * client C that it must wait for other events before processing E. */ public class WaitRecommendations implements Serializable { - private static final long serialVersionUID = 5700944080087353096L; + private static final long serialVersionUID = 5700944080087353096L; - private final String expression; - private final Set expressionsToWaitFor = new HashSet(); + private final String expression; + private final Set expressionsToWaitFor = new HashSet(); - public WaitRecommendations(String expression) { - this.expression = expression; - } + public WaitRecommendations(String expression) { + this.expression = expression; + } - public final void addRecommendation(String expressionToWaitFor) { - expressionsToWaitFor.add(expressionToWaitFor); - } + public final void addRecommendation(String expressionToWaitFor) { + expressionsToWaitFor.add(expressionToWaitFor); + } - public final String getExpression() { - return expression; - } + public final String getExpression() { + return expression; + } - public final Set getRecommendations() { - return expressionsToWaitFor; - } + public final Set getRecommendations() { + return expressionsToWaitFor; + } - public final WaitRecommendations dup() { - WaitRecommendations result = new WaitRecommendations(expression); - result.expressionsToWaitFor.addAll(expressionsToWaitFor); - return result; - } + public final WaitRecommendations dup() { + WaitRecommendations result = new WaitRecommendations(expression); + result.expressionsToWaitFor.addAll(expressionsToWaitFor); + return result; + } - public final void removeExpressionToWaitFor(String expressionToWaitFor) { - expressionsToWaitFor.remove(expressionToWaitFor); - } + public final void removeExpressionToWaitFor(String expressionToWaitFor) { + expressionsToWaitFor.remove(expressionToWaitFor); + } - @Override - public String toString() { - return "WaitRecommendations [expression=" + expression + ", expressionsToWaitFor=" + expressionsToWaitFor + "]"; - } + @Override + public String toString() { + return "WaitRecommendations [expression=" + expression + ", expressionsToWaitFor=" + expressionsToWaitFor + "]"; + } } diff --git a/Dream2/src/main/java/dream/locking/LockManager.java b/Dream2/src/main/java/dream/locking/LockManager.java index 9112088..f89b767 100644 --- a/Dream2/src/main/java/dream/locking/LockManager.java +++ b/Dream2/src/main/java/dream/locking/LockManager.java @@ -14,175 +14,176 @@ import dream.common.packets.locking.LockType; class LockManager { - // Pending requests - private final List pendingRequests = new ArrayList<>(); - - // All stored locks - private final Map activeLocks = new HashMap<>(); - - // Stored read and write locks - private final Map readLocks = new HashMap<>(); - private final WriteLockManager writeLocks = new WriteLockManager(); - - /** - * Process the given request. Returns true if the lock can be granted. - * - * @param request - * the request. - * @return true if the lock is granted, false otherwise. - */ - final boolean processLockRequest(LockRequestPacket request) { - if (canBeGranted(request)) { - lock(request); - return true; - } else { - pendingRequests.add(request); - return false; - } - } - - /** - * Process the given release. Returns the set of pending requests that can now - * obtain a lock. - * - * @param release - * the release. - * @return the set of granted locks. - */ - final Set processLockRelease(LockReleasePacket release) { - final UUID lockID = release.getLockID(); - assert activeLocks.containsKey(lockID); - final LockRequestPacket reqPkt = activeLocks.get(lockID); - - final Set result = new HashSet<>(); - if (release(reqPkt.getLockID(), reqPkt.getLockNodes(), reqPkt.getType())) { - final Iterator it = pendingRequests.iterator(); - while (it.hasNext()) { - final LockRequestPacket request = it.next(); - if (canBeGranted(request)) { - lock(request); - result.add(request); - it.remove(); - } - } - } - return result; - } - - private final boolean canBeGranted(LockRequestPacket request) { - final LockType type = request.getType(); - final Set lockNodes = request.getLockNodes(); - final boolean writeConflicts = lockNodes.stream()// - .anyMatch(n -> writeLocks.isLocked(n)); - if (writeConflicts) { - return false; - } - final boolean readConflicts = type == LockType.READ_WRITE && // - lockNodes.stream().anyMatch(n -> readLocks.containsKey(n)); - return !readConflicts; - } - - private final void lock(LockRequestPacket request) { - activeLocks.put(request.getLockID(), request); - final LockType type = request.getType(); - final Set lockNodes = request.getLockNodes(); - switch (type) { - case READ_ONLY: - lockNodes.forEach(n -> { - final Integer count = readLocks.get(n); - if (count == null) { - readLocks.put(n, 1); - } else { - readLocks.put(n, count + 1); - } - }); - break; - case READ_WRITE: - writeLocks.grant(request); - break; - default: - assert false : type; - break; - } - } - - /** - * Return true if the packet released at least one node - */ - private final boolean release(UUID lockId, Set lockNodes, LockType type) { - boolean result = false; - switch (type) { - case READ_ONLY: - for (final String lockNode : lockNodes) { - final int newCount = readLocks.get(lockNode) - 1; - if (newCount == 0) { - readLocks.remove(lockNode); - activeLocks.remove(lockId); - result = true; - } else { - readLocks.put(lockNode, newCount); - } - } - break; - case READ_WRITE: - if (writeLocks.release(lockId)) { - activeLocks.remove(lockId); - result = true; - } - break; - default: - assert false : type; - break; - } - return result; - } - - /** - * Store information about the write locks that have been granted. - */ - private class WriteLockManager { - private final Map grantedLocks = new HashMap<>(); - private final Map> lockedNodesMap = new HashMap<>(); - private final Set lockedNodes = new HashSet<>(); - - /** - * Store the lock coming from the given source and having the given lockId. - */ - void grant(LockRequestPacket request) { - final UUID lockId = request.getLockID(); - final Set nodesToLock = request.getLockNodes(); - assert!grantedLocks.containsKey(lockId); - assert!lockedNodesMap.containsKey(lockId); - - grantedLocks.put(lockId, request.getUnlockNodes().size()); - lockedNodesMap.put(lockId, new HashSet<>(nodesToLock)); - lockedNodes.addAll(nodesToLock); - } - - /** - * Return true if the given node is already locked. - */ - boolean isLocked(String node) { - return lockedNodes.contains(node); - } - - /** - * Return true if the lock has been entirely released. - */ - boolean release(UUID lockId) { - assert grantedLocks.containsKey(lockId); - Integer count = grantedLocks.get(lockId); - if (--count == 0) { - grantedLocks.remove(lockId); - lockedNodesMap.get(lockId).forEach(lockedNodes::remove); - lockedNodesMap.remove(lockId); - return true; - } else { - grantedLocks.put(lockId, count); - return false; - } - } - - } + // Pending requests + private final List pendingRequests = new ArrayList<>(); + + // All stored locks + private final Map activeLocks = new HashMap<>(); + + // Stored read and write locks + private final Map readLocks = new HashMap<>(); + private final WriteLockManager writeLocks = new WriteLockManager(); + + /** + * Process the given request. Returns true if the lock can be granted. + * + * @param request + * the request. + * @return true if the lock is granted, false otherwise. + */ + final boolean processLockRequest(LockRequestPacket request) { + if (canBeGranted(request)) { + lock(request); + return true; + } else { + pendingRequests.add(request); + return false; + } + } + + /** + * Process the given release. Returns the set of pending requests that can + * now obtain a lock. + * + * @param release + * the release. + * @return the set of granted locks. + */ + final Set processLockRelease(LockReleasePacket release) { + final UUID lockID = release.getLockID(); + assert activeLocks.containsKey(lockID); + final LockRequestPacket reqPkt = activeLocks.get(lockID); + + final Set result = new HashSet<>(); + if (release(reqPkt.getLockID(), reqPkt.getLockNodes(), reqPkt.getType())) { + final Iterator it = pendingRequests.iterator(); + while (it.hasNext()) { + final LockRequestPacket request = it.next(); + if (canBeGranted(request)) { + lock(request); + result.add(request); + it.remove(); + } + } + } + return result; + } + + private final boolean canBeGranted(LockRequestPacket request) { + final LockType type = request.getType(); + final Set lockNodes = request.getLockNodes(); + final boolean writeConflicts = lockNodes.stream()// + .anyMatch(n -> writeLocks.isLocked(n)); + if (writeConflicts) { + return false; + } + final boolean readConflicts = type == LockType.READ_WRITE && // + lockNodes.stream().anyMatch(n -> readLocks.containsKey(n)); + return !readConflicts; + } + + private final void lock(LockRequestPacket request) { + activeLocks.put(request.getLockID(), request); + final LockType type = request.getType(); + final Set lockNodes = request.getLockNodes(); + switch (type) { + case READ_ONLY: + lockNodes.forEach(n -> { + final Integer count = readLocks.get(n); + if (count == null) { + readLocks.put(n, 1); + } else { + readLocks.put(n, count + 1); + } + }); + break; + case READ_WRITE: + writeLocks.grant(request); + break; + default: + assert false : type; + break; + } + } + + /** + * Return true if the packet released at least one node + */ + private final boolean release(UUID lockId, Set lockNodes, LockType type) { + boolean result = false; + switch (type) { + case READ_ONLY: + for (final String lockNode : lockNodes) { + final int newCount = readLocks.get(lockNode) - 1; + if (newCount == 0) { + readLocks.remove(lockNode); + activeLocks.remove(lockId); + result = true; + } else { + readLocks.put(lockNode, newCount); + } + } + break; + case READ_WRITE: + if (writeLocks.release(lockId)) { + activeLocks.remove(lockId); + result = true; + } + break; + default: + assert false : type; + break; + } + return result; + } + + /** + * Store information about the write locks that have been granted. + */ + private class WriteLockManager { + private final Map grantedLocks = new HashMap<>(); + private final Map> lockedNodesMap = new HashMap<>(); + private final Set lockedNodes = new HashSet<>(); + + /** + * Store the lock coming from the given source and having the given + * lockId. + */ + void grant(LockRequestPacket request) { + final UUID lockId = request.getLockID(); + final Set nodesToLock = request.getLockNodes(); + assert!grantedLocks.containsKey(lockId); + assert!lockedNodesMap.containsKey(lockId); + + grantedLocks.put(lockId, request.getUnlockNodes().size()); + lockedNodesMap.put(lockId, new HashSet<>(nodesToLock)); + lockedNodes.addAll(nodesToLock); + } + + /** + * Return true if the given node is already locked. + */ + boolean isLocked(String node) { + return lockedNodes.contains(node); + } + + /** + * Return true if the lock has been entirely released. + */ + boolean release(UUID lockId) { + assert grantedLocks.containsKey(lockId); + Integer count = grantedLocks.get(lockId); + if (--count == 0) { + grantedLocks.remove(lockId); + lockedNodesMap.get(lockId).forEach(lockedNodes::remove); + lockedNodesMap.remove(lockId); + return true; + } else { + grantedLocks.put(lockId, count); + return false; + } + } + + } } diff --git a/Dream2/src/main/java/dream/locking/LockManagerForwarder.java b/Dream2/src/main/java/dream/locking/LockManagerForwarder.java index cb368b9..d5a04cb 100644 --- a/Dream2/src/main/java/dream/locking/LockManagerForwarder.java +++ b/Dream2/src/main/java/dream/locking/LockManagerForwarder.java @@ -14,44 +14,45 @@ import polimi.reds.broker.routing.PacketForwarder; public class LockManagerForwarder implements PacketForwarder { - private final Logger logger = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME); - private final LockManager lockManager = new LockManager(); + private final Logger logger = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME); + private final LockManager lockManager = new LockManager(); - @Override - public final Collection forwardPacket(String subject, NodeDescriptor sender, Serializable packet, Collection neighbors, Outbox outbox) { - if (subject.equals(LockRequestPacket.subject)) { - assert packet instanceof LockRequestPacket; - final LockRequestPacket reqPkt = (LockRequestPacket) packet; - logger.fine("Received a request packet: " + reqPkt); - processRequestPacket(sender, reqPkt, outbox); - } else if (subject.equals(LockReleasePacket.subject)) { - assert packet instanceof LockReleasePacket; - final LockReleasePacket relPkt = (LockReleasePacket) packet; - logger.finer("Received a release packet: " + relPkt); - processReleasePacket(sender, relPkt, outbox); - } else { - assert false; - logger.warning("Received an unknown packet subject"); - } - return new ArrayList(); - } + @Override + public final Collection forwardPacket(String subject, NodeDescriptor sender, Serializable packet, + Collection neighbors, Outbox outbox) { + if (subject.equals(LockRequestPacket.subject)) { + assert packet instanceof LockRequestPacket; + final LockRequestPacket reqPkt = (LockRequestPacket) packet; + logger.fine("Received a request packet: " + reqPkt); + processRequestPacket(sender, reqPkt, outbox); + } else if (subject.equals(LockReleasePacket.subject)) { + assert packet instanceof LockReleasePacket; + final LockReleasePacket relPkt = (LockReleasePacket) packet; + logger.finer("Received a release packet: " + relPkt); + processReleasePacket(sender, relPkt, outbox); + } else { + assert false; + logger.warning("Received an unknown packet subject"); + } + return new ArrayList(); + } - private final void processRequestPacket(NodeDescriptor sender, LockRequestPacket reqPkt, Outbox outbox) { - final boolean granted = lockManager.processLockRequest(reqPkt); - if (granted) { - final Collection recipients = new ArrayList<>(1); - recipients.add(sender); - outbox.add(LockGrantPacket.subject, new LockGrantPacket(reqPkt), recipients); - } - } + private final void processRequestPacket(NodeDescriptor sender, LockRequestPacket reqPkt, Outbox outbox) { + final boolean granted = lockManager.processLockRequest(reqPkt); + if (granted) { + final Collection recipients = new ArrayList<>(1); + recipients.add(sender); + outbox.add(LockGrantPacket.subject, new LockGrantPacket(reqPkt), recipients); + } + } - private final void processReleasePacket(NodeDescriptor sender, LockReleasePacket relPkt, Outbox outbox) { - final Set granted = lockManager.processLockRelease(relPkt); - granted.forEach(req -> { - final Collection recipients = new ArrayList<>(1); - recipients.add(req.getApplicant()); - outbox.add(LockGrantPacket.subject, new LockGrantPacket(req), recipients); - }); - } + private final void processReleasePacket(NodeDescriptor sender, LockReleasePacket relPkt, Outbox outbox) { + final Set granted = lockManager.processLockRelease(relPkt); + granted.forEach(req -> { + final Collection recipients = new ArrayList<>(1); + recipients.add(req.getApplicant()); + outbox.add(LockGrantPacket.subject, new LockGrantPacket(req), recipients); + }); + } } diff --git a/Dream2/src/main/java/dream/locking/LockManagerLauncher.java b/Dream2/src/main/java/dream/locking/LockManagerLauncher.java index 74c94a2..307cde9 100644 --- a/Dream2/src/main/java/dream/locking/LockManagerLauncher.java +++ b/Dream2/src/main/java/dream/locking/LockManagerLauncher.java @@ -19,54 +19,54 @@ import polimi.reds.broker.routing.GenericRouter; public class LockManagerLauncher implements NeighborhoodChangeListener { - private static LockManagerLauncher launcher; + private static LockManagerLauncher launcher; - private final Logger logger = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME); - private final Overlay overlay; + private final Logger logger = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME); + private final Overlay overlay; - private LockManagerLauncher() { - final Transport tr = new TCPTransport(Consts.lockManagerPort); - final TopologyManager tm = new SimpleTopologyManager(); - overlay = new GenericOverlay(tm, tr); - final GenericRouter router = new GenericRouter(overlay); - final LockManagerForwarder forwarder = new LockManagerForwarder(); - router.setPacketForwarder(LockRequestPacket.subject, forwarder); - router.setPacketForwarder(LockReleasePacket.subject, forwarder); - overlay.addNeighborhoodChangeListener(this); - } + private LockManagerLauncher() { + final Transport tr = new TCPTransport(Consts.lockManagerPort); + final TopologyManager tm = new SimpleTopologyManager(); + overlay = new GenericOverlay(tm, tr); + final GenericRouter router = new GenericRouter(overlay); + final LockManagerForwarder forwarder = new LockManagerForwarder(); + router.setPacketForwarder(LockRequestPacket.subject, forwarder); + router.setPacketForwarder(LockReleasePacket.subject, forwarder); + overlay.addNeighborhoodChangeListener(this); + } - public static final void start() { - if (launcher == null) { - launcher = new LockManagerLauncher(); - } - launcher.logger.info("Starting lock manager"); - launcher.overlay.start(); - } + public static final void start() { + if (launcher == null) { + launcher = new LockManagerLauncher(); + } + launcher.logger.info("Starting lock manager"); + launcher.overlay.start(); + } - public static final void stop() { - if (launcher != null) { - launcher.logger.info("Stopping lock manager"); - launcher.overlay.stop(); - } - } + public static final void stop() { + if (launcher != null) { + launcher.logger.info("Stopping lock manager"); + launcher.overlay.stop(); + } + } - @Override - public void notifyNeighborAdded(NodeDescriptor sender) { - try { - overlay.send(LockManagerHelloPacket.subject, new LockManagerHelloPacket(), sender); - } catch (IOException | NotRunningException e) { - e.printStackTrace(); - } - } + @Override + public void notifyNeighborAdded(NodeDescriptor sender) { + try { + overlay.send(LockManagerHelloPacket.subject, new LockManagerHelloPacket(), sender); + } catch (IOException | NotRunningException e) { + e.printStackTrace(); + } + } - @Override - public void notifyNeighborDead(NodeDescriptor sender) { - // Nothing to do - } + @Override + public void notifyNeighborDead(NodeDescriptor sender) { + // Nothing to do + } - @Override - public void notifyNeighborRemoved(NodeDescriptor sender) { - // Nothing to do - } + @Override + public void notifyNeighborRemoved(NodeDescriptor sender) { + // Nothing to do + } } diff --git a/Dream2/src/main/java/dream/server/AdvertisementTable.java b/Dream2/src/main/java/dream/server/AdvertisementTable.java index 98daa90..660d849 100755 --- a/Dream2/src/main/java/dream/server/AdvertisementTable.java +++ b/Dream2/src/main/java/dream/server/AdvertisementTable.java @@ -13,38 +13,38 @@ import polimi.reds.NodeDescriptor; final class AdvertisementTable { - private final Map> advs = new HashMap>(); - - final void addAdvertisement(NodeDescriptor node, Advertisement adv) { - Collection advsList = advs.get(node); - if (advsList == null) { - advsList = new ArrayList(); - advs.put(node, advsList); - } - advsList.add(adv); - } - - final void removeAdvertisement(NodeDescriptor node, Advertisement adv) { - final Collection advsList = advs.get(node); - if (advsList == null) { - return; - } - advsList.remove(adv); - if (advsList.isEmpty()) { - advs.remove(node); - } - } - - final Set getMatchingNodes(Subscription sub) { - final Predicate isAdvSat = adv -> adv.isSatisfiedBy(sub); - final Predicate hasAdvSat = node -> advs.get(node).stream().anyMatch(isAdvSat); - return advs.keySet().stream().// - filter(hasAdvSat).// - collect(Collectors.toSet()); - } - - final void removeAllAdvertisementsFor(NodeDescriptor node) { - advs.remove(node); - } + private final Map> advs = new HashMap>(); + + final void addAdvertisement(NodeDescriptor node, Advertisement adv) { + Collection advsList = advs.get(node); + if (advsList == null) { + advsList = new ArrayList(); + advs.put(node, advsList); + } + advsList.add(adv); + } + + final void removeAdvertisement(NodeDescriptor node, Advertisement adv) { + final Collection advsList = advs.get(node); + if (advsList == null) { + return; + } + advsList.remove(adv); + if (advsList.isEmpty()) { + advs.remove(node); + } + } + + final Set getMatchingNodes(Subscription sub) { + final Predicate isAdvSat = adv -> adv.isSatisfiedBy(sub); + final Predicate hasAdvSat = node -> advs.get(node).stream().anyMatch(isAdvSat); + return advs.keySet().stream().// + filter(hasAdvSat).// + collect(Collectors.toSet()); + } + + final void removeAllAdvertisementsFor(NodeDescriptor node) { + advs.remove(node); + } } diff --git a/Dream2/src/main/java/dream/server/ServerEventForwarder.java b/Dream2/src/main/java/dream/server/ServerEventForwarder.java index 5f92ac3..73fdd19 100755 --- a/Dream2/src/main/java/dream/server/ServerEventForwarder.java +++ b/Dream2/src/main/java/dream/server/ServerEventForwarder.java @@ -16,126 +16,131 @@ import polimi.reds.broker.routing.PacketForwarder; public class ServerEventForwarder implements PacketForwarder, NeighborhoodChangeListener { - protected final Logger logger = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME); - - protected final SubscriptionTable clientsSubTable = new SubscriptionTable(); - protected final SubscriptionTable brokersSubTable = new SubscriptionTable(); - protected final AdvertisementTable advTable = new AdvertisementTable(); - - @Override - public Collection forwardPacket(String subject, NodeDescriptor sender, Serializable packet, Collection neighbors, Outbox outbox) { - if (subject.equals(SubscriptionPacket.subject)) { - assert packet instanceof SubscriptionPacket; - final SubscriptionPacket subPkt = (SubscriptionPacket) packet; - logger.fine("Received a subscription packet: " + subPkt); - processSubscription(sender, subPkt, neighbors, outbox); - } else if (subject.equals(EventPacket.subject)) { - assert packet instanceof EventPacket; - final EventPacket evPkt = (EventPacket) packet; - logger.finer("Received an event packet: " + evPkt); - processEvent(sender, evPkt, neighbors, outbox); - } else if (subject.equals(AdvertisementPacket.subject)) { - assert packet instanceof AdvertisementPacket; - final AdvertisementPacket advPkt = (AdvertisementPacket) packet; - logger.fine("Received an advertisement packet: " + advPkt); - processAdvertisement(sender, advPkt, neighbors, outbox); - } else { - assert false; - logger.warning("Received an unknown packet subject"); - } - return new ArrayList(); - } - - private final void processSubscription(NodeDescriptor sender, SubscriptionPacket packet, Collection neighbors, Outbox box) { - updateSubscriptionTables(sender, packet); - final Set matchingNodes = advTable.getMatchingNodes(packet.getSubscription()); - if (!matchingNodes.isEmpty()) { - sendTo(SubscriptionPacket.subject, packet, box, matchingNodes); - } - } - - private final void updateSubscriptionTables(NodeDescriptor sender, SubscriptionPacket subPkt) { - final SubscriptionTable table = sender.isClient() ? clientsSubTable : brokersSubTable; - switch (subPkt.getSubType()) { - case SUB: - table.addSubscription(sender, subPkt.getSubscription()); - break; - case UNSUB: - table.removeSubscription(sender, subPkt.getSubscription()); - break; - default: - assert false : subPkt.getSubType(); - } - } - - private void processEvent(NodeDescriptor sender, EventPacket packet, Collection neighbors, Outbox outbox) { - final Map matchingClients = clientsSubTable.getMatchingNodes(packet.getEvent()); - final Map matchingBrokers = brokersSubTable.getMatchingNodes(packet.getEvent()); - sendTo(EventPacket.subject, packet, outbox, matchingClients.keySet()); - sendTo(EventPacket.subject, packet, outbox, matchingBrokers.keySet()); - } - - private final int getSubscribersCount(Map clients) { - int count = 0; - for (final Integer val : clients.values()) { - count += val; - } - return count; - } - - private final void processAdvertisement(NodeDescriptor sender, AdvertisementPacket packet, Collection neighbors, Outbox outbox) { - if (packet.isPublic()) { - switch (packet.getAdvType()) { - case ADV: - advTable.addAdvertisement(sender, packet.getAdvertisement()); - break; - case UNADV: - advTable.removeAdvertisement(sender, packet.getAdvertisement()); - break; - } - } - outbox.add(AdvertisementPacket.subject, packet, getAllNodesExcept(sender, neighbors)); - } - - private final void sendTo(String subject, Serializable packet, Outbox box, Collection recipients) { - box.add(subject, packet, recipients); - } - - private final void sendTo(String subject, Serializable packet, Outbox box, NodeDescriptor recipient) { - final Collection recipients = new ArrayList(1); - recipients.add(recipient); - box.add(subject, packet, recipients); - } - - private final Collection getAllNodesExcept(NodeDescriptor nodeToSkip, Collection neighbors) { - final Collection result = new ArrayList<>(neighbors); - result.remove(nodeToSkip); - return result; - } - - private final void reactToRemovedNeighbor(NodeDescriptor node) { - logger.fine("Removing neighbor"); - if (node.isBroker()) { - brokersSubTable.removeAllSubscriptionsFor(node); - advTable.removeAllAdvertisementsFor(node); - } else { - clientsSubTable.removeAllSubscriptionsFor(node); - } - } - - @Override - public final void notifyNeighborAdded(NodeDescriptor node) { - // Nothing to do - } - - @Override - public final void notifyNeighborDead(NodeDescriptor node) { - reactToRemovedNeighbor(node); - } - - @Override - public final void notifyNeighborRemoved(NodeDescriptor node) { - reactToRemovedNeighbor(node); - } + protected final Logger logger = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME); + + protected final SubscriptionTable clientsSubTable = new SubscriptionTable(); + protected final SubscriptionTable brokersSubTable = new SubscriptionTable(); + protected final AdvertisementTable advTable = new AdvertisementTable(); + + @Override + public Collection forwardPacket(String subject, NodeDescriptor sender, Serializable packet, + Collection neighbors, Outbox outbox) { + if (subject.equals(SubscriptionPacket.subject)) { + assert packet instanceof SubscriptionPacket; + final SubscriptionPacket subPkt = (SubscriptionPacket) packet; + logger.fine("Received a subscription packet: " + subPkt); + processSubscription(sender, subPkt, neighbors, outbox); + } else if (subject.equals(EventPacket.subject)) { + assert packet instanceof EventPacket; + final EventPacket evPkt = (EventPacket) packet; + logger.finer("Received an event packet: " + evPkt); + processEvent(sender, evPkt, neighbors, outbox); + } else if (subject.equals(AdvertisementPacket.subject)) { + assert packet instanceof AdvertisementPacket; + final AdvertisementPacket advPkt = (AdvertisementPacket) packet; + logger.fine("Received an advertisement packet: " + advPkt); + processAdvertisement(sender, advPkt, neighbors, outbox); + } else { + assert false; + logger.warning("Received an unknown packet subject"); + } + return new ArrayList(); + } + + private final void processSubscription(NodeDescriptor sender, SubscriptionPacket packet, + Collection neighbors, Outbox box) { + updateSubscriptionTables(sender, packet); + final Set matchingNodes = advTable.getMatchingNodes(packet.getSubscription()); + if (!matchingNodes.isEmpty()) { + sendTo(SubscriptionPacket.subject, packet, box, matchingNodes); + } + } + + private final void updateSubscriptionTables(NodeDescriptor sender, SubscriptionPacket subPkt) { + final SubscriptionTable table = sender.isClient() ? clientsSubTable : brokersSubTable; + switch (subPkt.getSubType()) { + case SUB: + table.addSubscription(sender, subPkt.getSubscription()); + break; + case UNSUB: + table.removeSubscription(sender, subPkt.getSubscription()); + break; + default: + assert false : subPkt.getSubType(); + } + } + + private void processEvent(NodeDescriptor sender, EventPacket packet, Collection neighbors, + Outbox outbox) { + final Map matchingClients = clientsSubTable.getMatchingNodes(packet.getEvent()); + final Map matchingBrokers = brokersSubTable.getMatchingNodes(packet.getEvent()); + sendTo(EventPacket.subject, packet, outbox, matchingClients.keySet()); + sendTo(EventPacket.subject, packet, outbox, matchingBrokers.keySet()); + } + + private final int getSubscribersCount(Map clients) { + int count = 0; + for (final Integer val : clients.values()) { + count += val; + } + return count; + } + + private final void processAdvertisement(NodeDescriptor sender, AdvertisementPacket packet, + Collection neighbors, Outbox outbox) { + if (packet.isPublic()) { + switch (packet.getAdvType()) { + case ADV: + advTable.addAdvertisement(sender, packet.getAdvertisement()); + break; + case UNADV: + advTable.removeAdvertisement(sender, packet.getAdvertisement()); + break; + } + } + outbox.add(AdvertisementPacket.subject, packet, getAllNodesExcept(sender, neighbors)); + } + + private final void sendTo(String subject, Serializable packet, Outbox box, Collection recipients) { + box.add(subject, packet, recipients); + } + + private final void sendTo(String subject, Serializable packet, Outbox box, NodeDescriptor recipient) { + final Collection recipients = new ArrayList(1); + recipients.add(recipient); + box.add(subject, packet, recipients); + } + + private final Collection getAllNodesExcept(NodeDescriptor nodeToSkip, + Collection neighbors) { + final Collection result = new ArrayList<>(neighbors); + result.remove(nodeToSkip); + return result; + } + + private final void reactToRemovedNeighbor(NodeDescriptor node) { + logger.fine("Removing neighbor"); + if (node.isBroker()) { + brokersSubTable.removeAllSubscriptionsFor(node); + advTable.removeAllAdvertisementsFor(node); + } else { + clientsSubTable.removeAllSubscriptionsFor(node); + } + } + + @Override + public final void notifyNeighborAdded(NodeDescriptor node) { + // Nothing to do + } + + @Override + public final void notifyNeighborDead(NodeDescriptor node) { + reactToRemovedNeighbor(node); + } + + @Override + public final void notifyNeighborRemoved(NodeDescriptor node) { + reactToRemovedNeighbor(node); + } } diff --git a/Dream2/src/main/java/dream/server/ServerLauncher.java b/Dream2/src/main/java/dream/server/ServerLauncher.java index 8cf53c9..bde0757 100755 --- a/Dream2/src/main/java/dream/server/ServerLauncher.java +++ b/Dream2/src/main/java/dream/server/ServerLauncher.java @@ -20,56 +20,56 @@ import polimi.reds.broker.routing.GenericRouter; public class ServerLauncher implements NeighborhoodChangeListener { - private static ServerLauncher launcher; + private static ServerLauncher launcher; - private final Logger logger = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME); - private final Overlay overlay; + private final Logger logger = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME); + private final Overlay overlay; - private ServerLauncher() { - final Transport tr = new TCPTransport(Consts.serverPort); - final TopologyManager tm = new SimpleTopologyManager(); - overlay = new GenericOverlay(tm, tr); - final GenericRouter router = new GenericRouter(overlay); - final ServerEventForwarder forwarder = new ServerEventForwarder(); - overlay.addNeighborhoodChangeListener(forwarder); - router.setPacketForwarder(EventPacket.subject, forwarder); - router.setPacketForwarder(SubscriptionPacket.subject, forwarder); - router.setPacketForwarder(AdvertisementPacket.subject, forwarder); - overlay.addNeighborhoodChangeListener(this); - } + private ServerLauncher() { + final Transport tr = new TCPTransport(Consts.serverPort); + final TopologyManager tm = new SimpleTopologyManager(); + overlay = new GenericOverlay(tm, tr); + final GenericRouter router = new GenericRouter(overlay); + final ServerEventForwarder forwarder = new ServerEventForwarder(); + overlay.addNeighborhoodChangeListener(forwarder); + router.setPacketForwarder(EventPacket.subject, forwarder); + router.setPacketForwarder(SubscriptionPacket.subject, forwarder); + router.setPacketForwarder(AdvertisementPacket.subject, forwarder); + overlay.addNeighborhoodChangeListener(this); + } - public static final void start() { - if (launcher == null) { - launcher = new ServerLauncher(); - } - launcher.logger.info("Starting server"); - launcher.overlay.start(); - } + public static final void start() { + if (launcher == null) { + launcher = new ServerLauncher(); + } + launcher.logger.info("Starting server"); + launcher.overlay.start(); + } - public static final void stop() { - if (launcher != null) { - launcher.logger.info("Stopping server"); - launcher.overlay.stop(); - } - } + public static final void stop() { + if (launcher != null) { + launcher.logger.info("Stopping server"); + launcher.overlay.stop(); + } + } - @Override - public void notifyNeighborAdded(NodeDescriptor sender) { - try { - overlay.send(ServerHelloPacket.subject, new ServerHelloPacket(), sender); - } catch (IOException | NotRunningException e) { - e.printStackTrace(); - } - } + @Override + public void notifyNeighborAdded(NodeDescriptor sender) { + try { + overlay.send(ServerHelloPacket.subject, new ServerHelloPacket(), sender); + } catch (IOException | NotRunningException e) { + e.printStackTrace(); + } + } - @Override - public void notifyNeighborDead(NodeDescriptor sender) { - // Nothing to do - } + @Override + public void notifyNeighborDead(NodeDescriptor sender) { + // Nothing to do + } - @Override - public void notifyNeighborRemoved(NodeDescriptor sender) { - // Nothing to do - } + @Override + public void notifyNeighborRemoved(NodeDescriptor sender) { + // Nothing to do + } } diff --git a/Dream2/src/main/java/dream/server/SubscriptionTable.java b/Dream2/src/main/java/dream/server/SubscriptionTable.java index ce6dbae..5fea54b 100755 --- a/Dream2/src/main/java/dream/server/SubscriptionTable.java +++ b/Dream2/src/main/java/dream/server/SubscriptionTable.java @@ -10,49 +10,50 @@ import polimi.reds.NodeDescriptor; final class SubscriptionTable { - private final Map> subs = new HashMap>(); - - final void addSubscription(NodeDescriptor node, Subscription sub) { - Collection subsList = subs.get(node); - if (subsList == null) { - subsList = new ArrayList(); - subs.put(node, subsList); - } - subsList.add(sub); - } - - final void removeSubscription(NodeDescriptor node, Subscription sub) { - Collection subsList = subs.get(node); - if (subsList == null) return; - subsList.remove(sub); - if (subsList.isEmpty()) { - subs.remove(node); - } - } - - final Map getMatchingNodes(Event ev) { - Map result = new HashMap(); - for (NodeDescriptor node : subs.keySet()) { - int count = 0; - for (Subscription sub : subs.get(node)) { - if (sub.isSatisfiedBy(ev)) { - count++; - } - } - if (count != 0) { - result.put(node, count); - } - } - return result; - } - - final void removeAllSubscriptionsFor(NodeDescriptor node) { - subs.remove(node); - } - - @Override - public String toString() { - return "SubscriptionTable [subs=" + subs + "]"; - } + private final Map> subs = new HashMap>(); + + final void addSubscription(NodeDescriptor node, Subscription sub) { + Collection subsList = subs.get(node); + if (subsList == null) { + subsList = new ArrayList(); + subs.put(node, subsList); + } + subsList.add(sub); + } + + final void removeSubscription(NodeDescriptor node, Subscription sub) { + Collection subsList = subs.get(node); + if (subsList == null) + return; + subsList.remove(sub); + if (subsList.isEmpty()) { + subs.remove(node); + } + } + + final Map getMatchingNodes(Event ev) { + Map result = new HashMap(); + for (NodeDescriptor node : subs.keySet()) { + int count = 0; + for (Subscription sub : subs.get(node)) { + if (sub.isSatisfiedBy(ev)) { + count++; + } + } + if (count != 0) { + result.put(node, count); + } + } + return result; + } + + final void removeAllSubscriptionsFor(NodeDescriptor node) { + subs.remove(node); + } + + @Override + public String toString() { + return "SubscriptionTable [subs=" + subs + "]"; + } } diff --git a/Dream2/src/test/java/dream/LocalTest.java b/Dream2/src/test/java/dream/LocalTest.java index 8d8a13d..bdd6b03 100755 --- a/Dream2/src/test/java/dream/LocalTest.java +++ b/Dream2/src/test/java/dream/LocalTest.java @@ -11,76 +11,80 @@ import dream.server.ServerLauncher; public class LocalTest { - private boolean serverStarted = false; - private final boolean lockManagerStarted = false; + private boolean serverStarted = false; + private final boolean lockManagerStarted = false; - @Test - public void localTest1() { - startServerIfNeeded(); - startTokenServiceIfNeeded(); + @Test + public void localTest1() { + startServerIfNeeded(); + startTokenServiceIfNeeded(); - DependencyGraph.instance.clear(); + DependencyGraph.instance.clear(); - final Var varInt = new Var<>("varInt", Integer.valueOf(1)); - final Var varString1 = new Var<>("varString1", ""); - final Var varString2 = new Var<>("varString2", ""); + final Var varInt = new Var<>("varInt", Integer.valueOf(1)); + final Var varString1 = new Var<>("varString1", ""); + final Var varString2 = new Var<>("varString2", ""); - final Signal signalInt = new Signal("signalInt", () -> 10 - 2 + (varInt.get() * 2 + varInt.get()) / 2, varInt); - final Signal signalString = new Signal("signalString", () -> varString1.get() + varString2.get(), varString1, varString2); - final Signal signalInt2 = new Signal("signalInt2", () -> signalInt.get() * 2, signalInt); + final Signal signalInt = new Signal("signalInt", + () -> 10 - 2 + (varInt.get() * 2 + varInt.get()) / 2, varInt); + final Signal signalString = new Signal("signalString", + () -> varString1.get() + varString2.get(), varString1, varString2); + final Signal signalInt2 = new Signal("signalInt2", () -> signalInt.get() * 2, signalInt); - final Var varStart = new Var<>("varStart", Integer.valueOf(1)); - final Signal signalMid1 = new Signal("signalMid1", () -> varStart.get() * 2, varStart); - final Signal signalMid2 = new Signal("signalMid2", () -> signalMid1.get() * 2, signalMid1); - final Signal signalFinal = new Signal("signalFinal", () -> signalMid1.get() + signalMid2.get(), signalMid1, signalMid2); - final Signal signalFinal2 = new Signal("signalFinal2", () -> signalMid1.get() + varStart.get(), signalMid1, varStart); + final Var varStart = new Var<>("varStart", Integer.valueOf(1)); + final Signal signalMid1 = new Signal("signalMid1", () -> varStart.get() * 2, varStart); + final Signal signalMid2 = new Signal("signalMid2", () -> signalMid1.get() * 2, signalMid1); + final Signal signalFinal = new Signal("signalFinal", + () -> signalMid1.get() + signalMid2.get(), signalMid1, signalMid2); + final Signal signalFinal2 = new Signal("signalFinal2", + () -> signalMid1.get() + varStart.get(), signalMid1, varStart); - try { - Thread.sleep(500); - } catch (final InterruptedException e) { - e.printStackTrace(); - } + try { + Thread.sleep(500); + } catch (final InterruptedException e) { + e.printStackTrace(); + } - varInt.set(100); - varString1.set("Hello "); - varString2.set("World!"); - varStart.set(100); + varInt.set(100); + varString1.set("Hello "); + varString2.set("World!"); + varStart.set(100); - try { - Thread.sleep(500); - } catch (final InterruptedException e) { - e.printStackTrace(); - } + try { + Thread.sleep(500); + } catch (final InterruptedException e) { + e.printStackTrace(); + } - assertEquals(signalInt.get(), Integer.valueOf(158)); - assertEquals(signalString.get(), "Hello World!"); - assertEquals(signalInt2.get(), Integer.valueOf(316)); - assertEquals(signalMid1.get(), Integer.valueOf(200)); - assertEquals(signalMid2.get(), Integer.valueOf(400)); - assertEquals(signalFinal.get(), Integer.valueOf(600)); - assertEquals(signalFinal2.get(), Integer.valueOf(300)); - } + assertEquals(signalInt.get(), Integer.valueOf(158)); + assertEquals(signalString.get(), "Hello World!"); + assertEquals(signalInt2.get(), Integer.valueOf(316)); + assertEquals(signalMid1.get(), Integer.valueOf(200)); + assertEquals(signalMid2.get(), Integer.valueOf(400)); + assertEquals(signalFinal.get(), Integer.valueOf(600)); + assertEquals(signalFinal2.get(), Integer.valueOf(300)); + } - private final void startServerIfNeeded() { - if (!serverStarted) { - ServerLauncher.start(); - serverStarted = true; - } - try { - Thread.sleep(500); - } catch (final InterruptedException e) { - e.printStackTrace(); - } - } + private final void startServerIfNeeded() { + if (!serverStarted) { + ServerLauncher.start(); + serverStarted = true; + } + try { + Thread.sleep(500); + } catch (final InterruptedException e) { + e.printStackTrace(); + } + } - private final void startTokenServiceIfNeeded() { - if (!lockManagerStarted) { - LockManagerLauncher.start(); - } - try { - Thread.sleep(500); - } catch (final InterruptedException e) { - e.printStackTrace(); - } - } + private final void startTokenServiceIfNeeded() { + if (!lockManagerStarted) { + LockManagerLauncher.start(); + } + try { + Thread.sleep(500); + } catch (final InterruptedException e) { + e.printStackTrace(); + } + } } diff --git a/Dream2/src/test/java/dream/RegressionTests.java b/Dream2/src/test/java/dream/RegressionTests.java index 382b386..e4cbb25 100755 --- a/Dream2/src/test/java/dream/RegressionTests.java +++ b/Dream2/src/test/java/dream/RegressionTests.java @@ -12,13 +12,13 @@ @RunWith(Suite.class) @Suite.SuiteClasses({ // - IntraSourceDependencyDetectorTest.class, // - DependencyGraphUtilsTest.class, // - CompleteGlitchFreeDependencyDetectorTest.class, // - AtomicDependencyDetectorTest.class, // - FinalNodesDetectorTest.class, // - LockManagerTest.class, // - LocalTest.class }) + IntraSourceDependencyDetectorTest.class, // + DependencyGraphUtilsTest.class, // + CompleteGlitchFreeDependencyDetectorTest.class, // + AtomicDependencyDetectorTest.class, // + FinalNodesDetectorTest.class, // + LockManagerTest.class, // + LocalTest.class }) public class RegressionTests { diff --git a/Dream2/src/test/java/dream/common/utils/AtomicDependencyDetectorTest.java b/Dream2/src/test/java/dream/common/utils/AtomicDependencyDetectorTest.java index cbc8aec..de240b7 100644 --- a/Dream2/src/test/java/dream/common/utils/AtomicDependencyDetectorTest.java +++ b/Dream2/src/test/java/dream/common/utils/AtomicDependencyDetectorTest.java @@ -15,98 +15,98 @@ public class AtomicDependencyDetectorTest { - @Test - public void test1() { - final DependencyGraph graph = DependencyGraph.instance; - graph.clear(); - final AtomicDependencyDetector depDetector = new AtomicDependencyDetector(); - - graph.processAdv(new Advertisement("host", "A")); - depDetector.consolidate(); - assertEquals(1, depDetector.getNodesToLockFor("A@host").size()); - assertTrue(depDetector.getNodesToLockFor("A@host").contains("A@host")); - - final Set subsB = new HashSet<>(); - subsB.add(new Subscription("host", "A")); - graph.processAdv(new Advertisement("host", "B"), subsB); - depDetector.consolidate(); - assertEquals(2, depDetector.getNodesToLockFor("A@host").size()); - assertTrue(depDetector.getNodesToLockFor("A@host").contains("A@host")); - assertTrue(depDetector.getNodesToLockFor("A@host").contains("B@host")); - - final Set subsC = new HashSet<>(); - subsC.add(new Subscription("host", "A")); - graph.processAdv(new Advertisement("host", "C"), subsC); - depDetector.consolidate(); - assertEquals(3, depDetector.getNodesToLockFor("A@host").size()); - assertTrue(depDetector.getNodesToLockFor("A@host").contains("A@host")); - assertTrue(depDetector.getNodesToLockFor("A@host").contains("B@host")); - assertTrue(depDetector.getNodesToLockFor("A@host").contains("C@host")); - - final Set subsD = new HashSet<>(); - subsD.add(new Subscription("host", "B")); - subsD.add(new Subscription("host", "C")); - graph.processAdv(new Advertisement("host", "D"), subsD); - depDetector.consolidate(); - assertEquals(4, depDetector.getNodesToLockFor("A@host").size()); - assertTrue(depDetector.getNodesToLockFor("A@host").contains("A@host")); - assertTrue(depDetector.getNodesToLockFor("A@host").contains("B@host")); - assertTrue(depDetector.getNodesToLockFor("A@host").contains("C@host")); - assertTrue(depDetector.getNodesToLockFor("A@host").contains("D@host")); - } - - @Test - public void test2() { - final DependencyGraph graph = DependencyGraph.instance; - graph.clear(); - final AtomicDependencyDetector depDetector = new AtomicDependencyDetector(); - - graph.processAdv(new Advertisement("host", "A1")); - graph.processAdv(new Advertisement("host", "A2")); - depDetector.consolidate(); - assertEquals(1, depDetector.getNodesToLockFor("A1@host").size()); - assertEquals(1, depDetector.getNodesToLockFor("A2@host").size()); - assertTrue(depDetector.getNodesToLockFor("A1@host").contains("A1@host")); - assertTrue(depDetector.getNodesToLockFor("A2@host").contains("A2@host")); - - final Set subsB = new HashSet<>(); - subsB.add(new Subscription("host", "A1")); - subsB.add(new Subscription("host", "A2")); - graph.processAdv(new Advertisement("host", "B"), subsB); - depDetector.consolidate(); - assertEquals(2, depDetector.getNodesToLockFor("A1@host").size()); - assertEquals(2, depDetector.getNodesToLockFor("A2@host").size()); - assertTrue(depDetector.getNodesToLockFor("A1@host").contains("A1@host")); - assertTrue(depDetector.getNodesToLockFor("A2@host").contains("A2@host")); - assertTrue(depDetector.getNodesToLockFor("A1@host").contains("B@host")); - assertTrue(depDetector.getNodesToLockFor("A2@host").contains("B@host")); - - final Set subsC = new HashSet<>(); - subsC.add(new Subscription("host", "A1")); - graph.processAdv(new Advertisement("host", "C"), subsC); - depDetector.consolidate(); - assertEquals(3, depDetector.getNodesToLockFor("A1@host").size()); - assertEquals(2, depDetector.getNodesToLockFor("A2@host").size()); - assertTrue(depDetector.getNodesToLockFor("A1@host").contains("A1@host")); - assertTrue(depDetector.getNodesToLockFor("A2@host").contains("A2@host")); - assertTrue(depDetector.getNodesToLockFor("A1@host").contains("B@host")); - assertTrue(depDetector.getNodesToLockFor("A2@host").contains("B@host")); - assertTrue(depDetector.getNodesToLockFor("A1@host").contains("C@host")); - - final Set subsD = new HashSet<>(); - subsD.add(new Subscription("host", "B")); - subsD.add(new Subscription("host", "C")); - graph.processAdv(new Advertisement("host", "D"), subsD); - depDetector.consolidate(); - assertEquals(4, depDetector.getNodesToLockFor("A1@host").size()); - assertEquals(3, depDetector.getNodesToLockFor("A2@host").size()); - assertTrue(depDetector.getNodesToLockFor("A1@host").contains("A1@host")); - assertTrue(depDetector.getNodesToLockFor("A2@host").contains("A2@host")); - assertTrue(depDetector.getNodesToLockFor("A1@host").contains("B@host")); - assertTrue(depDetector.getNodesToLockFor("A2@host").contains("B@host")); - assertTrue(depDetector.getNodesToLockFor("A1@host").contains("C@host")); - assertTrue(depDetector.getNodesToLockFor("A1@host").contains("D@host")); - assertTrue(depDetector.getNodesToLockFor("A2@host").contains("D@host")); - } + @Test + public void test1() { + final DependencyGraph graph = DependencyGraph.instance; + graph.clear(); + final AtomicDependencyDetector depDetector = new AtomicDependencyDetector(); + + graph.processAdv(new Advertisement("host", "A")); + depDetector.consolidate(); + assertEquals(1, depDetector.getNodesToLockFor("A@host").size()); + assertTrue(depDetector.getNodesToLockFor("A@host").contains("A@host")); + + final Set subsB = new HashSet<>(); + subsB.add(new Subscription("host", "A")); + graph.processAdv(new Advertisement("host", "B"), subsB); + depDetector.consolidate(); + assertEquals(2, depDetector.getNodesToLockFor("A@host").size()); + assertTrue(depDetector.getNodesToLockFor("A@host").contains("A@host")); + assertTrue(depDetector.getNodesToLockFor("A@host").contains("B@host")); + + final Set subsC = new HashSet<>(); + subsC.add(new Subscription("host", "A")); + graph.processAdv(new Advertisement("host", "C"), subsC); + depDetector.consolidate(); + assertEquals(3, depDetector.getNodesToLockFor("A@host").size()); + assertTrue(depDetector.getNodesToLockFor("A@host").contains("A@host")); + assertTrue(depDetector.getNodesToLockFor("A@host").contains("B@host")); + assertTrue(depDetector.getNodesToLockFor("A@host").contains("C@host")); + + final Set subsD = new HashSet<>(); + subsD.add(new Subscription("host", "B")); + subsD.add(new Subscription("host", "C")); + graph.processAdv(new Advertisement("host", "D"), subsD); + depDetector.consolidate(); + assertEquals(4, depDetector.getNodesToLockFor("A@host").size()); + assertTrue(depDetector.getNodesToLockFor("A@host").contains("A@host")); + assertTrue(depDetector.getNodesToLockFor("A@host").contains("B@host")); + assertTrue(depDetector.getNodesToLockFor("A@host").contains("C@host")); + assertTrue(depDetector.getNodesToLockFor("A@host").contains("D@host")); + } + + @Test + public void test2() { + final DependencyGraph graph = DependencyGraph.instance; + graph.clear(); + final AtomicDependencyDetector depDetector = new AtomicDependencyDetector(); + + graph.processAdv(new Advertisement("host", "A1")); + graph.processAdv(new Advertisement("host", "A2")); + depDetector.consolidate(); + assertEquals(1, depDetector.getNodesToLockFor("A1@host").size()); + assertEquals(1, depDetector.getNodesToLockFor("A2@host").size()); + assertTrue(depDetector.getNodesToLockFor("A1@host").contains("A1@host")); + assertTrue(depDetector.getNodesToLockFor("A2@host").contains("A2@host")); + + final Set subsB = new HashSet<>(); + subsB.add(new Subscription("host", "A1")); + subsB.add(new Subscription("host", "A2")); + graph.processAdv(new Advertisement("host", "B"), subsB); + depDetector.consolidate(); + assertEquals(2, depDetector.getNodesToLockFor("A1@host").size()); + assertEquals(2, depDetector.getNodesToLockFor("A2@host").size()); + assertTrue(depDetector.getNodesToLockFor("A1@host").contains("A1@host")); + assertTrue(depDetector.getNodesToLockFor("A2@host").contains("A2@host")); + assertTrue(depDetector.getNodesToLockFor("A1@host").contains("B@host")); + assertTrue(depDetector.getNodesToLockFor("A2@host").contains("B@host")); + + final Set subsC = new HashSet<>(); + subsC.add(new Subscription("host", "A1")); + graph.processAdv(new Advertisement("host", "C"), subsC); + depDetector.consolidate(); + assertEquals(3, depDetector.getNodesToLockFor("A1@host").size()); + assertEquals(2, depDetector.getNodesToLockFor("A2@host").size()); + assertTrue(depDetector.getNodesToLockFor("A1@host").contains("A1@host")); + assertTrue(depDetector.getNodesToLockFor("A2@host").contains("A2@host")); + assertTrue(depDetector.getNodesToLockFor("A1@host").contains("B@host")); + assertTrue(depDetector.getNodesToLockFor("A2@host").contains("B@host")); + assertTrue(depDetector.getNodesToLockFor("A1@host").contains("C@host")); + + final Set subsD = new HashSet<>(); + subsD.add(new Subscription("host", "B")); + subsD.add(new Subscription("host", "C")); + graph.processAdv(new Advertisement("host", "D"), subsD); + depDetector.consolidate(); + assertEquals(4, depDetector.getNodesToLockFor("A1@host").size()); + assertEquals(3, depDetector.getNodesToLockFor("A2@host").size()); + assertTrue(depDetector.getNodesToLockFor("A1@host").contains("A1@host")); + assertTrue(depDetector.getNodesToLockFor("A2@host").contains("A2@host")); + assertTrue(depDetector.getNodesToLockFor("A1@host").contains("B@host")); + assertTrue(depDetector.getNodesToLockFor("A2@host").contains("B@host")); + assertTrue(depDetector.getNodesToLockFor("A1@host").contains("C@host")); + assertTrue(depDetector.getNodesToLockFor("A1@host").contains("D@host")); + assertTrue(depDetector.getNodesToLockFor("A2@host").contains("D@host")); + } } diff --git a/Dream2/src/test/java/dream/common/utils/CompleteGlitchFreeDependencyDetectorTest.java b/Dream2/src/test/java/dream/common/utils/CompleteGlitchFreeDependencyDetectorTest.java index 74ba22d..a366756 100644 --- a/Dream2/src/test/java/dream/common/utils/CompleteGlitchFreeDependencyDetectorTest.java +++ b/Dream2/src/test/java/dream/common/utils/CompleteGlitchFreeDependencyDetectorTest.java @@ -15,140 +15,140 @@ public class CompleteGlitchFreeDependencyDetectorTest { - @Test - public void test1() { - final DependencyGraph graph = DependencyGraph.instance; - graph.clear(); - final CompleteGlitchFreeDependencyDetector depDetector = new CompleteGlitchFreeDependencyDetector(); - - graph.processAdv(new Advertisement("host", "A")); - depDetector.consolidate(); - assertTrue(depDetector.getNodesToLockFor("A@host").isEmpty()); - - final Set subsB = new HashSet<>(); - subsB.add(new Subscription("host", "A")); - graph.processAdv(new Advertisement("host", "B"), subsB); - depDetector.consolidate(); - assertTrue(depDetector.getNodesToLockFor("A@host").isEmpty()); - assertTrue(depDetector.getNodesToLockFor("B@host").isEmpty()); - - final Set subsC = new HashSet<>(); - subsC.add(new Subscription("host", "A")); - graph.processAdv(new Advertisement("host", "C"), subsC); - depDetector.consolidate(); - assertTrue(depDetector.getNodesToLockFor("A@host").isEmpty()); - assertTrue(depDetector.getNodesToLockFor("B@host").isEmpty()); - assertTrue(depDetector.getNodesToLockFor("C@host").isEmpty()); - - final Set subsD = new HashSet<>(); - subsD.add(new Subscription("host", "B")); - subsD.add(new Subscription("host", "C")); - graph.processAdv(new Advertisement("host", "D"), subsD); - depDetector.consolidate(); - assertTrue(depDetector.getNodesToLockFor("A@host").isEmpty()); - assertTrue(depDetector.getNodesToLockFor("B@host").isEmpty()); - assertTrue(depDetector.getNodesToLockFor("C@host").isEmpty()); - assertTrue(depDetector.getNodesToLockFor("D@host").isEmpty()); - } - - @Test - public void test2() { - final DependencyGraph graph = DependencyGraph.instance; - graph.clear(); - final CompleteGlitchFreeDependencyDetector depDetector = new CompleteGlitchFreeDependencyDetector(); - - graph.processAdv(new Advertisement("host", "A1")); - graph.processAdv(new Advertisement("host", "A2")); - depDetector.consolidate(); - assertTrue(depDetector.getNodesToLockFor("A1@host").isEmpty()); - assertTrue(depDetector.getNodesToLockFor("A2@host").isEmpty()); - - final Set subsB = new HashSet<>(); - subsB.add(new Subscription("host", "A1")); - subsB.add(new Subscription("host", "A2")); - graph.processAdv(new Advertisement("host", "B"), subsB); - depDetector.consolidate(); - assertTrue(depDetector.getNodesToLockFor("A1@host").isEmpty()); - assertTrue(depDetector.getNodesToLockFor("A2@host").isEmpty()); - assertTrue(depDetector.getNodesToLockFor("B@host").isEmpty()); - - final Set subsC = new HashSet<>(); - subsC.add(new Subscription("host", "A1")); - graph.processAdv(new Advertisement("host", "C"), subsC); - depDetector.consolidate(); - assertTrue(depDetector.getNodesToLockFor("A1@host").isEmpty()); - assertTrue(depDetector.getNodesToLockFor("A2@host").isEmpty()); - assertTrue(depDetector.getNodesToLockFor("B@host").isEmpty()); - assertTrue(depDetector.getNodesToLockFor("C@host").isEmpty()); - - final Set subsD = new HashSet<>(); - subsD.add(new Subscription("host", "B")); - subsD.add(new Subscription("host", "C")); - graph.processAdv(new Advertisement("host", "D"), subsD); - depDetector.consolidate(); - assertEquals(2, depDetector.getNodesToLockFor("A1@host").size()); - assertTrue(depDetector.getNodesToLockFor("A1@host").contains("B@host")); - assertTrue(depDetector.getNodesToLockFor("A1@host").contains("D@host")); - assertEquals(2, depDetector.getNodesToLockFor("A2@host").size()); - assertTrue(depDetector.getNodesToLockFor("A2@host").contains("B@host")); - assertTrue(depDetector.getNodesToLockFor("A2@host").contains("D@host")); - assertTrue(depDetector.getNodesToLockFor("B@host").isEmpty()); - assertTrue(depDetector.getNodesToLockFor("C@host").isEmpty()); - assertTrue(depDetector.getNodesToLockFor("D@host").isEmpty()); - } - - @Test - public void test3() { - final DependencyGraph graph = DependencyGraph.instance; - graph.clear(); - final CompleteGlitchFreeDependencyDetector depDetector = new CompleteGlitchFreeDependencyDetector(); - - graph.processAdv(new Advertisement("host", "A1")); - graph.processAdv(new Advertisement("host", "A2")); - depDetector.consolidate(); - assertTrue(depDetector.getNodesToLockFor("A1@host").isEmpty()); - assertTrue(depDetector.getNodesToLockFor("A2@host").isEmpty()); - - final Set subsB = new HashSet<>(); - subsB.add(new Subscription("host", "A1")); - subsB.add(new Subscription("host", "A2")); - graph.processAdv(new Advertisement("host", "B"), subsB); - depDetector.consolidate(); - assertTrue(depDetector.getNodesToLockFor("A1@host").isEmpty()); - assertTrue(depDetector.getNodesToLockFor("A2@host").isEmpty()); - assertTrue(depDetector.getNodesToLockFor("B@host").isEmpty()); - - final Set subsC = new HashSet<>(); - subsC.add(new Subscription("host", "A1")); - subsC.add(new Subscription("host", "A2")); - graph.processAdv(new Advertisement("host", "C"), subsC); - depDetector.consolidate(); - assertTrue(depDetector.getNodesToLockFor("B@host").isEmpty()); - assertTrue(depDetector.getNodesToLockFor("C@host").isEmpty()); - assertEquals(2, depDetector.getNodesToLockFor("A1@host").size()); - assertEquals(2, depDetector.getNodesToLockFor("A2@host").size()); - assertTrue(depDetector.getNodesToLockFor("A1@host").contains("B@host")); - assertTrue(depDetector.getNodesToLockFor("A1@host").contains("C@host")); - assertTrue(depDetector.getNodesToLockFor("A2@host").contains("B@host")); - assertTrue(depDetector.getNodesToLockFor("A2@host").contains("C@host")); - - final Set subsD = new HashSet<>(); - subsD.add(new Subscription("host", "B")); - subsD.add(new Subscription("host", "C")); - graph.processAdv(new Advertisement("host", "D"), subsD); - depDetector.consolidate(); - assertTrue(depDetector.getNodesToLockFor("B@host").isEmpty()); - assertTrue(depDetector.getNodesToLockFor("C@host").isEmpty()); - assertTrue(depDetector.getNodesToLockFor("D@host").isEmpty()); - assertEquals(3, depDetector.getNodesToLockFor("A1@host").size()); - assertEquals(3, depDetector.getNodesToLockFor("A2@host").size()); - assertTrue(depDetector.getNodesToLockFor("A1@host").contains("B@host")); - assertTrue(depDetector.getNodesToLockFor("A1@host").contains("C@host")); - assertTrue(depDetector.getNodesToLockFor("A1@host").contains("D@host")); - assertTrue(depDetector.getNodesToLockFor("A2@host").contains("B@host")); - assertTrue(depDetector.getNodesToLockFor("A2@host").contains("C@host")); - assertTrue(depDetector.getNodesToLockFor("A2@host").contains("D@host")); - - } + @Test + public void test1() { + final DependencyGraph graph = DependencyGraph.instance; + graph.clear(); + final CompleteGlitchFreeDependencyDetector depDetector = new CompleteGlitchFreeDependencyDetector(); + + graph.processAdv(new Advertisement("host", "A")); + depDetector.consolidate(); + assertTrue(depDetector.getNodesToLockFor("A@host").isEmpty()); + + final Set subsB = new HashSet<>(); + subsB.add(new Subscription("host", "A")); + graph.processAdv(new Advertisement("host", "B"), subsB); + depDetector.consolidate(); + assertTrue(depDetector.getNodesToLockFor("A@host").isEmpty()); + assertTrue(depDetector.getNodesToLockFor("B@host").isEmpty()); + + final Set subsC = new HashSet<>(); + subsC.add(new Subscription("host", "A")); + graph.processAdv(new Advertisement("host", "C"), subsC); + depDetector.consolidate(); + assertTrue(depDetector.getNodesToLockFor("A@host").isEmpty()); + assertTrue(depDetector.getNodesToLockFor("B@host").isEmpty()); + assertTrue(depDetector.getNodesToLockFor("C@host").isEmpty()); + + final Set subsD = new HashSet<>(); + subsD.add(new Subscription("host", "B")); + subsD.add(new Subscription("host", "C")); + graph.processAdv(new Advertisement("host", "D"), subsD); + depDetector.consolidate(); + assertTrue(depDetector.getNodesToLockFor("A@host").isEmpty()); + assertTrue(depDetector.getNodesToLockFor("B@host").isEmpty()); + assertTrue(depDetector.getNodesToLockFor("C@host").isEmpty()); + assertTrue(depDetector.getNodesToLockFor("D@host").isEmpty()); + } + + @Test + public void test2() { + final DependencyGraph graph = DependencyGraph.instance; + graph.clear(); + final CompleteGlitchFreeDependencyDetector depDetector = new CompleteGlitchFreeDependencyDetector(); + + graph.processAdv(new Advertisement("host", "A1")); + graph.processAdv(new Advertisement("host", "A2")); + depDetector.consolidate(); + assertTrue(depDetector.getNodesToLockFor("A1@host").isEmpty()); + assertTrue(depDetector.getNodesToLockFor("A2@host").isEmpty()); + + final Set subsB = new HashSet<>(); + subsB.add(new Subscription("host", "A1")); + subsB.add(new Subscription("host", "A2")); + graph.processAdv(new Advertisement("host", "B"), subsB); + depDetector.consolidate(); + assertTrue(depDetector.getNodesToLockFor("A1@host").isEmpty()); + assertTrue(depDetector.getNodesToLockFor("A2@host").isEmpty()); + assertTrue(depDetector.getNodesToLockFor("B@host").isEmpty()); + + final Set subsC = new HashSet<>(); + subsC.add(new Subscription("host", "A1")); + graph.processAdv(new Advertisement("host", "C"), subsC); + depDetector.consolidate(); + assertTrue(depDetector.getNodesToLockFor("A1@host").isEmpty()); + assertTrue(depDetector.getNodesToLockFor("A2@host").isEmpty()); + assertTrue(depDetector.getNodesToLockFor("B@host").isEmpty()); + assertTrue(depDetector.getNodesToLockFor("C@host").isEmpty()); + + final Set subsD = new HashSet<>(); + subsD.add(new Subscription("host", "B")); + subsD.add(new Subscription("host", "C")); + graph.processAdv(new Advertisement("host", "D"), subsD); + depDetector.consolidate(); + assertEquals(2, depDetector.getNodesToLockFor("A1@host").size()); + assertTrue(depDetector.getNodesToLockFor("A1@host").contains("B@host")); + assertTrue(depDetector.getNodesToLockFor("A1@host").contains("D@host")); + assertEquals(2, depDetector.getNodesToLockFor("A2@host").size()); + assertTrue(depDetector.getNodesToLockFor("A2@host").contains("B@host")); + assertTrue(depDetector.getNodesToLockFor("A2@host").contains("D@host")); + assertTrue(depDetector.getNodesToLockFor("B@host").isEmpty()); + assertTrue(depDetector.getNodesToLockFor("C@host").isEmpty()); + assertTrue(depDetector.getNodesToLockFor("D@host").isEmpty()); + } + + @Test + public void test3() { + final DependencyGraph graph = DependencyGraph.instance; + graph.clear(); + final CompleteGlitchFreeDependencyDetector depDetector = new CompleteGlitchFreeDependencyDetector(); + + graph.processAdv(new Advertisement("host", "A1")); + graph.processAdv(new Advertisement("host", "A2")); + depDetector.consolidate(); + assertTrue(depDetector.getNodesToLockFor("A1@host").isEmpty()); + assertTrue(depDetector.getNodesToLockFor("A2@host").isEmpty()); + + final Set subsB = new HashSet<>(); + subsB.add(new Subscription("host", "A1")); + subsB.add(new Subscription("host", "A2")); + graph.processAdv(new Advertisement("host", "B"), subsB); + depDetector.consolidate(); + assertTrue(depDetector.getNodesToLockFor("A1@host").isEmpty()); + assertTrue(depDetector.getNodesToLockFor("A2@host").isEmpty()); + assertTrue(depDetector.getNodesToLockFor("B@host").isEmpty()); + + final Set subsC = new HashSet<>(); + subsC.add(new Subscription("host", "A1")); + subsC.add(new Subscription("host", "A2")); + graph.processAdv(new Advertisement("host", "C"), subsC); + depDetector.consolidate(); + assertTrue(depDetector.getNodesToLockFor("B@host").isEmpty()); + assertTrue(depDetector.getNodesToLockFor("C@host").isEmpty()); + assertEquals(2, depDetector.getNodesToLockFor("A1@host").size()); + assertEquals(2, depDetector.getNodesToLockFor("A2@host").size()); + assertTrue(depDetector.getNodesToLockFor("A1@host").contains("B@host")); + assertTrue(depDetector.getNodesToLockFor("A1@host").contains("C@host")); + assertTrue(depDetector.getNodesToLockFor("A2@host").contains("B@host")); + assertTrue(depDetector.getNodesToLockFor("A2@host").contains("C@host")); + + final Set subsD = new HashSet<>(); + subsD.add(new Subscription("host", "B")); + subsD.add(new Subscription("host", "C")); + graph.processAdv(new Advertisement("host", "D"), subsD); + depDetector.consolidate(); + assertTrue(depDetector.getNodesToLockFor("B@host").isEmpty()); + assertTrue(depDetector.getNodesToLockFor("C@host").isEmpty()); + assertTrue(depDetector.getNodesToLockFor("D@host").isEmpty()); + assertEquals(3, depDetector.getNodesToLockFor("A1@host").size()); + assertEquals(3, depDetector.getNodesToLockFor("A2@host").size()); + assertTrue(depDetector.getNodesToLockFor("A1@host").contains("B@host")); + assertTrue(depDetector.getNodesToLockFor("A1@host").contains("C@host")); + assertTrue(depDetector.getNodesToLockFor("A1@host").contains("D@host")); + assertTrue(depDetector.getNodesToLockFor("A2@host").contains("B@host")); + assertTrue(depDetector.getNodesToLockFor("A2@host").contains("C@host")); + assertTrue(depDetector.getNodesToLockFor("A2@host").contains("D@host")); + + } } diff --git a/Dream2/src/test/java/dream/common/utils/DependencyGraphUtilsTest.java b/Dream2/src/test/java/dream/common/utils/DependencyGraphUtilsTest.java index 0e503aa..643f71b 100644 --- a/Dream2/src/test/java/dream/common/utils/DependencyGraphUtilsTest.java +++ b/Dream2/src/test/java/dream/common/utils/DependencyGraphUtilsTest.java @@ -16,224 +16,224 @@ public class DependencyGraphUtilsTest { - @Test - public void test1() { - final DependencyGraph graph = DependencyGraph.instance; - graph.clear(); - - assertTrue(DependencyGraphUtils.computeRelevantSources().isEmpty()); - assertTrue(DependencyGraphUtils.computeDependencyClosure().isEmpty()); - - graph.processAdv(new Advertisement("host", "A")); - - Map> relevantSources = DependencyGraphUtils.computeRelevantSources(); - assertEquals(1, relevantSources.size()); - assertTrue(relevantSources.containsKey("A@host")); - assertTrue(relevantSources.get("A@host").contains("A@host")); - - Map> dependencyClosure = DependencyGraphUtils.computeDependencyClosure(); - assertEquals(1, dependencyClosure.size()); - assertTrue(dependencyClosure.containsKey("A@host")); - assertEquals(1, dependencyClosure.get("A@host").size()); - assertTrue(dependencyClosure.get("A@host").contains("A@host")); - - final Set subsB = new HashSet<>(); - subsB.add(new Subscription("host", "A")); - graph.processAdv(new Advertisement("host", "B"), subsB); - - relevantSources = DependencyGraphUtils.computeRelevantSources(); - assertEquals(2, relevantSources.size()); - assertTrue(relevantSources.containsKey("A@host")); - assertEquals(1, relevantSources.get("A@host").size()); - assertTrue(relevantSources.get("A@host").contains("A@host")); - assertTrue(relevantSources.containsKey("B@host")); - assertEquals(1, relevantSources.get("B@host").size()); - assertTrue(relevantSources.get("B@host").contains("A@host")); - - dependencyClosure = DependencyGraphUtils.computeDependencyClosure(); - assertEquals(1, dependencyClosure.size()); - assertTrue(dependencyClosure.containsKey("A@host")); - assertEquals(2, dependencyClosure.get("A@host").size()); - assertTrue(dependencyClosure.get("A@host").contains("A@host")); - assertTrue(dependencyClosure.get("A@host").contains("B@host")); - - final Set subsC = new HashSet<>(); - subsC.add(new Subscription("host", "A")); - graph.processAdv(new Advertisement("host", "C"), subsC); - - relevantSources = DependencyGraphUtils.computeRelevantSources(); - assertEquals(3, relevantSources.size()); - assertTrue(relevantSources.containsKey("A@host")); - assertTrue(relevantSources.containsKey("B@host")); - assertTrue(relevantSources.containsKey("C@host")); - assertEquals(1, relevantSources.get("A@host").size()); - assertEquals(1, relevantSources.get("B@host").size()); - assertEquals(1, relevantSources.get("C@host").size()); - assertTrue(relevantSources.get("B@host").contains("A@host")); - assertTrue(relevantSources.get("C@host").contains("A@host")); - - dependencyClosure = DependencyGraphUtils.computeDependencyClosure(); - assertEquals(1, dependencyClosure.size()); - assertTrue(dependencyClosure.containsKey("A@host")); - assertEquals(3, dependencyClosure.get("A@host").size()); - assertTrue(dependencyClosure.get("A@host").contains("A@host")); - assertTrue(dependencyClosure.get("A@host").contains("B@host")); - assertTrue(dependencyClosure.get("A@host").contains("C@host")); - - final Set subsD = new HashSet<>(); - subsD.add(new Subscription("host", "B")); - subsD.add(new Subscription("host", "C")); - graph.processAdv(new Advertisement("host", "D"), subsD); - - relevantSources = DependencyGraphUtils.computeRelevantSources(); - assertEquals(4, relevantSources.size()); - assertTrue(relevantSources.containsKey("A@host")); - assertTrue(relevantSources.containsKey("B@host")); - assertTrue(relevantSources.containsKey("C@host")); - assertTrue(relevantSources.containsKey("D@host")); - assertEquals(1, relevantSources.get("A@host").size()); - assertEquals(1, relevantSources.get("B@host").size()); - assertEquals(1, relevantSources.get("C@host").size()); - assertEquals(1, relevantSources.get("D@host").size()); - assertTrue(relevantSources.get("A@host").contains("A@host")); - assertTrue(relevantSources.get("B@host").contains("A@host")); - assertTrue(relevantSources.get("C@host").contains("A@host")); - assertTrue(relevantSources.get("D@host").contains("A@host")); - - dependencyClosure = DependencyGraphUtils.computeDependencyClosure(); - assertEquals(1, dependencyClosure.size()); - assertTrue(dependencyClosure.containsKey("A@host")); - assertEquals(4, dependencyClosure.get("A@host").size()); - assertTrue(dependencyClosure.get("A@host").contains("A@host")); - assertTrue(dependencyClosure.get("A@host").contains("B@host")); - assertTrue(dependencyClosure.get("A@host").contains("C@host")); - assertTrue(dependencyClosure.get("A@host").contains("D@host")); - - } - - @Test - public void test2() { - final DependencyGraph graph = DependencyGraph.instance; - graph.clear(); - - assertTrue(DependencyGraphUtils.computeRelevantSources().isEmpty()); - assertTrue(DependencyGraphUtils.computeDependencyClosure().isEmpty()); - - graph.processAdv(new Advertisement("host", "A1")); - graph.processAdv(new Advertisement("host", "A2")); - - Map> relevantSources = DependencyGraphUtils.computeRelevantSources(); - assertEquals(2, relevantSources.size()); - assertTrue(relevantSources.containsKey("A1@host")); - assertTrue(relevantSources.get("A1@host").contains("A1@host")); - assertTrue(relevantSources.containsKey("A2@host")); - assertTrue(relevantSources.get("A2@host").contains("A2@host")); - - Map> dependencyClosure = DependencyGraphUtils.computeDependencyClosure(); - assertEquals(2, dependencyClosure.size()); - assertTrue(dependencyClosure.containsKey("A1@host")); - assertEquals(1, dependencyClosure.get("A1@host").size()); - assertTrue(dependencyClosure.get("A1@host").contains("A1@host")); - assertTrue(dependencyClosure.containsKey("A2@host")); - assertEquals(1, dependencyClosure.get("A2@host").size()); - assertTrue(dependencyClosure.get("A2@host").contains("A2@host")); - - final Set subsB = new HashSet<>(); - subsB.add(new Subscription("host", "A1")); - subsB.add(new Subscription("host", "A2")); - graph.processAdv(new Advertisement("host", "B"), subsB); - - relevantSources = DependencyGraphUtils.computeRelevantSources(); - assertEquals(3, relevantSources.size()); - assertTrue(relevantSources.containsKey("A1@host")); - assertEquals(1, relevantSources.get("A1@host").size()); - assertTrue(relevantSources.get("A1@host").contains("A1@host")); - assertTrue(relevantSources.containsKey("A2@host")); - assertEquals(1, relevantSources.get("A2@host").size()); - assertTrue(relevantSources.get("A2@host").contains("A2@host")); - assertTrue(relevantSources.containsKey("B@host")); - assertEquals(2, relevantSources.get("B@host").size()); - assertTrue(relevantSources.get("B@host").contains("A1@host")); - assertTrue(relevantSources.get("B@host").contains("A2@host")); - - dependencyClosure = DependencyGraphUtils.computeDependencyClosure(); - assertEquals(2, dependencyClosure.size()); - assertTrue(dependencyClosure.containsKey("A1@host")); - assertEquals(2, dependencyClosure.get("A1@host").size()); - assertTrue(dependencyClosure.get("A1@host").contains("A1@host")); - assertTrue(dependencyClosure.get("A1@host").contains("B@host")); - assertTrue(dependencyClosure.containsKey("A2@host")); - assertEquals(2, dependencyClosure.get("A2@host").size()); - assertTrue(dependencyClosure.get("A2@host").contains("A2@host")); - assertTrue(dependencyClosure.get("A2@host").contains("B@host")); - - final Set subsC = new HashSet<>(); - subsC.add(new Subscription("host", "A1")); - graph.processAdv(new Advertisement("host", "C"), subsC); - - relevantSources = DependencyGraphUtils.computeRelevantSources(); - assertEquals(4, relevantSources.size()); - assertTrue(relevantSources.containsKey("A1@host")); - assertTrue(relevantSources.containsKey("A2@host")); - assertTrue(relevantSources.containsKey("B@host")); - assertTrue(relevantSources.containsKey("C@host")); - assertEquals(1, relevantSources.get("A1@host").size()); - assertEquals(1, relevantSources.get("A2@host").size()); - assertEquals(2, relevantSources.get("B@host").size()); - assertEquals(1, relevantSources.get("C@host").size()); - assertTrue(relevantSources.get("B@host").contains("A1@host")); - assertTrue(relevantSources.get("B@host").contains("A2@host")); - assertTrue(relevantSources.get("C@host").contains("A1@host")); - - dependencyClosure = DependencyGraphUtils.computeDependencyClosure(); - assertEquals(2, dependencyClosure.size()); - assertTrue(dependencyClosure.containsKey("A1@host")); - assertTrue(dependencyClosure.containsKey("A2@host")); - assertEquals(3, dependencyClosure.get("A1@host").size()); - assertTrue(dependencyClosure.get("A1@host").contains("A1@host")); - assertTrue(dependencyClosure.get("A1@host").contains("B@host")); - assertTrue(dependencyClosure.get("A1@host").contains("C@host")); - assertEquals(2, dependencyClosure.get("A2@host").size()); - assertTrue(dependencyClosure.get("A2@host").contains("A2@host")); - assertTrue(dependencyClosure.get("A2@host").contains("B@host")); - - final Set subsD = new HashSet<>(); - subsD.add(new Subscription("host", "B")); - subsD.add(new Subscription("host", "C")); - graph.processAdv(new Advertisement("host", "D"), subsD); - - relevantSources = DependencyGraphUtils.computeRelevantSources(); - assertEquals(5, relevantSources.size()); - assertTrue(relevantSources.containsKey("A1@host")); - assertTrue(relevantSources.containsKey("A2@host")); - assertTrue(relevantSources.containsKey("B@host")); - assertTrue(relevantSources.containsKey("C@host")); - assertTrue(relevantSources.containsKey("D@host")); - assertEquals(1, relevantSources.get("A1@host").size()); - assertEquals(1, relevantSources.get("A2@host").size()); - assertEquals(2, relevantSources.get("B@host").size()); - assertEquals(1, relevantSources.get("C@host").size()); - assertEquals(2, relevantSources.get("D@host").size()); - assertTrue(relevantSources.get("A1@host").contains("A1@host")); - assertTrue(relevantSources.get("B@host").contains("A1@host")); - assertTrue(relevantSources.get("B@host").contains("A2@host")); - assertTrue(relevantSources.get("C@host").contains("A1@host")); - assertTrue(relevantSources.get("D@host").contains("A1@host")); - - dependencyClosure = DependencyGraphUtils.computeDependencyClosure(); - assertEquals(2, dependencyClosure.size()); - assertTrue(dependencyClosure.containsKey("A1@host")); - assertTrue(dependencyClosure.containsKey("A2@host")); - assertEquals(4, dependencyClosure.get("A1@host").size()); - assertTrue(dependencyClosure.get("A1@host").contains("A1@host")); - assertTrue(dependencyClosure.get("A1@host").contains("B@host")); - assertTrue(dependencyClosure.get("A1@host").contains("C@host")); - assertTrue(dependencyClosure.get("A1@host").contains("D@host")); - assertEquals(3, dependencyClosure.get("A2@host").size()); - assertTrue(dependencyClosure.get("A2@host").contains("A2@host")); - assertTrue(dependencyClosure.get("A2@host").contains("B@host")); - assertTrue(dependencyClosure.get("A2@host").contains("D@host")); - - } + @Test + public void test1() { + final DependencyGraph graph = DependencyGraph.instance; + graph.clear(); + + assertTrue(DependencyGraphUtils.computeRelevantSources().isEmpty()); + assertTrue(DependencyGraphUtils.computeDependencyClosure().isEmpty()); + + graph.processAdv(new Advertisement("host", "A")); + + Map> relevantSources = DependencyGraphUtils.computeRelevantSources(); + assertEquals(1, relevantSources.size()); + assertTrue(relevantSources.containsKey("A@host")); + assertTrue(relevantSources.get("A@host").contains("A@host")); + + Map> dependencyClosure = DependencyGraphUtils.computeDependencyClosure(); + assertEquals(1, dependencyClosure.size()); + assertTrue(dependencyClosure.containsKey("A@host")); + assertEquals(1, dependencyClosure.get("A@host").size()); + assertTrue(dependencyClosure.get("A@host").contains("A@host")); + + final Set subsB = new HashSet<>(); + subsB.add(new Subscription("host", "A")); + graph.processAdv(new Advertisement("host", "B"), subsB); + + relevantSources = DependencyGraphUtils.computeRelevantSources(); + assertEquals(2, relevantSources.size()); + assertTrue(relevantSources.containsKey("A@host")); + assertEquals(1, relevantSources.get("A@host").size()); + assertTrue(relevantSources.get("A@host").contains("A@host")); + assertTrue(relevantSources.containsKey("B@host")); + assertEquals(1, relevantSources.get("B@host").size()); + assertTrue(relevantSources.get("B@host").contains("A@host")); + + dependencyClosure = DependencyGraphUtils.computeDependencyClosure(); + assertEquals(1, dependencyClosure.size()); + assertTrue(dependencyClosure.containsKey("A@host")); + assertEquals(2, dependencyClosure.get("A@host").size()); + assertTrue(dependencyClosure.get("A@host").contains("A@host")); + assertTrue(dependencyClosure.get("A@host").contains("B@host")); + + final Set subsC = new HashSet<>(); + subsC.add(new Subscription("host", "A")); + graph.processAdv(new Advertisement("host", "C"), subsC); + + relevantSources = DependencyGraphUtils.computeRelevantSources(); + assertEquals(3, relevantSources.size()); + assertTrue(relevantSources.containsKey("A@host")); + assertTrue(relevantSources.containsKey("B@host")); + assertTrue(relevantSources.containsKey("C@host")); + assertEquals(1, relevantSources.get("A@host").size()); + assertEquals(1, relevantSources.get("B@host").size()); + assertEquals(1, relevantSources.get("C@host").size()); + assertTrue(relevantSources.get("B@host").contains("A@host")); + assertTrue(relevantSources.get("C@host").contains("A@host")); + + dependencyClosure = DependencyGraphUtils.computeDependencyClosure(); + assertEquals(1, dependencyClosure.size()); + assertTrue(dependencyClosure.containsKey("A@host")); + assertEquals(3, dependencyClosure.get("A@host").size()); + assertTrue(dependencyClosure.get("A@host").contains("A@host")); + assertTrue(dependencyClosure.get("A@host").contains("B@host")); + assertTrue(dependencyClosure.get("A@host").contains("C@host")); + + final Set subsD = new HashSet<>(); + subsD.add(new Subscription("host", "B")); + subsD.add(new Subscription("host", "C")); + graph.processAdv(new Advertisement("host", "D"), subsD); + + relevantSources = DependencyGraphUtils.computeRelevantSources(); + assertEquals(4, relevantSources.size()); + assertTrue(relevantSources.containsKey("A@host")); + assertTrue(relevantSources.containsKey("B@host")); + assertTrue(relevantSources.containsKey("C@host")); + assertTrue(relevantSources.containsKey("D@host")); + assertEquals(1, relevantSources.get("A@host").size()); + assertEquals(1, relevantSources.get("B@host").size()); + assertEquals(1, relevantSources.get("C@host").size()); + assertEquals(1, relevantSources.get("D@host").size()); + assertTrue(relevantSources.get("A@host").contains("A@host")); + assertTrue(relevantSources.get("B@host").contains("A@host")); + assertTrue(relevantSources.get("C@host").contains("A@host")); + assertTrue(relevantSources.get("D@host").contains("A@host")); + + dependencyClosure = DependencyGraphUtils.computeDependencyClosure(); + assertEquals(1, dependencyClosure.size()); + assertTrue(dependencyClosure.containsKey("A@host")); + assertEquals(4, dependencyClosure.get("A@host").size()); + assertTrue(dependencyClosure.get("A@host").contains("A@host")); + assertTrue(dependencyClosure.get("A@host").contains("B@host")); + assertTrue(dependencyClosure.get("A@host").contains("C@host")); + assertTrue(dependencyClosure.get("A@host").contains("D@host")); + + } + + @Test + public void test2() { + final DependencyGraph graph = DependencyGraph.instance; + graph.clear(); + + assertTrue(DependencyGraphUtils.computeRelevantSources().isEmpty()); + assertTrue(DependencyGraphUtils.computeDependencyClosure().isEmpty()); + + graph.processAdv(new Advertisement("host", "A1")); + graph.processAdv(new Advertisement("host", "A2")); + + Map> relevantSources = DependencyGraphUtils.computeRelevantSources(); + assertEquals(2, relevantSources.size()); + assertTrue(relevantSources.containsKey("A1@host")); + assertTrue(relevantSources.get("A1@host").contains("A1@host")); + assertTrue(relevantSources.containsKey("A2@host")); + assertTrue(relevantSources.get("A2@host").contains("A2@host")); + + Map> dependencyClosure = DependencyGraphUtils.computeDependencyClosure(); + assertEquals(2, dependencyClosure.size()); + assertTrue(dependencyClosure.containsKey("A1@host")); + assertEquals(1, dependencyClosure.get("A1@host").size()); + assertTrue(dependencyClosure.get("A1@host").contains("A1@host")); + assertTrue(dependencyClosure.containsKey("A2@host")); + assertEquals(1, dependencyClosure.get("A2@host").size()); + assertTrue(dependencyClosure.get("A2@host").contains("A2@host")); + + final Set subsB = new HashSet<>(); + subsB.add(new Subscription("host", "A1")); + subsB.add(new Subscription("host", "A2")); + graph.processAdv(new Advertisement("host", "B"), subsB); + + relevantSources = DependencyGraphUtils.computeRelevantSources(); + assertEquals(3, relevantSources.size()); + assertTrue(relevantSources.containsKey("A1@host")); + assertEquals(1, relevantSources.get("A1@host").size()); + assertTrue(relevantSources.get("A1@host").contains("A1@host")); + assertTrue(relevantSources.containsKey("A2@host")); + assertEquals(1, relevantSources.get("A2@host").size()); + assertTrue(relevantSources.get("A2@host").contains("A2@host")); + assertTrue(relevantSources.containsKey("B@host")); + assertEquals(2, relevantSources.get("B@host").size()); + assertTrue(relevantSources.get("B@host").contains("A1@host")); + assertTrue(relevantSources.get("B@host").contains("A2@host")); + + dependencyClosure = DependencyGraphUtils.computeDependencyClosure(); + assertEquals(2, dependencyClosure.size()); + assertTrue(dependencyClosure.containsKey("A1@host")); + assertEquals(2, dependencyClosure.get("A1@host").size()); + assertTrue(dependencyClosure.get("A1@host").contains("A1@host")); + assertTrue(dependencyClosure.get("A1@host").contains("B@host")); + assertTrue(dependencyClosure.containsKey("A2@host")); + assertEquals(2, dependencyClosure.get("A2@host").size()); + assertTrue(dependencyClosure.get("A2@host").contains("A2@host")); + assertTrue(dependencyClosure.get("A2@host").contains("B@host")); + + final Set subsC = new HashSet<>(); + subsC.add(new Subscription("host", "A1")); + graph.processAdv(new Advertisement("host", "C"), subsC); + + relevantSources = DependencyGraphUtils.computeRelevantSources(); + assertEquals(4, relevantSources.size()); + assertTrue(relevantSources.containsKey("A1@host")); + assertTrue(relevantSources.containsKey("A2@host")); + assertTrue(relevantSources.containsKey("B@host")); + assertTrue(relevantSources.containsKey("C@host")); + assertEquals(1, relevantSources.get("A1@host").size()); + assertEquals(1, relevantSources.get("A2@host").size()); + assertEquals(2, relevantSources.get("B@host").size()); + assertEquals(1, relevantSources.get("C@host").size()); + assertTrue(relevantSources.get("B@host").contains("A1@host")); + assertTrue(relevantSources.get("B@host").contains("A2@host")); + assertTrue(relevantSources.get("C@host").contains("A1@host")); + + dependencyClosure = DependencyGraphUtils.computeDependencyClosure(); + assertEquals(2, dependencyClosure.size()); + assertTrue(dependencyClosure.containsKey("A1@host")); + assertTrue(dependencyClosure.containsKey("A2@host")); + assertEquals(3, dependencyClosure.get("A1@host").size()); + assertTrue(dependencyClosure.get("A1@host").contains("A1@host")); + assertTrue(dependencyClosure.get("A1@host").contains("B@host")); + assertTrue(dependencyClosure.get("A1@host").contains("C@host")); + assertEquals(2, dependencyClosure.get("A2@host").size()); + assertTrue(dependencyClosure.get("A2@host").contains("A2@host")); + assertTrue(dependencyClosure.get("A2@host").contains("B@host")); + + final Set subsD = new HashSet<>(); + subsD.add(new Subscription("host", "B")); + subsD.add(new Subscription("host", "C")); + graph.processAdv(new Advertisement("host", "D"), subsD); + + relevantSources = DependencyGraphUtils.computeRelevantSources(); + assertEquals(5, relevantSources.size()); + assertTrue(relevantSources.containsKey("A1@host")); + assertTrue(relevantSources.containsKey("A2@host")); + assertTrue(relevantSources.containsKey("B@host")); + assertTrue(relevantSources.containsKey("C@host")); + assertTrue(relevantSources.containsKey("D@host")); + assertEquals(1, relevantSources.get("A1@host").size()); + assertEquals(1, relevantSources.get("A2@host").size()); + assertEquals(2, relevantSources.get("B@host").size()); + assertEquals(1, relevantSources.get("C@host").size()); + assertEquals(2, relevantSources.get("D@host").size()); + assertTrue(relevantSources.get("A1@host").contains("A1@host")); + assertTrue(relevantSources.get("B@host").contains("A1@host")); + assertTrue(relevantSources.get("B@host").contains("A2@host")); + assertTrue(relevantSources.get("C@host").contains("A1@host")); + assertTrue(relevantSources.get("D@host").contains("A1@host")); + + dependencyClosure = DependencyGraphUtils.computeDependencyClosure(); + assertEquals(2, dependencyClosure.size()); + assertTrue(dependencyClosure.containsKey("A1@host")); + assertTrue(dependencyClosure.containsKey("A2@host")); + assertEquals(4, dependencyClosure.get("A1@host").size()); + assertTrue(dependencyClosure.get("A1@host").contains("A1@host")); + assertTrue(dependencyClosure.get("A1@host").contains("B@host")); + assertTrue(dependencyClosure.get("A1@host").contains("C@host")); + assertTrue(dependencyClosure.get("A1@host").contains("D@host")); + assertEquals(3, dependencyClosure.get("A2@host").size()); + assertTrue(dependencyClosure.get("A2@host").contains("A2@host")); + assertTrue(dependencyClosure.get("A2@host").contains("B@host")); + assertTrue(dependencyClosure.get("A2@host").contains("D@host")); + + } } diff --git a/Dream2/src/test/java/dream/common/utils/FinalNodesDetectorTest.java b/Dream2/src/test/java/dream/common/utils/FinalNodesDetectorTest.java index de4c3d9..14a403b 100755 --- a/Dream2/src/test/java/dream/common/utils/FinalNodesDetectorTest.java +++ b/Dream2/src/test/java/dream/common/utils/FinalNodesDetectorTest.java @@ -13,258 +13,259 @@ public class FinalNodesDetectorTest { - private static final String hostId = "hostId"; - private static final String atHostId = "@" + hostId; - - @Test - public void singleExpressionTest() { - // A - DependencyGraph.instance.clear(); - final FinalNodesDetector finalNodesDetector = new FinalNodesDetector(); - - generateAdvertisementPacket(finalNodesDetector, "A"); - finalNodesDetector.consolidate(); - - final Set results = finalNodesDetector.getFinalNodesFor("A" + atHostId); - assertEquals(1, results.size()); - assertTrue(results.contains("A" + atHostId)); - } - - @Test - public void simpleDependencyTest() { - // A - // B = f(A) - DependencyGraph.instance.clear(); - final FinalNodesDetector finalNodesDetector = new FinalNodesDetector(); - - generateAdvertisementPacket(finalNodesDetector, "A"); - generateAdvertisementPacket(finalNodesDetector, "B", "A"); - finalNodesDetector.consolidate(); - - final Set results = finalNodesDetector.getFinalNodesFor("A" + atHostId); - assertEquals(1, results.size()); - assertTrue(results.contains("B" + atHostId)); - } - - @Test - public void simpleDependencyTest2() { - // A - // B = f(A) - // C = f(B) - // D = f(C) - DependencyGraph.instance.clear(); - final FinalNodesDetector finalNodesDetector = new FinalNodesDetector(); - - generateAdvertisementPacket(finalNodesDetector, "A"); - generateAdvertisementPacket(finalNodesDetector, "B", "A"); - generateAdvertisementPacket(finalNodesDetector, "C", "B"); - generateAdvertisementPacket(finalNodesDetector, "D", "C"); - finalNodesDetector.consolidate(); - - final Set results = finalNodesDetector.getFinalNodesFor("A" + atHostId); - assertEquals(1, results.size()); - assertTrue(results.contains("D" + atHostId)); - } - - @Test - public void simpleDependencyTest3() { - // A - // B = f(A) - // C = f(A) - DependencyGraph.instance.clear(); - final FinalNodesDetector finalNodesDetector = new FinalNodesDetector(); - - generateAdvertisementPacket(finalNodesDetector, "A"); - generateAdvertisementPacket(finalNodesDetector, "B", "A"); - generateAdvertisementPacket(finalNodesDetector, "C", "A"); - finalNodesDetector.consolidate(); - - final Set results = finalNodesDetector.getFinalNodesFor("A" + atHostId); - assertEquals(2, results.size()); - assertTrue(results.contains("B" + atHostId)); - assertTrue(results.contains("C" + atHostId)); - } - - @Test - public void doubleDependencyTest() { - // A - // B = f(A) - // C = f(B) - // D = f(B) - DependencyGraph.instance.clear(); - final FinalNodesDetector finalNodesDetector = new FinalNodesDetector(); - - generateAdvertisementPacket(finalNodesDetector, "A"); - generateAdvertisementPacket(finalNodesDetector, "B", "A"); - generateAdvertisementPacket(finalNodesDetector, "C", "B"); - generateAdvertisementPacket(finalNodesDetector, "D", "B"); - finalNodesDetector.consolidate(); - - final Set results = finalNodesDetector.getFinalNodesFor("A" + atHostId); - assertEquals(2, results.size()); - assertTrue(results.contains("C" + atHostId)); - assertTrue(results.contains("C" + atHostId)); - } - - @Test - public void tripleDependencyTest() { - // A - // B = f(A) - // C = f(B) - // D = f(B) - // E = f(D) - DependencyGraph.instance.clear(); - final FinalNodesDetector finalNodesDetector = new FinalNodesDetector(); - - generateAdvertisementPacket(finalNodesDetector, "A"); - generateAdvertisementPacket(finalNodesDetector, "B", "A"); - generateAdvertisementPacket(finalNodesDetector, "C", "B"); - generateAdvertisementPacket(finalNodesDetector, "D", "B"); - generateAdvertisementPacket(finalNodesDetector, "E", "D"); - finalNodesDetector.consolidate(); - - final Set results = finalNodesDetector.getFinalNodesFor("A" + atHostId); - assertEquals(2, results.size()); - assertTrue(results.contains("C" + atHostId)); - assertTrue(results.contains("E" + atHostId)); - } - - @Test - public void treeDependencyTest() { - // A - // B = f(A) - // C = f(A) - // D = f(B) - // E = f(B) - // F = f(C) - // G = f(C) - DependencyGraph.instance.clear(); - final FinalNodesDetector finalNodesDetector = new FinalNodesDetector(); - - generateAdvertisementPacket(finalNodesDetector, "A"); - generateAdvertisementPacket(finalNodesDetector, "B", "A"); - generateAdvertisementPacket(finalNodesDetector, "C", "A"); - generateAdvertisementPacket(finalNodesDetector, "D", "B"); - generateAdvertisementPacket(finalNodesDetector, "E", "B"); - generateAdvertisementPacket(finalNodesDetector, "F", "C"); - generateAdvertisementPacket(finalNodesDetector, "G", "C"); - finalNodesDetector.consolidate(); - - final Set results = finalNodesDetector.getFinalNodesFor("A" + atHostId); - assertEquals(4, results.size()); - assertTrue(results.contains("D" + atHostId)); - assertTrue(results.contains("E" + atHostId)); - assertTrue(results.contains("F" + atHostId)); - assertTrue(results.contains("G" + atHostId)); - } - - @Test - public void treeDependencyTest2() { - // A - // B = f(A) - // C = f(A) - // D = f(B) - // E = f(B) - // F = f(C) - // G = f(C) - // H = f(G) - DependencyGraph.instance.clear(); - final FinalNodesDetector finalNodesDetector = new FinalNodesDetector(); - - generateAdvertisementPacket(finalNodesDetector, "A"); - generateAdvertisementPacket(finalNodesDetector, "B", "A"); - generateAdvertisementPacket(finalNodesDetector, "C", "A"); - generateAdvertisementPacket(finalNodesDetector, "D", "B"); - generateAdvertisementPacket(finalNodesDetector, "E", "B"); - generateAdvertisementPacket(finalNodesDetector, "F", "C"); - generateAdvertisementPacket(finalNodesDetector, "G", "C"); - generateAdvertisementPacket(finalNodesDetector, "H", "G"); - finalNodesDetector.consolidate(); - - final Set results = finalNodesDetector.getFinalNodesFor("A" + atHostId); - assertEquals(4, results.size()); - assertTrue(results.contains("D" + atHostId)); - assertTrue(results.contains("E" + atHostId)); - assertTrue(results.contains("F" + atHostId)); - assertTrue(results.contains("H" + atHostId)); - } - - @Test - public void graphDependencyTest() { - // A - // B - // C = f(A, B) - // D = f(A, B) - // E = f(C, D) - DependencyGraph.instance.clear(); - final FinalNodesDetector finalNodesDetector = new FinalNodesDetector(); - - generateAdvertisementPacket(finalNodesDetector, "A"); - generateAdvertisementPacket(finalNodesDetector, "B"); - generateAdvertisementPacket(finalNodesDetector, "C", "A", "B"); - generateAdvertisementPacket(finalNodesDetector, "D", "A", "B"); - generateAdvertisementPacket(finalNodesDetector, "E", "C", "D"); - finalNodesDetector.consolidate(); - - final Set resultsA = finalNodesDetector.getFinalNodesFor("A" + atHostId); - assertEquals(1, resultsA.size()); - assertTrue(resultsA.contains("E" + atHostId)); - - final Set resultsB = finalNodesDetector.getFinalNodesFor("B" + atHostId); - assertEquals(1, resultsB.size()); - assertTrue(resultsB.contains("E" + atHostId)); - } - - @Test - public void triangularDependencyTest() { - // A - // B = f(A) - // C = f(A, B) - DependencyGraph.instance.clear(); - final FinalNodesDetector finalNodesDetector = new FinalNodesDetector(); - - generateAdvertisementPacket(finalNodesDetector, "A"); - generateAdvertisementPacket(finalNodesDetector, "B", "A"); - generateAdvertisementPacket(finalNodesDetector, "C", "A", "B"); - finalNodesDetector.consolidate(); - - final Set results = finalNodesDetector.getFinalNodesFor("A" + atHostId); - assertEquals(1, results.size()); - assertTrue(results.contains("C" + atHostId)); - } - - @Test - public void cycleDependencyTest() { - // A - // B = f(A) - // C = f(A) - // D = f(B, C) - DependencyGraph.instance.clear(); - final FinalNodesDetector finalNodesDetector = new FinalNodesDetector(); - - generateAdvertisementPacket(finalNodesDetector, "A"); - generateAdvertisementPacket(finalNodesDetector, "B", "A"); - generateAdvertisementPacket(finalNodesDetector, "C", "A"); - generateAdvertisementPacket(finalNodesDetector, "D", "B", "C"); - finalNodesDetector.consolidate(); - - final Set results = finalNodesDetector.getFinalNodesFor("A" + atHostId); - assertEquals(1, results.size()); - assertTrue(results.contains("D" + atHostId)); - } - - private final void generateAdvertisementPacket(FinalNodesDetector finalNodesDetector, String name, String... subsNames) { - final Advertisement adv = new Advertisement(hostId, name); - final Set subs = new HashSet(); - for (final String subName : subsNames) { - final Subscription sub = new Subscription(hostId, subName); - subs.add(sub); - } - if (subs.isEmpty()) { - DependencyGraph.instance.processAdv(adv); - } else { - DependencyGraph.instance.processAdv(adv, subs); - } - } + private static final String hostId = "hostId"; + private static final String atHostId = "@" + hostId; + + @Test + public void singleExpressionTest() { + // A + DependencyGraph.instance.clear(); + final FinalNodesDetector finalNodesDetector = new FinalNodesDetector(); + + generateAdvertisementPacket(finalNodesDetector, "A"); + finalNodesDetector.consolidate(); + + final Set results = finalNodesDetector.getFinalNodesFor("A" + atHostId); + assertEquals(1, results.size()); + assertTrue(results.contains("A" + atHostId)); + } + + @Test + public void simpleDependencyTest() { + // A + // B = f(A) + DependencyGraph.instance.clear(); + final FinalNodesDetector finalNodesDetector = new FinalNodesDetector(); + + generateAdvertisementPacket(finalNodesDetector, "A"); + generateAdvertisementPacket(finalNodesDetector, "B", "A"); + finalNodesDetector.consolidate(); + + final Set results = finalNodesDetector.getFinalNodesFor("A" + atHostId); + assertEquals(1, results.size()); + assertTrue(results.contains("B" + atHostId)); + } + + @Test + public void simpleDependencyTest2() { + // A + // B = f(A) + // C = f(B) + // D = f(C) + DependencyGraph.instance.clear(); + final FinalNodesDetector finalNodesDetector = new FinalNodesDetector(); + + generateAdvertisementPacket(finalNodesDetector, "A"); + generateAdvertisementPacket(finalNodesDetector, "B", "A"); + generateAdvertisementPacket(finalNodesDetector, "C", "B"); + generateAdvertisementPacket(finalNodesDetector, "D", "C"); + finalNodesDetector.consolidate(); + + final Set results = finalNodesDetector.getFinalNodesFor("A" + atHostId); + assertEquals(1, results.size()); + assertTrue(results.contains("D" + atHostId)); + } + + @Test + public void simpleDependencyTest3() { + // A + // B = f(A) + // C = f(A) + DependencyGraph.instance.clear(); + final FinalNodesDetector finalNodesDetector = new FinalNodesDetector(); + + generateAdvertisementPacket(finalNodesDetector, "A"); + generateAdvertisementPacket(finalNodesDetector, "B", "A"); + generateAdvertisementPacket(finalNodesDetector, "C", "A"); + finalNodesDetector.consolidate(); + + final Set results = finalNodesDetector.getFinalNodesFor("A" + atHostId); + assertEquals(2, results.size()); + assertTrue(results.contains("B" + atHostId)); + assertTrue(results.contains("C" + atHostId)); + } + + @Test + public void doubleDependencyTest() { + // A + // B = f(A) + // C = f(B) + // D = f(B) + DependencyGraph.instance.clear(); + final FinalNodesDetector finalNodesDetector = new FinalNodesDetector(); + + generateAdvertisementPacket(finalNodesDetector, "A"); + generateAdvertisementPacket(finalNodesDetector, "B", "A"); + generateAdvertisementPacket(finalNodesDetector, "C", "B"); + generateAdvertisementPacket(finalNodesDetector, "D", "B"); + finalNodesDetector.consolidate(); + + final Set results = finalNodesDetector.getFinalNodesFor("A" + atHostId); + assertEquals(2, results.size()); + assertTrue(results.contains("C" + atHostId)); + assertTrue(results.contains("C" + atHostId)); + } + + @Test + public void tripleDependencyTest() { + // A + // B = f(A) + // C = f(B) + // D = f(B) + // E = f(D) + DependencyGraph.instance.clear(); + final FinalNodesDetector finalNodesDetector = new FinalNodesDetector(); + + generateAdvertisementPacket(finalNodesDetector, "A"); + generateAdvertisementPacket(finalNodesDetector, "B", "A"); + generateAdvertisementPacket(finalNodesDetector, "C", "B"); + generateAdvertisementPacket(finalNodesDetector, "D", "B"); + generateAdvertisementPacket(finalNodesDetector, "E", "D"); + finalNodesDetector.consolidate(); + + final Set results = finalNodesDetector.getFinalNodesFor("A" + atHostId); + assertEquals(2, results.size()); + assertTrue(results.contains("C" + atHostId)); + assertTrue(results.contains("E" + atHostId)); + } + + @Test + public void treeDependencyTest() { + // A + // B = f(A) + // C = f(A) + // D = f(B) + // E = f(B) + // F = f(C) + // G = f(C) + DependencyGraph.instance.clear(); + final FinalNodesDetector finalNodesDetector = new FinalNodesDetector(); + + generateAdvertisementPacket(finalNodesDetector, "A"); + generateAdvertisementPacket(finalNodesDetector, "B", "A"); + generateAdvertisementPacket(finalNodesDetector, "C", "A"); + generateAdvertisementPacket(finalNodesDetector, "D", "B"); + generateAdvertisementPacket(finalNodesDetector, "E", "B"); + generateAdvertisementPacket(finalNodesDetector, "F", "C"); + generateAdvertisementPacket(finalNodesDetector, "G", "C"); + finalNodesDetector.consolidate(); + + final Set results = finalNodesDetector.getFinalNodesFor("A" + atHostId); + assertEquals(4, results.size()); + assertTrue(results.contains("D" + atHostId)); + assertTrue(results.contains("E" + atHostId)); + assertTrue(results.contains("F" + atHostId)); + assertTrue(results.contains("G" + atHostId)); + } + + @Test + public void treeDependencyTest2() { + // A + // B = f(A) + // C = f(A) + // D = f(B) + // E = f(B) + // F = f(C) + // G = f(C) + // H = f(G) + DependencyGraph.instance.clear(); + final FinalNodesDetector finalNodesDetector = new FinalNodesDetector(); + + generateAdvertisementPacket(finalNodesDetector, "A"); + generateAdvertisementPacket(finalNodesDetector, "B", "A"); + generateAdvertisementPacket(finalNodesDetector, "C", "A"); + generateAdvertisementPacket(finalNodesDetector, "D", "B"); + generateAdvertisementPacket(finalNodesDetector, "E", "B"); + generateAdvertisementPacket(finalNodesDetector, "F", "C"); + generateAdvertisementPacket(finalNodesDetector, "G", "C"); + generateAdvertisementPacket(finalNodesDetector, "H", "G"); + finalNodesDetector.consolidate(); + + final Set results = finalNodesDetector.getFinalNodesFor("A" + atHostId); + assertEquals(4, results.size()); + assertTrue(results.contains("D" + atHostId)); + assertTrue(results.contains("E" + atHostId)); + assertTrue(results.contains("F" + atHostId)); + assertTrue(results.contains("H" + atHostId)); + } + + @Test + public void graphDependencyTest() { + // A + // B + // C = f(A, B) + // D = f(A, B) + // E = f(C, D) + DependencyGraph.instance.clear(); + final FinalNodesDetector finalNodesDetector = new FinalNodesDetector(); + + generateAdvertisementPacket(finalNodesDetector, "A"); + generateAdvertisementPacket(finalNodesDetector, "B"); + generateAdvertisementPacket(finalNodesDetector, "C", "A", "B"); + generateAdvertisementPacket(finalNodesDetector, "D", "A", "B"); + generateAdvertisementPacket(finalNodesDetector, "E", "C", "D"); + finalNodesDetector.consolidate(); + + final Set resultsA = finalNodesDetector.getFinalNodesFor("A" + atHostId); + assertEquals(1, resultsA.size()); + assertTrue(resultsA.contains("E" + atHostId)); + + final Set resultsB = finalNodesDetector.getFinalNodesFor("B" + atHostId); + assertEquals(1, resultsB.size()); + assertTrue(resultsB.contains("E" + atHostId)); + } + + @Test + public void triangularDependencyTest() { + // A + // B = f(A) + // C = f(A, B) + DependencyGraph.instance.clear(); + final FinalNodesDetector finalNodesDetector = new FinalNodesDetector(); + + generateAdvertisementPacket(finalNodesDetector, "A"); + generateAdvertisementPacket(finalNodesDetector, "B", "A"); + generateAdvertisementPacket(finalNodesDetector, "C", "A", "B"); + finalNodesDetector.consolidate(); + + final Set results = finalNodesDetector.getFinalNodesFor("A" + atHostId); + assertEquals(1, results.size()); + assertTrue(results.contains("C" + atHostId)); + } + + @Test + public void cycleDependencyTest() { + // A + // B = f(A) + // C = f(A) + // D = f(B, C) + DependencyGraph.instance.clear(); + final FinalNodesDetector finalNodesDetector = new FinalNodesDetector(); + + generateAdvertisementPacket(finalNodesDetector, "A"); + generateAdvertisementPacket(finalNodesDetector, "B", "A"); + generateAdvertisementPacket(finalNodesDetector, "C", "A"); + generateAdvertisementPacket(finalNodesDetector, "D", "B", "C"); + finalNodesDetector.consolidate(); + + final Set results = finalNodesDetector.getFinalNodesFor("A" + atHostId); + assertEquals(1, results.size()); + assertTrue(results.contains("D" + atHostId)); + } + + private final void generateAdvertisementPacket(FinalNodesDetector finalNodesDetector, String name, + String... subsNames) { + final Advertisement adv = new Advertisement(hostId, name); + final Set subs = new HashSet(); + for (final String subName : subsNames) { + final Subscription sub = new Subscription(hostId, subName); + subs.add(sub); + } + if (subs.isEmpty()) { + DependencyGraph.instance.processAdv(adv); + } else { + DependencyGraph.instance.processAdv(adv, subs); + } + } } diff --git a/Dream2/src/test/java/dream/common/utils/IntraSourceDependencyDetectorTest.java b/Dream2/src/test/java/dream/common/utils/IntraSourceDependencyDetectorTest.java index e1b2a7c..89e9b0c 100755 --- a/Dream2/src/test/java/dream/common/utils/IntraSourceDependencyDetectorTest.java +++ b/Dream2/src/test/java/dream/common/utils/IntraSourceDependencyDetectorTest.java @@ -14,870 +14,870 @@ public class IntraSourceDependencyDetectorTest { - @Test - public void noDependencyTest() { - // B = f(A) - // D = f(B, C) - final DependencyGraph graph = DependencyGraph.instance; - final IntraSourceDependencyDetector depDetector = IntraSourceDependencyDetector.instance; - graph.clear(); - - final Subscription subA = new Subscription("Host", "A"); - final Subscription subB = new Subscription("Host", "B"); - final Subscription subC = new Subscription("Host", "C"); - - final Advertisement advA = new Advertisement("Host", "A"); - final Advertisement advB = new Advertisement("Host", "B"); - final Advertisement advC = new Advertisement("Host", "C"); - final Advertisement advD = new Advertisement("Host", "D"); - - // Subscription to A (A generates B) - final Set subsB = new HashSet<>(); - subsB.add(subA); - graph.processAdv(advB, subsB); - - // Subscription to B and C (B, C generate D) - final Set subsD = new HashSet<>(); - subsD.add(subB); - subsD.add(subC); - graph.processAdv(advD, subsD); - - graph.processAdv(advA); - graph.processAdv(advC); - - // Consolidate - depDetector.consolidate(); - - // EventA - final Event evA = new Event<>("Host", "A", 1); - assertEquals(depDetector.getWaitRecommendations(evA, "A@Host").size(), 0); - - // EventB - final Event evB = new Event<>("Host", "B", 1); - assertEquals(depDetector.getWaitRecommendations(evB, "A@Host").size(), 0); - - // EventC - final Event evC = new Event<>("Host", "C", 1); - assertEquals(depDetector.getWaitRecommendations(evC, "A@Host").size(), 0); - - // EventD - final Event evD = new Event<>("Host", "D", 1); - assertEquals(depDetector.getWaitRecommendations(evD, "A@Host").size(), 0); - } - - @Test - public void basicTriangularCycleTest() { - // B = f(A) - // C = f(A, B) - final DependencyGraph graph = DependencyGraph.instance; - final IntraSourceDependencyDetector depDetector = IntraSourceDependencyDetector.instance; - graph.clear(); - - final Subscription subA = new Subscription("Host", "A"); - final Subscription subB = new Subscription("Host", "B"); - - final Advertisement advA = new Advertisement("Host", "A"); - final Advertisement advB = new Advertisement("Host", "B"); - final Advertisement advC = new Advertisement("Host", "C"); - - // Subscription to A (A generates B) - final Set subsB = new HashSet<>(); - subsB.add(subA); - graph.processAdv(advB, subsB); - - // Subscription to A, B (A, B generates C) - final Set subsC = new HashSet<>(); - subsC.add(subA); - subsC.add(subB); - graph.processAdv(advC, subsC); - - graph.processAdv(advA); - - // Consolidate - depDetector.consolidate(); - - // EventA - final Event evA = new Event("Host", "A", 1); - assertEquals(depDetector.getWaitRecommendations(evA, "").size(), 0); - final Event evA2 = new Event("Host", "A", 1); - assertEquals(depDetector.getWaitRecommendations(evA2, "A@Host").size(), 1); - for (final WaitRecommendations wr : depDetector.getWaitRecommendations(evA2, "A@Host")) { - assertTrue(wr.getExpression().equals("C@Host")); - assertEquals(wr.getRecommendations().size(), 1); - assertTrue(wr.getRecommendations().contains("B@Host")); - } - - // EventB - final Event evB1 = new Event("Host", "B", 1); - assertEquals(depDetector.getWaitRecommendations(evB1, "").size(), 0); - final Event evB2 = new Event("Host", "B", 1); - assertEquals(depDetector.getWaitRecommendations(evB2, "A@Host").size(), 1); - for (final WaitRecommendations wr : depDetector.getWaitRecommendations(evB2, "A@Host")) { - assertTrue(wr.getExpression().equals("C@Host")); - assertEquals(wr.getRecommendations().size(), 1); - assertTrue(wr.getRecommendations().contains("A@Host")); - } - - // EventC - final Event evC = new Event("Host", "C", 1); - assertEquals(depDetector.getWaitRecommendations(evC, "").size(), 0); - } - - @Test - public void basicDualCycleTest() { - // B = f(A) - // C = f(A) - // D = f(B, C) - final DependencyGraph graph = DependencyGraph.instance; - final IntraSourceDependencyDetector depDetector = IntraSourceDependencyDetector.instance; - graph.clear(); - - final Subscription subA = new Subscription("Host", "A"); - final Subscription subB = new Subscription("Host", "B"); - final Subscription subC = new Subscription("Host", "C"); - - final Advertisement advA = new Advertisement("Host", "A"); - final Advertisement advB = new Advertisement("Host", "B"); - final Advertisement advC = new Advertisement("Host", "C"); - final Advertisement advD = new Advertisement("Host", "D"); - - // Subscription to A (A generates B) - final Set subsB = new HashSet(); - subsB.add(subA); - graph.processAdv(advB, subsB); - - // Subscription to A (A generates C) - final Set subsC = new HashSet(); - subsC.add(subA); - graph.processAdv(advC, subsC); - - // Subscription to B, C (B, C generate D) - final Set subsD = new HashSet(); - subsD.add(subB); - subsD.add(subC); - graph.processAdv(advD, subsD); - - graph.processAdv(advA); - - // Consolidate - depDetector.consolidate(); - - // EventA - final Event evA = new Event("Host", "A", 1); - assertEquals(depDetector.getWaitRecommendations(evA, "").size(), 0); - - // EventB - final Event evB1 = new Event("Host", "B", 1); - assertEquals(depDetector.getWaitRecommendations(evB1, "").size(), 0); - final Event evB2 = new Event("Host", "B", 1); - assertEquals(depDetector.getWaitRecommendations(evB2, "A@Host").size(), 1); - for (final WaitRecommendations wr : depDetector.getWaitRecommendations(evB2, "A@Host")) { - assertTrue(wr.getExpression().equals("D@Host")); - assertEquals(wr.getRecommendations().size(), 1); - assertTrue(wr.getRecommendations().contains("C@Host")); - } - - // EventC - final Event evC1 = new Event("Host", "C", 1); - assertEquals(depDetector.getWaitRecommendations(evC1, "").size(), 0); - final Event evC2 = new Event("Host", "C", 1); - assertEquals(depDetector.getWaitRecommendations(evC2, "A@Host").size(), 1); - for (final WaitRecommendations wr : depDetector.getWaitRecommendations(evC2, "A@Host")) { - assertTrue(wr.getExpression().equals("D@Host")); - assertEquals(wr.getRecommendations().size(), 1); - assertTrue(wr.getRecommendations().contains("B@Host")); - } - - // EventD - final Event evD = new Event("Host", "D", 1); - assertEquals(depDetector.getWaitRecommendations(evD, "").size(), 0); - } - - @Test - public void basicDualCycleTest2() { - // B = f(A) - // C = f(A) - // D = f(C) - // E = f(B, D) - final DependencyGraph graph = DependencyGraph.instance; - final IntraSourceDependencyDetector depDetector = IntraSourceDependencyDetector.instance; - graph.clear(); - - final Subscription subA = new Subscription("Host", "A"); - final Subscription subB = new Subscription("Host", "B"); - final Subscription subC = new Subscription("Host", "C"); - final Subscription subD = new Subscription("Host", "D"); - - final Advertisement advA = new Advertisement("Host", "A"); - final Advertisement advB = new Advertisement("Host", "B"); - final Advertisement advC = new Advertisement("Host", "C"); - final Advertisement advD = new Advertisement("Host", "D"); - final Advertisement advE = new Advertisement("Host", "E"); - - graph.processAdv(advA); - - // Subscription to A (A generates B) - final Set subsB = new HashSet(); - subsB.add(subA); - graph.processAdv(advB, subsB); - - // Subscription to A (A generates C) - final Set subsC = new HashSet(); - subsC.add(subA); - graph.processAdv(advC, subsC); - - // Subscription to C (C generate D) - final Set subsD = new HashSet(); - subsD.add(subC); - graph.processAdv(advD, subsD); - - // Subscription to B, D (B, D generate E) - final Set subsE = new HashSet(); - subsE.add(subB); - subsE.add(subD); - graph.processAdv(advE, subsE); - - // Consolidate - depDetector.consolidate(); - - // EventA - final Event evA = new Event("Host", "A", 1); - assertEquals(depDetector.getWaitRecommendations(evA, "").size(), 0); - - // EventB - final Event evB1 = new Event("Host", "B", 1); - assertEquals(depDetector.getWaitRecommendations(evB1, "").size(), 0); - final Event evB2 = new Event("Host", "B", 1); - assertEquals(depDetector.getWaitRecommendations(evB2, "A@Host").size(), 1); - for (final WaitRecommendations wr : depDetector.getWaitRecommendations(evB2, "A@Host")) { - assertTrue(wr.getExpression().equals("E@Host")); - assertEquals(wr.getRecommendations().size(), 1); - assertTrue(wr.getRecommendations().contains("D@Host")); - } - - // EventC - final Event evC1 = new Event("Host", "C", 1); - assertEquals(depDetector.getWaitRecommendations(evC1, "").size(), 0); - final Event evC2 = new Event("Host", "C", 1); - assertEquals(depDetector.getWaitRecommendations(evC2, "A@Host").size(), 0); - - // EventD - final Event evD1 = new Event("Host", "D", 1); - assertEquals(depDetector.getWaitRecommendations(evD1, "").size(), 0); - final Event evD2 = new Event("Host", "D", 1); - assertEquals(depDetector.getWaitRecommendations(evD2, "A@Host").size(), 1); - for (final WaitRecommendations wr : depDetector.getWaitRecommendations(evD2, "A@Host")) { - assertTrue(wr.getExpression().equals("E@Host")); - assertEquals(wr.getRecommendations().size(), 1); - assertTrue(wr.getRecommendations().contains("B@Host")); - } - - // EventE - final Event evE = new Event("E", "Host", 1); - assertEquals(depDetector.getWaitRecommendations(evE, "").size(), 0); - } - - @Test - public void basicDualTriangle() { - // B = f(A) - // C = f(B) - // D = f(B, C) - final DependencyGraph graph = DependencyGraph.instance; - final IntraSourceDependencyDetector depDetector = IntraSourceDependencyDetector.instance; - graph.clear(); - - final Subscription subA = new Subscription("Host", "A"); - final Subscription subB = new Subscription("Host", "B"); - final Subscription subC = new Subscription("Host", "C"); - - final Advertisement advA = new Advertisement("Host", "A"); - final Advertisement advB = new Advertisement("Host", "B"); - final Advertisement advC = new Advertisement("Host", "C"); - final Advertisement advD = new Advertisement("Host", "D"); - - // Subscription to A (A generates B) - final Set subsB = new HashSet(); - subsB.add(subA); - graph.processAdv(advB, subsB); - - // Subscription to B (B generates C) - final Set subsC = new HashSet(); - subsC.add(subB); - graph.processAdv(advC, subsC); - - // Subscription to B, C (B, C generate D) - final Set subsD = new HashSet(); - subsD.add(subB); - subsD.add(subC); - graph.processAdv(advD, subsD); - - graph.processAdv(advA); - - // Consolidate - depDetector.consolidate(); - - // EventA - final Event evA = new Event("Host", "A", 1); - assertEquals(depDetector.getWaitRecommendations(evA, "").size(), 0); - - // EventB - final Event evB1 = new Event("Host", "B", 1); - assertEquals(depDetector.getWaitRecommendations(evB1, "").size(), 0); - final Event evB2 = new Event("Host", "B", 1); - assertEquals(depDetector.getWaitRecommendations(evB2, "A@Host").size(), 1); - for (final WaitRecommendations wr : depDetector.getWaitRecommendations(evB2, "A@Host")) { - assertTrue(wr.getExpression().equals("D@Host")); - assertEquals(wr.getRecommendations().size(), 1); - assertTrue(wr.getRecommendations().contains("C@Host")); - } - - // EventC - final Event evC1 = new Event("Host", "C", 1); - assertEquals(depDetector.getWaitRecommendations(evC1, "").size(), 0); - final Event evC2 = new Event("Host", "C", 1); - assertEquals(depDetector.getWaitRecommendations(evC2, "A@Host").size(), 1); - for (final WaitRecommendations wr : depDetector.getWaitRecommendations(evC2, "A@Host")) { - assertTrue(wr.getExpression().equals("D@Host")); - assertEquals(wr.getRecommendations().size(), 1); - assertTrue(wr.getRecommendations().contains("B@Host")); - } - - // EventD - final Event evD1 = new Event("Host", "D", 1); - assertEquals(depDetector.getWaitRecommendations(evD1, "").size(), 0); - final Event evD2 = new Event("Host", "D", 1); - assertEquals(depDetector.getWaitRecommendations(evD2, "A@Host").size(), 0); - } - - @Test - public void basicTripleCycleTest() { - // B = f(A) - // C = f(A) - // D = f(A) - // E = f(B, C, D) - final DependencyGraph graph = DependencyGraph.instance; - final IntraSourceDependencyDetector depDetector = IntraSourceDependencyDetector.instance; - graph.clear(); - - final Subscription subA = new Subscription("Host", "A"); - final Subscription subB = new Subscription("Host", "B"); - final Subscription subC = new Subscription("Host", "C"); - final Subscription subD = new Subscription("Host", "D"); - - final Advertisement advA = new Advertisement("Host", "A"); - final Advertisement advB = new Advertisement("Host", "B"); - final Advertisement advC = new Advertisement("Host", "C"); - final Advertisement advD = new Advertisement("Host", "D"); - final Advertisement advE = new Advertisement("Host", "E"); - - graph.processAdv(advA); - - // Subscription to A (A generates B) - final Set subsB = new HashSet(); - subsB.add(subA); - graph.processAdv(advB, subsB); - - // Subscription to A (A generates C) - final Set subsC = new HashSet(); - subsC.add(subA); - graph.processAdv(advC, subsC); - - // Subscription to A (A generates D) - final Set subsD = new HashSet(); - subsD.add(subA); - graph.processAdv(advD, subsD); - - // Subscription to B, C, D (B, C, D generate E) - final Set subsE = new HashSet(); - subsE.add(subB); - subsE.add(subC); - subsE.add(subD); - graph.processAdv(advE, subsE); - - // Consolidate - depDetector.consolidate(); - - // EventA - final Event evA = new Event("Host", "A", 1); - assertEquals(depDetector.getWaitRecommendations(evA, "").size(), 0); - - // EventB - final Event evB1 = new Event("Host", "B", 1); - assertEquals(depDetector.getWaitRecommendations(evB1, "").size(), 0); - final Event evB2 = new Event("Host", "B", 1); - assertEquals(depDetector.getWaitRecommendations(evB2, "A@Host").size(), 1); - for (final WaitRecommendations wr : depDetector.getWaitRecommendations(evB2, "A@Host")) { - assertTrue(wr.getExpression().equals("E@Host")); - assertEquals(wr.getRecommendations().size(), 2); - assertTrue(wr.getRecommendations().contains("C@Host")); - assertTrue(wr.getRecommendations().contains("D@Host")); - } - - // EventC - final Event evC1 = new Event("Host", "C", 1); - assertEquals(depDetector.getWaitRecommendations(evC1, "").size(), 0); - final Event evC2 = new Event("Host", "C", 1); - assertEquals(depDetector.getWaitRecommendations(evC2, "A@Host").size(), 1); - for (final WaitRecommendations wr : depDetector.getWaitRecommendations(evC2, "A@Host")) { - assertTrue(wr.getExpression().equals("E@Host")); - assertEquals(wr.getRecommendations().size(), 2); - assertTrue(wr.getRecommendations().contains("B@Host")); - assertTrue(wr.getRecommendations().contains("D@Host")); - } - - // EventD - final Event evD1 = new Event("Host", "D", 1); - assertEquals(depDetector.getWaitRecommendations(evD1, "").size(), 0); - final Event evD2 = new Event("Host", "D", 1); - assertEquals(depDetector.getWaitRecommendations(evD2, "A@Host").size(), 1); - for (final WaitRecommendations wr : depDetector.getWaitRecommendations(evD2, "A@Host")) { - assertTrue(wr.getExpression().equals("E@Host")); - assertEquals(wr.getRecommendations().size(), 2); - assertTrue(wr.getRecommendations().contains("B@Host")); - assertTrue(wr.getRecommendations().contains("C@Host")); - } - - // EventE - final Event evE = new Event("Host", "E", 1); - assertEquals(depDetector.getWaitRecommendations(evE, "A@Host").size(), 0); - } - - @Test - public void dualCyclesTest() { - // B1 = f(A1) - // C1 = f(A1) - // B2 = f(A2) - // C2 = f(A2) - // D = f(B1, C1, B2, C2) - final DependencyGraph graph = DependencyGraph.instance; - final IntraSourceDependencyDetector depDetector = IntraSourceDependencyDetector.instance; - graph.clear(); - - final Subscription subA1 = new Subscription("Host", "A1"); - final Subscription subA2 = new Subscription("Host", "A2"); - final Subscription subB1 = new Subscription("Host", "B1"); - final Subscription subB2 = new Subscription("Host", "B2"); - final Subscription subC1 = new Subscription("Host", "C1"); - final Subscription subC2 = new Subscription("Host", "C2"); - - final Advertisement advA1 = new Advertisement("Host", "A1"); - final Advertisement advA2 = new Advertisement("Host", "A2"); - final Advertisement advB1 = new Advertisement("Host", "B1"); - final Advertisement advB2 = new Advertisement("Host", "B2"); - final Advertisement advC1 = new Advertisement("Host", "C1"); - final Advertisement advC2 = new Advertisement("Host", "C2"); - final Advertisement advD = new Advertisement("Host", "D"); - - // Subscription to A1 (A1 generates B2) - final Set subsB1 = new HashSet(); - subsB1.add(subA1); - graph.processAdv(advB1, subsB1); - - // Subscription to A2 (A2 generates B2) - final Set subsB2 = new HashSet(); - subsB2.add(subA2); - graph.processAdv(advB2, subsB2); - - // Subscription to A1 (A1 generates C1) - final Set subsC1 = new HashSet(); - subsC1.add(subA1); - graph.processAdv(advC1, subsC1); - - // Subscription to A2 (A2 generates C2) - final Set subsC2 = new HashSet(); - subsC2.add(subA2); - graph.processAdv(advC2, subsC2); - - // Subscription to B1, B2, C1, C2 (B1, B2, C1, C2 generate D) - final Set subsD = new HashSet(); - subsD.add(subB1); - subsD.add(subB2); - subsD.add(subC1); - subsD.add(subC2); - graph.processAdv(advD, subsD); - - graph.processAdv(advA1); - graph.processAdv(advA2); - - // Consolidate - depDetector.consolidate(); - - // EventA1 - final Event evA1 = new Event("Host", "A1", 1); - assertEquals(depDetector.getWaitRecommendations(evA1, "A1@Host").size(), 0); - - // EventA2 - final Event evA2 = new Event("Host", "A2", 1); - assertEquals(depDetector.getWaitRecommendations(evA2, "A2@Host").size(), 0); - - // EventB1 - final Event evB1 = new Event("Host", "B1", 1); - assertEquals(depDetector.getWaitRecommendations(evB1, "A1@Host").size(), 1); - for (final WaitRecommendations wr : depDetector.getWaitRecommendations(evB1, "A1@Host")) { - assertTrue(wr.getExpression().equals("D@Host")); - assertEquals(wr.getRecommendations().size(), 1); - assertTrue(wr.getRecommendations().contains("C1@Host")); - } - - // EventC1 - final Event evC1 = new Event("Host", "C1", 1); - assertEquals(depDetector.getWaitRecommendations(evC1, "A1@Host").size(), 1); - for (final WaitRecommendations wr : depDetector.getWaitRecommendations(evC1, "A1@Host")) { - assertTrue(wr.getExpression().equals("D@Host")); - assertEquals(wr.getRecommendations().size(), 1); - assertTrue(wr.getRecommendations().contains("B1@Host")); - } - - // EventB2 - final Event evB2 = new Event("Host", "B2", 1); - assertEquals(depDetector.getWaitRecommendations(evB2, "A2@Host").size(), 1); - for (final WaitRecommendations wr : depDetector.getWaitRecommendations(evB2, "A2@Host")) { - assertTrue(wr.getExpression().equals("D@Host")); - assertEquals(wr.getRecommendations().size(), 1); - assertTrue(wr.getRecommendations().contains("C2@Host")); - } - - // EventC2 - final Event evC2 = new Event("Host", "C2", 1); - assertEquals(depDetector.getWaitRecommendations(evC2, "A2@Host").size(), 1); - for (final WaitRecommendations wr : depDetector.getWaitRecommendations(evC2, "A2@Host")) { - assertTrue(wr.getExpression().equals("D@Host")); - assertEquals(wr.getRecommendations().size(), 1); - assertTrue(wr.getRecommendations().contains("B2@Host")); - } - - // EventD - final Event evD = new Event("Host", "D", 1); - assertEquals(depDetector.getWaitRecommendations(evD, "A2@Host").size(), 0); - } - - @Test - public void dualDependencyTest() { - // B = f(A1) - // C = f(A1, A2) - // D = f(B, C) - final DependencyGraph graph = DependencyGraph.instance; - final IntraSourceDependencyDetector depDetector = IntraSourceDependencyDetector.instance; - graph.clear(); - - final Subscription subA1 = new Subscription("Host", "A1"); - final Subscription subA2 = new Subscription("Host", "A2"); - final Subscription subB = new Subscription("Host", "B"); - final Subscription subC = new Subscription("Host", "C"); - - final Advertisement advA1 = new Advertisement("Host", "A1"); - final Advertisement advA2 = new Advertisement("Host", "A2"); - final Advertisement advB = new Advertisement("Host", "B"); - final Advertisement advC = new Advertisement("Host", "C"); - final Advertisement advD = new Advertisement("Host", "D"); - - graph.processAdv(advA1); - graph.processAdv(advA2); - - // Subscription to A1 (A1 generates B) - final Set subsB = new HashSet(); - subsB.add(subA1); - graph.processAdv(advB, subsB); - - // Subscription to A1, A2 (A1, A2 generate C) - final Set subsC = new HashSet(); - subsC.add(subA1); - subsC.add(subA2); - graph.processAdv(advC, subsC); - - // Subscription to D (B, C generate D) - final Set subsD = new HashSet(); - subsD.add(subB); - subsD.add(subC); - graph.processAdv(advD, subsD); - - // Consolidate - depDetector.consolidate(); - - // EventA1 - final Event evA1 = new Event("Host", "A1", 1); - assertEquals(depDetector.getWaitRecommendations(evA1, "A1@Host").size(), 0); - - // EventA2 - final Event evA2 = new Event("Host", "A2", 1); - assertEquals(depDetector.getWaitRecommendations(evA2, "A2@Host").size(), 0); - - // EventB from A1 - final Event evB1 = new Event("Host", "B", 1); - assertEquals(depDetector.getWaitRecommendations(evB1, "").size(), 0); - final Event evB2 = new Event("Host", "B", 1); - assertEquals(depDetector.getWaitRecommendations(evB2, "A1@Host").size(), 1); - for (final WaitRecommendations wr : depDetector.getWaitRecommendations(evB2, "A1@Host")) { - assertTrue(wr.getExpression().equals("D@Host")); - assertEquals(wr.getRecommendations().size(), 1); - assertTrue(wr.getRecommendations().contains("C@Host")); - } - - // EventC from A1 - final Event evC1_1 = new Event("Host", "C", 1); - assertEquals(depDetector.getWaitRecommendations(evC1_1, "").size(), 0); - final Event evC1_2 = new Event("Host", "C", 1); - assertEquals(depDetector.getWaitRecommendations(evC1_2, "A1@Host").size(), 1); - for (final WaitRecommendations wr : depDetector.getWaitRecommendations(evC1_2, "A1@Host")) { - assertTrue(wr.getExpression().equals("D@Host")); - assertEquals(wr.getRecommendations().size(), 1); - assertTrue(wr.getRecommendations().contains("B@Host")); - } - - // EventC from A2 - final Event evC2_1 = new Event("Host", "C", 1); - assertEquals(depDetector.getWaitRecommendations(evC2_1, "").size(), 0); - final Event evC2_2 = new Event("Host", "C", 1); - assertEquals(depDetector.getWaitRecommendations(evC2_2, "A2@Host").size(), 0); - - // EventD - final Event evD = new Event("Host", "D", 1); - assertEquals(depDetector.getWaitRecommendations(evD, "A2@Host").size(), 0); - } - - @Test - public void nestedCyclesTest() { - // B = f(A) - // C = f(A) - // E = f(A) - // G = f(E) - // F = f(E) - // H = f(F, G) - // D = f(B, C, H) - final DependencyGraph graph = DependencyGraph.instance; - final IntraSourceDependencyDetector depDetector = IntraSourceDependencyDetector.instance; - graph.clear(); - - final Subscription subA = new Subscription("Host", "A"); - final Subscription subB = new Subscription("Host", "B"); - final Subscription subC = new Subscription("Host", "C"); - final Subscription subE = new Subscription("Host", "E"); - final Subscription subF = new Subscription("Host", "F"); - final Subscription subG = new Subscription("Host", "G"); - final Subscription subH = new Subscription("Host", "H"); - - final Advertisement advA = new Advertisement("Host", "A"); - final Advertisement advB = new Advertisement("Host", "B"); - final Advertisement advC = new Advertisement("Host", "C"); - final Advertisement advD = new Advertisement("Host", "D"); - final Advertisement advE = new Advertisement("Host", "E"); - final Advertisement advF = new Advertisement("Host", "F"); - final Advertisement advG = new Advertisement("Host", "G"); - final Advertisement advH = new Advertisement("Host", "H"); - - // Subscription to A (A generates B) - final Set subsB = new HashSet(); - subsB.add(subA); - graph.processAdv(advB, subsB); - - // Subscription to A (A generates C) - final Set subsC = new HashSet(); - subsC.add(subA); - graph.processAdv(advC, subsC); - - // Subscription to A (A generates E) - final Set subsE = new HashSet(); - subsE.add(subA); - graph.processAdv(advE, subsE); - - // Subscription to E (E generates G) - final Set subsG = new HashSet(); - subsG.add(subE); - graph.processAdv(advG, subsG); - - // Subscription to E (E generates F) - final Set subsF = new HashSet(); - subsF.add(subE); - graph.processAdv(advF, subsF); - - // Subscription to F, G (F, G generate H) - final Set subsH = new HashSet(); - subsH.add(subF); - subsH.add(subG); - graph.processAdv(advH, subsH); - - // Subscription to B, C, H (B, C, H generate D) - final Set subsD = new HashSet(); - subsD.add(subB); - subsD.add(subC); - subsD.add(subH); - graph.processAdv(advD, subsD); - - graph.processAdv(advA); - - // Consolidate - depDetector.consolidate(); - - // EventA - final Event evA = new Event("Host", "A", 1); - assertEquals(depDetector.getWaitRecommendations(evA, "A@Host").size(), 0); - - // EventB - final Event evB1 = new Event("Host", "B", 1); - assertEquals(depDetector.getWaitRecommendations(evB1, "").size(), 0); - final Event evB2 = new Event("Host", "B", 1); - assertEquals(depDetector.getWaitRecommendations(evB2, "A@Host").size(), 1); - for (final WaitRecommendations wr : depDetector.getWaitRecommendations(evB2, "A@Host")) { - assertTrue(wr.getExpression().equals("D@Host")); - assertEquals(wr.getRecommendations().size(), 2); - assertTrue(wr.getRecommendations().contains("C@Host")); - assertTrue(wr.getRecommendations().contains("H@Host")); - } - - // EventC - final Event evC1 = new Event("Host", "C", 1); - assertEquals(depDetector.getWaitRecommendations(evC1, "").size(), 0); - final Event evC2 = new Event("Host", "C", 1); - assertEquals(depDetector.getWaitRecommendations(evC2, "A@Host").size(), 1); - for (final WaitRecommendations wr : depDetector.getWaitRecommendations(evC2, "A@Host")) { - assertTrue(wr.getExpression().equals("D@Host")); - assertEquals(wr.getRecommendations().size(), 2); - assertTrue(wr.getRecommendations().contains("B@Host")); - assertTrue(wr.getRecommendations().contains("H@Host")); - } - - // EventE - final Event evE = new Event("Host", "E", 1); - assertEquals(depDetector.getWaitRecommendations(evE, "A@Host").size(), 0); - - // EventF - final Event evF1 = new Event("Host", "F", 1); - assertEquals(depDetector.getWaitRecommendations(evF1, "").size(), 0); - final Event evF2 = new Event("Host", "F", 1); - assertEquals(depDetector.getWaitRecommendations(evF2, "A@Host").size(), 1); - for (final WaitRecommendations wr : depDetector.getWaitRecommendations(evF2, "A@Host")) { - assertTrue(wr.getExpression().equals("H@Host")); - assertEquals(wr.getRecommendations().size(), 1); - assertTrue(wr.getRecommendations().contains("G@Host")); - } - - // EventG - final Event evG1 = new Event("Host", "G", 1); - assertEquals(depDetector.getWaitRecommendations(evG1, "").size(), 0); - final Event evG2 = new Event("Host", "G", 1); - assertEquals(depDetector.getWaitRecommendations(evG2, "A@Host").size(), 1); - for (final WaitRecommendations wr : depDetector.getWaitRecommendations(evG2, "A@Host")) { - assertTrue(wr.getExpression().equals("H@Host")); - assertEquals(wr.getRecommendations().size(), 1); - assertTrue(wr.getRecommendations().contains("F@Host")); - } - - // EventH - final Event evH1 = new Event("Host", "H", 1); - assertEquals(depDetector.getWaitRecommendations(evH1, "").size(), 0); - final Event evH2 = new Event("Host", "H", 1); - assertEquals(depDetector.getWaitRecommendations(evH2, "A@Host").size(), 1); - for (final WaitRecommendations wr : depDetector.getWaitRecommendations(evH2, "A@Host")) { - assertTrue(wr.getExpression().equals("D@Host")); - assertEquals(wr.getRecommendations().size(), 2); - assertTrue(wr.getRecommendations().contains("B@Host")); - assertTrue(wr.getRecommendations().contains("C@Host")); - } - - // EventD - final Event evD = new Event("Host", "D", 1); - assertEquals(depDetector.getWaitRecommendations(evD, "A@Host").size(), 0); - } - - @Test - public void multipleCyclesTest() { - // B = f(A) - // C = f(A, B) - // D = f(B, C) - // E = f(A, D) - final DependencyGraph graph = DependencyGraph.instance; - final IntraSourceDependencyDetector depDetector = IntraSourceDependencyDetector.instance; - graph.clear(); - - final Subscription subA = new Subscription("Host", "A"); - final Subscription subB = new Subscription("Host", "B"); - final Subscription subC = new Subscription("Host", "C"); - final Subscription subD = new Subscription("Host", "D"); - - final Advertisement advA = new Advertisement("Host", "A"); - final Advertisement advB = new Advertisement("Host", "B"); - final Advertisement advC = new Advertisement("Host", "C"); - final Advertisement advD = new Advertisement("Host", "D"); - final Advertisement advE = new Advertisement("Host", "E"); - - // Subscription to A (A generates B) - final Set subsB = new HashSet(); - subsB.add(subA); - graph.processAdv(advB, subsB); - - // Subscription to A, B (A, B generate C) - final Set subsC = new HashSet(); - subsC.add(subA); - subsC.add(subB); - graph.processAdv(advC, subsC); - - // Subscription to B, C (B, C generate D) - final Set subsD = new HashSet(); - subsD.add(subB); - subsD.add(subC); - graph.processAdv(advD, subsD); - - // Subscription to A, D (A, D generate E) - final Set subsE = new HashSet(); - subsE.add(subA); - subsE.add(subD); - graph.processAdv(advE, subsE); - - graph.processAdv(advA); - - // Consolidate - depDetector.consolidate(); - - // EventA - final Event evA = new Event("Host", "A", 1); - assertEquals(2, depDetector.getWaitRecommendations(evA, "A@Host").size()); - for (final WaitRecommendations wr : depDetector.getWaitRecommendations(evA, "A@Host")) { - assertTrue(wr.getExpression().equals("C@Host") || wr.getExpression().equals("E@Host")); - if (wr.getExpression().equals("C@Host")) { - assertEquals(1, wr.getRecommendations().size()); - assertTrue(wr.getRecommendations().contains("B@Host")); - } - if (wr.getExpression().equals("E@Host")) { - assertEquals(1, wr.getRecommendations().size()); - assertTrue(wr.getRecommendations().contains("D@Host")); - } - } - - // EventB - final Event evB = new Event("Host", "B", 1); - assertEquals(2, depDetector.getWaitRecommendations(evB, "A@Host").size()); - for (final WaitRecommendations wr : depDetector.getWaitRecommendations(evB, "A@Host")) { - assertTrue(wr.getExpression().equals("C@Host") || wr.getExpression().equals("D@Host")); - if (wr.getExpression().equals("C@Host")) { - assertEquals(1, wr.getRecommendations().size()); - assertTrue(wr.getRecommendations().contains("A@Host")); - } - if (wr.getExpression().equals("D@Host")) { - assertEquals(1, wr.getRecommendations().size()); - assertTrue(wr.getRecommendations().contains("C@Host")); - } - } - - // EventC - final Event evC = new Event("Host", "C", 1); - assertEquals(1, depDetector.getWaitRecommendations(evC, "A@Host").size()); - for (final WaitRecommendations wr : depDetector.getWaitRecommendations(evC, "A@Host")) { - assertTrue(wr.getExpression().equals("D@Host")); - assertEquals(1, wr.getRecommendations().size()); - assertTrue(wr.getRecommendations().contains("B@Host")); - } - - // EventD - final Event evD = new Event("Host", "D", 1); - assertEquals(1, depDetector.getWaitRecommendations(evD, "A@Host").size()); - for (final WaitRecommendations wr : depDetector.getWaitRecommendations(evD, "A@Host")) { - assertTrue(wr.getExpression().equals("E@Host")); - assertEquals(1, wr.getRecommendations().size()); - assertTrue(wr.getRecommendations().contains("A@Host")); - } - - // EventE - final Event evE = new Event("Host", "E", 1); - assertTrue(depDetector.getWaitRecommendations(evE, "A@Host").isEmpty()); - } + @Test + public void noDependencyTest() { + // B = f(A) + // D = f(B, C) + final DependencyGraph graph = DependencyGraph.instance; + final IntraSourceDependencyDetector depDetector = IntraSourceDependencyDetector.instance; + graph.clear(); + + final Subscription subA = new Subscription("Host", "A"); + final Subscription subB = new Subscription("Host", "B"); + final Subscription subC = new Subscription("Host", "C"); + + final Advertisement advA = new Advertisement("Host", "A"); + final Advertisement advB = new Advertisement("Host", "B"); + final Advertisement advC = new Advertisement("Host", "C"); + final Advertisement advD = new Advertisement("Host", "D"); + + // Subscription to A (A generates B) + final Set subsB = new HashSet<>(); + subsB.add(subA); + graph.processAdv(advB, subsB); + + // Subscription to B and C (B, C generate D) + final Set subsD = new HashSet<>(); + subsD.add(subB); + subsD.add(subC); + graph.processAdv(advD, subsD); + + graph.processAdv(advA); + graph.processAdv(advC); + + // Consolidate + depDetector.consolidate(); + + // EventA + final Event evA = new Event<>("Host", "A", 1); + assertEquals(depDetector.getWaitRecommendations(evA, "A@Host").size(), 0); + + // EventB + final Event evB = new Event<>("Host", "B", 1); + assertEquals(depDetector.getWaitRecommendations(evB, "A@Host").size(), 0); + + // EventC + final Event evC = new Event<>("Host", "C", 1); + assertEquals(depDetector.getWaitRecommendations(evC, "A@Host").size(), 0); + + // EventD + final Event evD = new Event<>("Host", "D", 1); + assertEquals(depDetector.getWaitRecommendations(evD, "A@Host").size(), 0); + } + + @Test + public void basicTriangularCycleTest() { + // B = f(A) + // C = f(A, B) + final DependencyGraph graph = DependencyGraph.instance; + final IntraSourceDependencyDetector depDetector = IntraSourceDependencyDetector.instance; + graph.clear(); + + final Subscription subA = new Subscription("Host", "A"); + final Subscription subB = new Subscription("Host", "B"); + + final Advertisement advA = new Advertisement("Host", "A"); + final Advertisement advB = new Advertisement("Host", "B"); + final Advertisement advC = new Advertisement("Host", "C"); + + // Subscription to A (A generates B) + final Set subsB = new HashSet<>(); + subsB.add(subA); + graph.processAdv(advB, subsB); + + // Subscription to A, B (A, B generates C) + final Set subsC = new HashSet<>(); + subsC.add(subA); + subsC.add(subB); + graph.processAdv(advC, subsC); + + graph.processAdv(advA); + + // Consolidate + depDetector.consolidate(); + + // EventA + final Event evA = new Event("Host", "A", 1); + assertEquals(depDetector.getWaitRecommendations(evA, "").size(), 0); + final Event evA2 = new Event("Host", "A", 1); + assertEquals(depDetector.getWaitRecommendations(evA2, "A@Host").size(), 1); + for (final WaitRecommendations wr : depDetector.getWaitRecommendations(evA2, "A@Host")) { + assertTrue(wr.getExpression().equals("C@Host")); + assertEquals(wr.getRecommendations().size(), 1); + assertTrue(wr.getRecommendations().contains("B@Host")); + } + + // EventB + final Event evB1 = new Event("Host", "B", 1); + assertEquals(depDetector.getWaitRecommendations(evB1, "").size(), 0); + final Event evB2 = new Event("Host", "B", 1); + assertEquals(depDetector.getWaitRecommendations(evB2, "A@Host").size(), 1); + for (final WaitRecommendations wr : depDetector.getWaitRecommendations(evB2, "A@Host")) { + assertTrue(wr.getExpression().equals("C@Host")); + assertEquals(wr.getRecommendations().size(), 1); + assertTrue(wr.getRecommendations().contains("A@Host")); + } + + // EventC + final Event evC = new Event("Host", "C", 1); + assertEquals(depDetector.getWaitRecommendations(evC, "").size(), 0); + } + + @Test + public void basicDualCycleTest() { + // B = f(A) + // C = f(A) + // D = f(B, C) + final DependencyGraph graph = DependencyGraph.instance; + final IntraSourceDependencyDetector depDetector = IntraSourceDependencyDetector.instance; + graph.clear(); + + final Subscription subA = new Subscription("Host", "A"); + final Subscription subB = new Subscription("Host", "B"); + final Subscription subC = new Subscription("Host", "C"); + + final Advertisement advA = new Advertisement("Host", "A"); + final Advertisement advB = new Advertisement("Host", "B"); + final Advertisement advC = new Advertisement("Host", "C"); + final Advertisement advD = new Advertisement("Host", "D"); + + // Subscription to A (A generates B) + final Set subsB = new HashSet(); + subsB.add(subA); + graph.processAdv(advB, subsB); + + // Subscription to A (A generates C) + final Set subsC = new HashSet(); + subsC.add(subA); + graph.processAdv(advC, subsC); + + // Subscription to B, C (B, C generate D) + final Set subsD = new HashSet(); + subsD.add(subB); + subsD.add(subC); + graph.processAdv(advD, subsD); + + graph.processAdv(advA); + + // Consolidate + depDetector.consolidate(); + + // EventA + final Event evA = new Event("Host", "A", 1); + assertEquals(depDetector.getWaitRecommendations(evA, "").size(), 0); + + // EventB + final Event evB1 = new Event("Host", "B", 1); + assertEquals(depDetector.getWaitRecommendations(evB1, "").size(), 0); + final Event evB2 = new Event("Host", "B", 1); + assertEquals(depDetector.getWaitRecommendations(evB2, "A@Host").size(), 1); + for (final WaitRecommendations wr : depDetector.getWaitRecommendations(evB2, "A@Host")) { + assertTrue(wr.getExpression().equals("D@Host")); + assertEquals(wr.getRecommendations().size(), 1); + assertTrue(wr.getRecommendations().contains("C@Host")); + } + + // EventC + final Event evC1 = new Event("Host", "C", 1); + assertEquals(depDetector.getWaitRecommendations(evC1, "").size(), 0); + final Event evC2 = new Event("Host", "C", 1); + assertEquals(depDetector.getWaitRecommendations(evC2, "A@Host").size(), 1); + for (final WaitRecommendations wr : depDetector.getWaitRecommendations(evC2, "A@Host")) { + assertTrue(wr.getExpression().equals("D@Host")); + assertEquals(wr.getRecommendations().size(), 1); + assertTrue(wr.getRecommendations().contains("B@Host")); + } + + // EventD + final Event evD = new Event("Host", "D", 1); + assertEquals(depDetector.getWaitRecommendations(evD, "").size(), 0); + } + + @Test + public void basicDualCycleTest2() { + // B = f(A) + // C = f(A) + // D = f(C) + // E = f(B, D) + final DependencyGraph graph = DependencyGraph.instance; + final IntraSourceDependencyDetector depDetector = IntraSourceDependencyDetector.instance; + graph.clear(); + + final Subscription subA = new Subscription("Host", "A"); + final Subscription subB = new Subscription("Host", "B"); + final Subscription subC = new Subscription("Host", "C"); + final Subscription subD = new Subscription("Host", "D"); + + final Advertisement advA = new Advertisement("Host", "A"); + final Advertisement advB = new Advertisement("Host", "B"); + final Advertisement advC = new Advertisement("Host", "C"); + final Advertisement advD = new Advertisement("Host", "D"); + final Advertisement advE = new Advertisement("Host", "E"); + + graph.processAdv(advA); + + // Subscription to A (A generates B) + final Set subsB = new HashSet(); + subsB.add(subA); + graph.processAdv(advB, subsB); + + // Subscription to A (A generates C) + final Set subsC = new HashSet(); + subsC.add(subA); + graph.processAdv(advC, subsC); + + // Subscription to C (C generate D) + final Set subsD = new HashSet(); + subsD.add(subC); + graph.processAdv(advD, subsD); + + // Subscription to B, D (B, D generate E) + final Set subsE = new HashSet(); + subsE.add(subB); + subsE.add(subD); + graph.processAdv(advE, subsE); + + // Consolidate + depDetector.consolidate(); + + // EventA + final Event evA = new Event("Host", "A", 1); + assertEquals(depDetector.getWaitRecommendations(evA, "").size(), 0); + + // EventB + final Event evB1 = new Event("Host", "B", 1); + assertEquals(depDetector.getWaitRecommendations(evB1, "").size(), 0); + final Event evB2 = new Event("Host", "B", 1); + assertEquals(depDetector.getWaitRecommendations(evB2, "A@Host").size(), 1); + for (final WaitRecommendations wr : depDetector.getWaitRecommendations(evB2, "A@Host")) { + assertTrue(wr.getExpression().equals("E@Host")); + assertEquals(wr.getRecommendations().size(), 1); + assertTrue(wr.getRecommendations().contains("D@Host")); + } + + // EventC + final Event evC1 = new Event("Host", "C", 1); + assertEquals(depDetector.getWaitRecommendations(evC1, "").size(), 0); + final Event evC2 = new Event("Host", "C", 1); + assertEquals(depDetector.getWaitRecommendations(evC2, "A@Host").size(), 0); + + // EventD + final Event evD1 = new Event("Host", "D", 1); + assertEquals(depDetector.getWaitRecommendations(evD1, "").size(), 0); + final Event evD2 = new Event("Host", "D", 1); + assertEquals(depDetector.getWaitRecommendations(evD2, "A@Host").size(), 1); + for (final WaitRecommendations wr : depDetector.getWaitRecommendations(evD2, "A@Host")) { + assertTrue(wr.getExpression().equals("E@Host")); + assertEquals(wr.getRecommendations().size(), 1); + assertTrue(wr.getRecommendations().contains("B@Host")); + } + + // EventE + final Event evE = new Event("E", "Host", 1); + assertEquals(depDetector.getWaitRecommendations(evE, "").size(), 0); + } + + @Test + public void basicDualTriangle() { + // B = f(A) + // C = f(B) + // D = f(B, C) + final DependencyGraph graph = DependencyGraph.instance; + final IntraSourceDependencyDetector depDetector = IntraSourceDependencyDetector.instance; + graph.clear(); + + final Subscription subA = new Subscription("Host", "A"); + final Subscription subB = new Subscription("Host", "B"); + final Subscription subC = new Subscription("Host", "C"); + + final Advertisement advA = new Advertisement("Host", "A"); + final Advertisement advB = new Advertisement("Host", "B"); + final Advertisement advC = new Advertisement("Host", "C"); + final Advertisement advD = new Advertisement("Host", "D"); + + // Subscription to A (A generates B) + final Set subsB = new HashSet(); + subsB.add(subA); + graph.processAdv(advB, subsB); + + // Subscription to B (B generates C) + final Set subsC = new HashSet(); + subsC.add(subB); + graph.processAdv(advC, subsC); + + // Subscription to B, C (B, C generate D) + final Set subsD = new HashSet(); + subsD.add(subB); + subsD.add(subC); + graph.processAdv(advD, subsD); + + graph.processAdv(advA); + + // Consolidate + depDetector.consolidate(); + + // EventA + final Event evA = new Event("Host", "A", 1); + assertEquals(depDetector.getWaitRecommendations(evA, "").size(), 0); + + // EventB + final Event evB1 = new Event("Host", "B", 1); + assertEquals(depDetector.getWaitRecommendations(evB1, "").size(), 0); + final Event evB2 = new Event("Host", "B", 1); + assertEquals(depDetector.getWaitRecommendations(evB2, "A@Host").size(), 1); + for (final WaitRecommendations wr : depDetector.getWaitRecommendations(evB2, "A@Host")) { + assertTrue(wr.getExpression().equals("D@Host")); + assertEquals(wr.getRecommendations().size(), 1); + assertTrue(wr.getRecommendations().contains("C@Host")); + } + + // EventC + final Event evC1 = new Event("Host", "C", 1); + assertEquals(depDetector.getWaitRecommendations(evC1, "").size(), 0); + final Event evC2 = new Event("Host", "C", 1); + assertEquals(depDetector.getWaitRecommendations(evC2, "A@Host").size(), 1); + for (final WaitRecommendations wr : depDetector.getWaitRecommendations(evC2, "A@Host")) { + assertTrue(wr.getExpression().equals("D@Host")); + assertEquals(wr.getRecommendations().size(), 1); + assertTrue(wr.getRecommendations().contains("B@Host")); + } + + // EventD + final Event evD1 = new Event("Host", "D", 1); + assertEquals(depDetector.getWaitRecommendations(evD1, "").size(), 0); + final Event evD2 = new Event("Host", "D", 1); + assertEquals(depDetector.getWaitRecommendations(evD2, "A@Host").size(), 0); + } + + @Test + public void basicTripleCycleTest() { + // B = f(A) + // C = f(A) + // D = f(A) + // E = f(B, C, D) + final DependencyGraph graph = DependencyGraph.instance; + final IntraSourceDependencyDetector depDetector = IntraSourceDependencyDetector.instance; + graph.clear(); + + final Subscription subA = new Subscription("Host", "A"); + final Subscription subB = new Subscription("Host", "B"); + final Subscription subC = new Subscription("Host", "C"); + final Subscription subD = new Subscription("Host", "D"); + + final Advertisement advA = new Advertisement("Host", "A"); + final Advertisement advB = new Advertisement("Host", "B"); + final Advertisement advC = new Advertisement("Host", "C"); + final Advertisement advD = new Advertisement("Host", "D"); + final Advertisement advE = new Advertisement("Host", "E"); + + graph.processAdv(advA); + + // Subscription to A (A generates B) + final Set subsB = new HashSet(); + subsB.add(subA); + graph.processAdv(advB, subsB); + + // Subscription to A (A generates C) + final Set subsC = new HashSet(); + subsC.add(subA); + graph.processAdv(advC, subsC); + + // Subscription to A (A generates D) + final Set subsD = new HashSet(); + subsD.add(subA); + graph.processAdv(advD, subsD); + + // Subscription to B, C, D (B, C, D generate E) + final Set subsE = new HashSet(); + subsE.add(subB); + subsE.add(subC); + subsE.add(subD); + graph.processAdv(advE, subsE); + + // Consolidate + depDetector.consolidate(); + + // EventA + final Event evA = new Event("Host", "A", 1); + assertEquals(depDetector.getWaitRecommendations(evA, "").size(), 0); + + // EventB + final Event evB1 = new Event("Host", "B", 1); + assertEquals(depDetector.getWaitRecommendations(evB1, "").size(), 0); + final Event evB2 = new Event("Host", "B", 1); + assertEquals(depDetector.getWaitRecommendations(evB2, "A@Host").size(), 1); + for (final WaitRecommendations wr : depDetector.getWaitRecommendations(evB2, "A@Host")) { + assertTrue(wr.getExpression().equals("E@Host")); + assertEquals(wr.getRecommendations().size(), 2); + assertTrue(wr.getRecommendations().contains("C@Host")); + assertTrue(wr.getRecommendations().contains("D@Host")); + } + + // EventC + final Event evC1 = new Event("Host", "C", 1); + assertEquals(depDetector.getWaitRecommendations(evC1, "").size(), 0); + final Event evC2 = new Event("Host", "C", 1); + assertEquals(depDetector.getWaitRecommendations(evC2, "A@Host").size(), 1); + for (final WaitRecommendations wr : depDetector.getWaitRecommendations(evC2, "A@Host")) { + assertTrue(wr.getExpression().equals("E@Host")); + assertEquals(wr.getRecommendations().size(), 2); + assertTrue(wr.getRecommendations().contains("B@Host")); + assertTrue(wr.getRecommendations().contains("D@Host")); + } + + // EventD + final Event evD1 = new Event("Host", "D", 1); + assertEquals(depDetector.getWaitRecommendations(evD1, "").size(), 0); + final Event evD2 = new Event("Host", "D", 1); + assertEquals(depDetector.getWaitRecommendations(evD2, "A@Host").size(), 1); + for (final WaitRecommendations wr : depDetector.getWaitRecommendations(evD2, "A@Host")) { + assertTrue(wr.getExpression().equals("E@Host")); + assertEquals(wr.getRecommendations().size(), 2); + assertTrue(wr.getRecommendations().contains("B@Host")); + assertTrue(wr.getRecommendations().contains("C@Host")); + } + + // EventE + final Event evE = new Event("Host", "E", 1); + assertEquals(depDetector.getWaitRecommendations(evE, "A@Host").size(), 0); + } + + @Test + public void dualCyclesTest() { + // B1 = f(A1) + // C1 = f(A1) + // B2 = f(A2) + // C2 = f(A2) + // D = f(B1, C1, B2, C2) + final DependencyGraph graph = DependencyGraph.instance; + final IntraSourceDependencyDetector depDetector = IntraSourceDependencyDetector.instance; + graph.clear(); + + final Subscription subA1 = new Subscription("Host", "A1"); + final Subscription subA2 = new Subscription("Host", "A2"); + final Subscription subB1 = new Subscription("Host", "B1"); + final Subscription subB2 = new Subscription("Host", "B2"); + final Subscription subC1 = new Subscription("Host", "C1"); + final Subscription subC2 = new Subscription("Host", "C2"); + + final Advertisement advA1 = new Advertisement("Host", "A1"); + final Advertisement advA2 = new Advertisement("Host", "A2"); + final Advertisement advB1 = new Advertisement("Host", "B1"); + final Advertisement advB2 = new Advertisement("Host", "B2"); + final Advertisement advC1 = new Advertisement("Host", "C1"); + final Advertisement advC2 = new Advertisement("Host", "C2"); + final Advertisement advD = new Advertisement("Host", "D"); + + // Subscription to A1 (A1 generates B2) + final Set subsB1 = new HashSet(); + subsB1.add(subA1); + graph.processAdv(advB1, subsB1); + + // Subscription to A2 (A2 generates B2) + final Set subsB2 = new HashSet(); + subsB2.add(subA2); + graph.processAdv(advB2, subsB2); + + // Subscription to A1 (A1 generates C1) + final Set subsC1 = new HashSet(); + subsC1.add(subA1); + graph.processAdv(advC1, subsC1); + + // Subscription to A2 (A2 generates C2) + final Set subsC2 = new HashSet(); + subsC2.add(subA2); + graph.processAdv(advC2, subsC2); + + // Subscription to B1, B2, C1, C2 (B1, B2, C1, C2 generate D) + final Set subsD = new HashSet(); + subsD.add(subB1); + subsD.add(subB2); + subsD.add(subC1); + subsD.add(subC2); + graph.processAdv(advD, subsD); + + graph.processAdv(advA1); + graph.processAdv(advA2); + + // Consolidate + depDetector.consolidate(); + + // EventA1 + final Event evA1 = new Event("Host", "A1", 1); + assertEquals(depDetector.getWaitRecommendations(evA1, "A1@Host").size(), 0); + + // EventA2 + final Event evA2 = new Event("Host", "A2", 1); + assertEquals(depDetector.getWaitRecommendations(evA2, "A2@Host").size(), 0); + + // EventB1 + final Event evB1 = new Event("Host", "B1", 1); + assertEquals(depDetector.getWaitRecommendations(evB1, "A1@Host").size(), 1); + for (final WaitRecommendations wr : depDetector.getWaitRecommendations(evB1, "A1@Host")) { + assertTrue(wr.getExpression().equals("D@Host")); + assertEquals(wr.getRecommendations().size(), 1); + assertTrue(wr.getRecommendations().contains("C1@Host")); + } + + // EventC1 + final Event evC1 = new Event("Host", "C1", 1); + assertEquals(depDetector.getWaitRecommendations(evC1, "A1@Host").size(), 1); + for (final WaitRecommendations wr : depDetector.getWaitRecommendations(evC1, "A1@Host")) { + assertTrue(wr.getExpression().equals("D@Host")); + assertEquals(wr.getRecommendations().size(), 1); + assertTrue(wr.getRecommendations().contains("B1@Host")); + } + + // EventB2 + final Event evB2 = new Event("Host", "B2", 1); + assertEquals(depDetector.getWaitRecommendations(evB2, "A2@Host").size(), 1); + for (final WaitRecommendations wr : depDetector.getWaitRecommendations(evB2, "A2@Host")) { + assertTrue(wr.getExpression().equals("D@Host")); + assertEquals(wr.getRecommendations().size(), 1); + assertTrue(wr.getRecommendations().contains("C2@Host")); + } + + // EventC2 + final Event evC2 = new Event("Host", "C2", 1); + assertEquals(depDetector.getWaitRecommendations(evC2, "A2@Host").size(), 1); + for (final WaitRecommendations wr : depDetector.getWaitRecommendations(evC2, "A2@Host")) { + assertTrue(wr.getExpression().equals("D@Host")); + assertEquals(wr.getRecommendations().size(), 1); + assertTrue(wr.getRecommendations().contains("B2@Host")); + } + + // EventD + final Event evD = new Event("Host", "D", 1); + assertEquals(depDetector.getWaitRecommendations(evD, "A2@Host").size(), 0); + } + + @Test + public void dualDependencyTest() { + // B = f(A1) + // C = f(A1, A2) + // D = f(B, C) + final DependencyGraph graph = DependencyGraph.instance; + final IntraSourceDependencyDetector depDetector = IntraSourceDependencyDetector.instance; + graph.clear(); + + final Subscription subA1 = new Subscription("Host", "A1"); + final Subscription subA2 = new Subscription("Host", "A2"); + final Subscription subB = new Subscription("Host", "B"); + final Subscription subC = new Subscription("Host", "C"); + + final Advertisement advA1 = new Advertisement("Host", "A1"); + final Advertisement advA2 = new Advertisement("Host", "A2"); + final Advertisement advB = new Advertisement("Host", "B"); + final Advertisement advC = new Advertisement("Host", "C"); + final Advertisement advD = new Advertisement("Host", "D"); + + graph.processAdv(advA1); + graph.processAdv(advA2); + + // Subscription to A1 (A1 generates B) + final Set subsB = new HashSet(); + subsB.add(subA1); + graph.processAdv(advB, subsB); + + // Subscription to A1, A2 (A1, A2 generate C) + final Set subsC = new HashSet(); + subsC.add(subA1); + subsC.add(subA2); + graph.processAdv(advC, subsC); + + // Subscription to D (B, C generate D) + final Set subsD = new HashSet(); + subsD.add(subB); + subsD.add(subC); + graph.processAdv(advD, subsD); + + // Consolidate + depDetector.consolidate(); + + // EventA1 + final Event evA1 = new Event("Host", "A1", 1); + assertEquals(depDetector.getWaitRecommendations(evA1, "A1@Host").size(), 0); + + // EventA2 + final Event evA2 = new Event("Host", "A2", 1); + assertEquals(depDetector.getWaitRecommendations(evA2, "A2@Host").size(), 0); + + // EventB from A1 + final Event evB1 = new Event("Host", "B", 1); + assertEquals(depDetector.getWaitRecommendations(evB1, "").size(), 0); + final Event evB2 = new Event("Host", "B", 1); + assertEquals(depDetector.getWaitRecommendations(evB2, "A1@Host").size(), 1); + for (final WaitRecommendations wr : depDetector.getWaitRecommendations(evB2, "A1@Host")) { + assertTrue(wr.getExpression().equals("D@Host")); + assertEquals(wr.getRecommendations().size(), 1); + assertTrue(wr.getRecommendations().contains("C@Host")); + } + + // EventC from A1 + final Event evC1_1 = new Event("Host", "C", 1); + assertEquals(depDetector.getWaitRecommendations(evC1_1, "").size(), 0); + final Event evC1_2 = new Event("Host", "C", 1); + assertEquals(depDetector.getWaitRecommendations(evC1_2, "A1@Host").size(), 1); + for (final WaitRecommendations wr : depDetector.getWaitRecommendations(evC1_2, "A1@Host")) { + assertTrue(wr.getExpression().equals("D@Host")); + assertEquals(wr.getRecommendations().size(), 1); + assertTrue(wr.getRecommendations().contains("B@Host")); + } + + // EventC from A2 + final Event evC2_1 = new Event("Host", "C", 1); + assertEquals(depDetector.getWaitRecommendations(evC2_1, "").size(), 0); + final Event evC2_2 = new Event("Host", "C", 1); + assertEquals(depDetector.getWaitRecommendations(evC2_2, "A2@Host").size(), 0); + + // EventD + final Event evD = new Event("Host", "D", 1); + assertEquals(depDetector.getWaitRecommendations(evD, "A2@Host").size(), 0); + } + + @Test + public void nestedCyclesTest() { + // B = f(A) + // C = f(A) + // E = f(A) + // G = f(E) + // F = f(E) + // H = f(F, G) + // D = f(B, C, H) + final DependencyGraph graph = DependencyGraph.instance; + final IntraSourceDependencyDetector depDetector = IntraSourceDependencyDetector.instance; + graph.clear(); + + final Subscription subA = new Subscription("Host", "A"); + final Subscription subB = new Subscription("Host", "B"); + final Subscription subC = new Subscription("Host", "C"); + final Subscription subE = new Subscription("Host", "E"); + final Subscription subF = new Subscription("Host", "F"); + final Subscription subG = new Subscription("Host", "G"); + final Subscription subH = new Subscription("Host", "H"); + + final Advertisement advA = new Advertisement("Host", "A"); + final Advertisement advB = new Advertisement("Host", "B"); + final Advertisement advC = new Advertisement("Host", "C"); + final Advertisement advD = new Advertisement("Host", "D"); + final Advertisement advE = new Advertisement("Host", "E"); + final Advertisement advF = new Advertisement("Host", "F"); + final Advertisement advG = new Advertisement("Host", "G"); + final Advertisement advH = new Advertisement("Host", "H"); + + // Subscription to A (A generates B) + final Set subsB = new HashSet(); + subsB.add(subA); + graph.processAdv(advB, subsB); + + // Subscription to A (A generates C) + final Set subsC = new HashSet(); + subsC.add(subA); + graph.processAdv(advC, subsC); + + // Subscription to A (A generates E) + final Set subsE = new HashSet(); + subsE.add(subA); + graph.processAdv(advE, subsE); + + // Subscription to E (E generates G) + final Set subsG = new HashSet(); + subsG.add(subE); + graph.processAdv(advG, subsG); + + // Subscription to E (E generates F) + final Set subsF = new HashSet(); + subsF.add(subE); + graph.processAdv(advF, subsF); + + // Subscription to F, G (F, G generate H) + final Set subsH = new HashSet(); + subsH.add(subF); + subsH.add(subG); + graph.processAdv(advH, subsH); + + // Subscription to B, C, H (B, C, H generate D) + final Set subsD = new HashSet(); + subsD.add(subB); + subsD.add(subC); + subsD.add(subH); + graph.processAdv(advD, subsD); + + graph.processAdv(advA); + + // Consolidate + depDetector.consolidate(); + + // EventA + final Event evA = new Event("Host", "A", 1); + assertEquals(depDetector.getWaitRecommendations(evA, "A@Host").size(), 0); + + // EventB + final Event evB1 = new Event("Host", "B", 1); + assertEquals(depDetector.getWaitRecommendations(evB1, "").size(), 0); + final Event evB2 = new Event("Host", "B", 1); + assertEquals(depDetector.getWaitRecommendations(evB2, "A@Host").size(), 1); + for (final WaitRecommendations wr : depDetector.getWaitRecommendations(evB2, "A@Host")) { + assertTrue(wr.getExpression().equals("D@Host")); + assertEquals(wr.getRecommendations().size(), 2); + assertTrue(wr.getRecommendations().contains("C@Host")); + assertTrue(wr.getRecommendations().contains("H@Host")); + } + + // EventC + final Event evC1 = new Event("Host", "C", 1); + assertEquals(depDetector.getWaitRecommendations(evC1, "").size(), 0); + final Event evC2 = new Event("Host", "C", 1); + assertEquals(depDetector.getWaitRecommendations(evC2, "A@Host").size(), 1); + for (final WaitRecommendations wr : depDetector.getWaitRecommendations(evC2, "A@Host")) { + assertTrue(wr.getExpression().equals("D@Host")); + assertEquals(wr.getRecommendations().size(), 2); + assertTrue(wr.getRecommendations().contains("B@Host")); + assertTrue(wr.getRecommendations().contains("H@Host")); + } + + // EventE + final Event evE = new Event("Host", "E", 1); + assertEquals(depDetector.getWaitRecommendations(evE, "A@Host").size(), 0); + + // EventF + final Event evF1 = new Event("Host", "F", 1); + assertEquals(depDetector.getWaitRecommendations(evF1, "").size(), 0); + final Event evF2 = new Event("Host", "F", 1); + assertEquals(depDetector.getWaitRecommendations(evF2, "A@Host").size(), 1); + for (final WaitRecommendations wr : depDetector.getWaitRecommendations(evF2, "A@Host")) { + assertTrue(wr.getExpression().equals("H@Host")); + assertEquals(wr.getRecommendations().size(), 1); + assertTrue(wr.getRecommendations().contains("G@Host")); + } + + // EventG + final Event evG1 = new Event("Host", "G", 1); + assertEquals(depDetector.getWaitRecommendations(evG1, "").size(), 0); + final Event evG2 = new Event("Host", "G", 1); + assertEquals(depDetector.getWaitRecommendations(evG2, "A@Host").size(), 1); + for (final WaitRecommendations wr : depDetector.getWaitRecommendations(evG2, "A@Host")) { + assertTrue(wr.getExpression().equals("H@Host")); + assertEquals(wr.getRecommendations().size(), 1); + assertTrue(wr.getRecommendations().contains("F@Host")); + } + + // EventH + final Event evH1 = new Event("Host", "H", 1); + assertEquals(depDetector.getWaitRecommendations(evH1, "").size(), 0); + final Event evH2 = new Event("Host", "H", 1); + assertEquals(depDetector.getWaitRecommendations(evH2, "A@Host").size(), 1); + for (final WaitRecommendations wr : depDetector.getWaitRecommendations(evH2, "A@Host")) { + assertTrue(wr.getExpression().equals("D@Host")); + assertEquals(wr.getRecommendations().size(), 2); + assertTrue(wr.getRecommendations().contains("B@Host")); + assertTrue(wr.getRecommendations().contains("C@Host")); + } + + // EventD + final Event evD = new Event("Host", "D", 1); + assertEquals(depDetector.getWaitRecommendations(evD, "A@Host").size(), 0); + } + + @Test + public void multipleCyclesTest() { + // B = f(A) + // C = f(A, B) + // D = f(B, C) + // E = f(A, D) + final DependencyGraph graph = DependencyGraph.instance; + final IntraSourceDependencyDetector depDetector = IntraSourceDependencyDetector.instance; + graph.clear(); + + final Subscription subA = new Subscription("Host", "A"); + final Subscription subB = new Subscription("Host", "B"); + final Subscription subC = new Subscription("Host", "C"); + final Subscription subD = new Subscription("Host", "D"); + + final Advertisement advA = new Advertisement("Host", "A"); + final Advertisement advB = new Advertisement("Host", "B"); + final Advertisement advC = new Advertisement("Host", "C"); + final Advertisement advD = new Advertisement("Host", "D"); + final Advertisement advE = new Advertisement("Host", "E"); + + // Subscription to A (A generates B) + final Set subsB = new HashSet(); + subsB.add(subA); + graph.processAdv(advB, subsB); + + // Subscription to A, B (A, B generate C) + final Set subsC = new HashSet(); + subsC.add(subA); + subsC.add(subB); + graph.processAdv(advC, subsC); + + // Subscription to B, C (B, C generate D) + final Set subsD = new HashSet(); + subsD.add(subB); + subsD.add(subC); + graph.processAdv(advD, subsD); + + // Subscription to A, D (A, D generate E) + final Set subsE = new HashSet(); + subsE.add(subA); + subsE.add(subD); + graph.processAdv(advE, subsE); + + graph.processAdv(advA); + + // Consolidate + depDetector.consolidate(); + + // EventA + final Event evA = new Event("Host", "A", 1); + assertEquals(2, depDetector.getWaitRecommendations(evA, "A@Host").size()); + for (final WaitRecommendations wr : depDetector.getWaitRecommendations(evA, "A@Host")) { + assertTrue(wr.getExpression().equals("C@Host") || wr.getExpression().equals("E@Host")); + if (wr.getExpression().equals("C@Host")) { + assertEquals(1, wr.getRecommendations().size()); + assertTrue(wr.getRecommendations().contains("B@Host")); + } + if (wr.getExpression().equals("E@Host")) { + assertEquals(1, wr.getRecommendations().size()); + assertTrue(wr.getRecommendations().contains("D@Host")); + } + } + + // EventB + final Event evB = new Event("Host", "B", 1); + assertEquals(2, depDetector.getWaitRecommendations(evB, "A@Host").size()); + for (final WaitRecommendations wr : depDetector.getWaitRecommendations(evB, "A@Host")) { + assertTrue(wr.getExpression().equals("C@Host") || wr.getExpression().equals("D@Host")); + if (wr.getExpression().equals("C@Host")) { + assertEquals(1, wr.getRecommendations().size()); + assertTrue(wr.getRecommendations().contains("A@Host")); + } + if (wr.getExpression().equals("D@Host")) { + assertEquals(1, wr.getRecommendations().size()); + assertTrue(wr.getRecommendations().contains("C@Host")); + } + } + + // EventC + final Event evC = new Event("Host", "C", 1); + assertEquals(1, depDetector.getWaitRecommendations(evC, "A@Host").size()); + for (final WaitRecommendations wr : depDetector.getWaitRecommendations(evC, "A@Host")) { + assertTrue(wr.getExpression().equals("D@Host")); + assertEquals(1, wr.getRecommendations().size()); + assertTrue(wr.getRecommendations().contains("B@Host")); + } + + // EventD + final Event evD = new Event("Host", "D", 1); + assertEquals(1, depDetector.getWaitRecommendations(evD, "A@Host").size()); + for (final WaitRecommendations wr : depDetector.getWaitRecommendations(evD, "A@Host")) { + assertTrue(wr.getExpression().equals("E@Host")); + assertEquals(1, wr.getRecommendations().size()); + assertTrue(wr.getRecommendations().contains("A@Host")); + } + + // EventE + final Event evE = new Event("Host", "E", 1); + assertTrue(depDetector.getWaitRecommendations(evE, "A@Host").isEmpty()); + } } \ No newline at end of file diff --git a/Dream2/src/test/java/dream/locking/LockManagerTest.java b/Dream2/src/test/java/dream/locking/LockManagerTest.java index ffc8548..c00305e 100644 --- a/Dream2/src/test/java/dream/locking/LockManagerTest.java +++ b/Dream2/src/test/java/dream/locking/LockManagerTest.java @@ -17,119 +17,129 @@ public class LockManagerTest { - @Test - public void test1() { - final LockManager manager = new LockManager(); - - final Set lockNodes = new HashSet<>(); - lockNodes.add("A"); - lockNodes.add("B"); - - final Set unlockNodes = new HashSet<>(); - unlockNodes.add("A"); - unlockNodes.add("B"); - - final LockRequestPacket req1 = new LockRequestPacket(new NodeDescriptor(true), lockNodes, unlockNodes, LockType.READ_WRITE); - assertTrue(manager.processLockRequest(req1)); - - final LockRequestPacket req2 = new LockRequestPacket(new NodeDescriptor(true), lockNodes, unlockNodes, LockType.READ_WRITE); - assertFalse(manager.processLockRequest(req2)); - - final UUID reqID1 = req1.getLockID(); - final LockReleasePacket rel1 = new LockReleasePacket(reqID1); - Set result = manager.processLockRelease(rel1); - assertTrue(result.isEmpty()); - - final LockRequestPacket req3 = new LockRequestPacket(new NodeDescriptor(true), lockNodes, unlockNodes, LockType.READ_WRITE); - assertFalse(manager.processLockRequest(req3)); - - final LockReleasePacket rel2 = new LockReleasePacket(reqID1); - result = manager.processLockRelease(rel2); - assertEquals(1, result.size()); - assertTrue(result.contains(req2)); - - final UUID reqID2 = req2.getLockID(); - final LockReleasePacket rel3 = new LockReleasePacket(reqID2); - result = manager.processLockRelease(rel3); - assertTrue(result.isEmpty()); - - final LockReleasePacket rel4 = new LockReleasePacket(reqID2); - result = manager.processLockRelease(rel4); - assertEquals(1, result.size()); - assertTrue(result.contains(req3)); - } - - @Test - public void test2() { - final LockManager manager = new LockManager(); - - final Set lockNodesA = new HashSet<>(); - lockNodesA.add("A"); - - final Set lockNodesB = new HashSet<>(); - lockNodesB.add("B"); - - final Set lockNodesAB = new HashSet<>(); - lockNodesAB.add("A"); - lockNodesAB.add("B"); - - final Set unlockNodes = new HashSet<>(); - unlockNodes.add("C"); - - final LockRequestPacket reqAB = new LockRequestPacket(new NodeDescriptor(true), lockNodesAB, unlockNodes, LockType.READ_WRITE); - assertTrue(manager.processLockRequest(reqAB)); - - final LockRequestPacket reqA = new LockRequestPacket(new NodeDescriptor(true), lockNodesA, unlockNodes, LockType.READ_WRITE); - assertFalse(manager.processLockRequest(reqA)); - - final LockRequestPacket reqB = new LockRequestPacket(new NodeDescriptor(true), lockNodesB, unlockNodes, LockType.READ_WRITE); - assertFalse(manager.processLockRequest(reqB)); - - final UUID id = reqAB.getLockID(); - final LockReleasePacket rel = new LockReleasePacket(id); - final Set result = manager.processLockRelease(rel); - assertEquals(2, result.size()); - assertTrue(result.contains(reqA)); - assertTrue(result.contains(reqB)); - } - - @Test - public void test3() { - final LockManager manager = new LockManager(); - - final Set lockNodesA = new HashSet<>(); - lockNodesA.add("A"); - - final Set lockNodesB = new HashSet<>(); - lockNodesB.add("B"); - - final Set lockNodesAB = new HashSet<>(); - lockNodesAB.add("A"); - lockNodesAB.add("B"); - - final Set unlockNodes = new HashSet<>(); - unlockNodes.add("C"); - - final LockRequestPacket reqAB = new LockRequestPacket(new NodeDescriptor(true), lockNodesAB, unlockNodes, LockType.READ_ONLY); - assertTrue(manager.processLockRequest(reqAB)); - - final LockRequestPacket reqA1 = new LockRequestPacket(new NodeDescriptor(true), lockNodesA, unlockNodes, LockType.READ_ONLY); - assertTrue(manager.processLockRequest(reqA1)); - - final LockRequestPacket reqA2 = new LockRequestPacket(new NodeDescriptor(true), lockNodesA, unlockNodes, LockType.READ_ONLY); - assertTrue(manager.processLockRequest(reqA2)); - - final LockRequestPacket reqB = new LockRequestPacket(new NodeDescriptor(true), lockNodesB, unlockNodes, LockType.READ_WRITE); - assertFalse(manager.processLockRequest(reqB)); - - UUID id = reqA1.getLockID(); - final LockReleasePacket relA1 = new LockReleasePacket(id); - assertTrue(manager.processLockRelease(relA1).isEmpty()); - - id = reqAB.getLockID(); - final LockReleasePacket relB = new LockReleasePacket(id); - final Set result = manager.processLockRelease(relB); - assertEquals(1, result.size()); - assertTrue(result.contains(reqB)); - } + @Test + public void test1() { + final LockManager manager = new LockManager(); + + final Set lockNodes = new HashSet<>(); + lockNodes.add("A"); + lockNodes.add("B"); + + final Set unlockNodes = new HashSet<>(); + unlockNodes.add("A"); + unlockNodes.add("B"); + + final LockRequestPacket req1 = new LockRequestPacket(new NodeDescriptor(true), lockNodes, unlockNodes, + LockType.READ_WRITE); + assertTrue(manager.processLockRequest(req1)); + + final LockRequestPacket req2 = new LockRequestPacket(new NodeDescriptor(true), lockNodes, unlockNodes, + LockType.READ_WRITE); + assertFalse(manager.processLockRequest(req2)); + + final UUID reqID1 = req1.getLockID(); + final LockReleasePacket rel1 = new LockReleasePacket(reqID1); + Set result = manager.processLockRelease(rel1); + assertTrue(result.isEmpty()); + + final LockRequestPacket req3 = new LockRequestPacket(new NodeDescriptor(true), lockNodes, unlockNodes, + LockType.READ_WRITE); + assertFalse(manager.processLockRequest(req3)); + + final LockReleasePacket rel2 = new LockReleasePacket(reqID1); + result = manager.processLockRelease(rel2); + assertEquals(1, result.size()); + assertTrue(result.contains(req2)); + + final UUID reqID2 = req2.getLockID(); + final LockReleasePacket rel3 = new LockReleasePacket(reqID2); + result = manager.processLockRelease(rel3); + assertTrue(result.isEmpty()); + + final LockReleasePacket rel4 = new LockReleasePacket(reqID2); + result = manager.processLockRelease(rel4); + assertEquals(1, result.size()); + assertTrue(result.contains(req3)); + } + + @Test + public void test2() { + final LockManager manager = new LockManager(); + + final Set lockNodesA = new HashSet<>(); + lockNodesA.add("A"); + + final Set lockNodesB = new HashSet<>(); + lockNodesB.add("B"); + + final Set lockNodesAB = new HashSet<>(); + lockNodesAB.add("A"); + lockNodesAB.add("B"); + + final Set unlockNodes = new HashSet<>(); + unlockNodes.add("C"); + + final LockRequestPacket reqAB = new LockRequestPacket(new NodeDescriptor(true), lockNodesAB, unlockNodes, + LockType.READ_WRITE); + assertTrue(manager.processLockRequest(reqAB)); + + final LockRequestPacket reqA = new LockRequestPacket(new NodeDescriptor(true), lockNodesA, unlockNodes, + LockType.READ_WRITE); + assertFalse(manager.processLockRequest(reqA)); + + final LockRequestPacket reqB = new LockRequestPacket(new NodeDescriptor(true), lockNodesB, unlockNodes, + LockType.READ_WRITE); + assertFalse(manager.processLockRequest(reqB)); + + final UUID id = reqAB.getLockID(); + final LockReleasePacket rel = new LockReleasePacket(id); + final Set result = manager.processLockRelease(rel); + assertEquals(2, result.size()); + assertTrue(result.contains(reqA)); + assertTrue(result.contains(reqB)); + } + + @Test + public void test3() { + final LockManager manager = new LockManager(); + + final Set lockNodesA = new HashSet<>(); + lockNodesA.add("A"); + + final Set lockNodesB = new HashSet<>(); + lockNodesB.add("B"); + + final Set lockNodesAB = new HashSet<>(); + lockNodesAB.add("A"); + lockNodesAB.add("B"); + + final Set unlockNodes = new HashSet<>(); + unlockNodes.add("C"); + + final LockRequestPacket reqAB = new LockRequestPacket(new NodeDescriptor(true), lockNodesAB, unlockNodes, + LockType.READ_ONLY); + assertTrue(manager.processLockRequest(reqAB)); + + final LockRequestPacket reqA1 = new LockRequestPacket(new NodeDescriptor(true), lockNodesA, unlockNodes, + LockType.READ_ONLY); + assertTrue(manager.processLockRequest(reqA1)); + + final LockRequestPacket reqA2 = new LockRequestPacket(new NodeDescriptor(true), lockNodesA, unlockNodes, + LockType.READ_ONLY); + assertTrue(manager.processLockRequest(reqA2)); + + final LockRequestPacket reqB = new LockRequestPacket(new NodeDescriptor(true), lockNodesB, unlockNodes, + LockType.READ_WRITE); + assertFalse(manager.processLockRequest(reqB)); + + UUID id = reqA1.getLockID(); + final LockReleasePacket relA1 = new LockReleasePacket(id); + assertTrue(manager.processLockRelease(relA1).isEmpty()); + + id = reqAB.getLockID(); + final LockReleasePacket relB = new LockReleasePacket(id); + final Set result = manager.processLockRelease(relB); + assertEquals(1, result.size()); + assertTrue(result.contains(reqB)); + } } From 5bfdb08a4681eaf2363604f511a63cc0f7b02637 Mon Sep 17 00:00:00 2001 From: Tobias Becker Date: Thu, 11 Feb 2016 22:22:23 +0100 Subject: [PATCH 016/161] added a ChangeEvent --- .../examples/financial/FinancialApp.java | 12 +++--- .../java/dream/examples/financial/Model1.java | 11 +---- .../java/dream/examples/financial/Model2.java | 10 +---- .../java/dream/examples/financial/Model3.java | 10 +---- .../java/dream/examples/local/Example1.java | 14 ++++--- .../dream/examples/local/ExampleFilter.java | 4 +- .../dream/examples/local/ExampleGlitch.java | 2 +- .../dream/examples/local/ExampleList.java | 2 +- .../examples/remote/RemoteSignalExample.java | 8 ++-- .../main/java/dream/client/ChangeEvent.java | 41 +++++++++++++++++++ .../java/dream/client/ChangeEventHandler.java | 6 +++ Dream2/src/main/java/dream/client/Signal.java | 20 +++------ .../java/dream/client/TimeChangingValue.java | 20 +++------ .../dream/client/ValueChangeListener.java | 11 ----- 14 files changed, 86 insertions(+), 85 deletions(-) create mode 100644 Dream2/src/main/java/dream/client/ChangeEvent.java create mode 100644 Dream2/src/main/java/dream/client/ChangeEventHandler.java delete mode 100755 Dream2/src/main/java/dream/client/ValueChangeListener.java diff --git a/Dream2/src/examples/java/dream/examples/financial/FinancialApp.java b/Dream2/src/examples/java/dream/examples/financial/FinancialApp.java index 4ddf40a..3817c33 100644 --- a/Dream2/src/examples/java/dream/examples/financial/FinancialApp.java +++ b/Dream2/src/examples/java/dream/examples/financial/FinancialApp.java @@ -1,11 +1,11 @@ package dream.examples.financial; +import dream.client.ChangeEventHandler; import dream.client.RemoteVar; import dream.client.Signal; -import dream.client.ValueChangeListener; import dream.common.Consts; -public class FinancialApp implements ValueChangeListener { +public class FinancialApp implements ChangeEventHandler { private Signal f1Signal; private Signal f2Signal; private Signal f3Signal; @@ -37,9 +37,9 @@ public void start() { f2Signal = new Signal<>("f2Signal", () -> f2.get(), f2); f3Signal = new Signal<>("f3Signal", () -> f3.get(), f3); - f1Signal.addValueChangeListener(this); - f2Signal.addValueChangeListener(this); - f3Signal.addValueChangeListener(this); + f1Signal.change().addHandler(this); + f2Signal.change().addHandler(this); + f3Signal.change().addHandler(this); try { Thread.sleep(2000); @@ -55,7 +55,7 @@ public void start() { } @Override - public void notifyValueChanged(Integer newValue) { + public void handle(Integer oldVal, Integer newVal) { System.out.println("Value changed"); if (f1.get() != null && f2.get() != null && f3.get() != null) { diff --git a/Dream2/src/examples/java/dream/examples/financial/Model1.java b/Dream2/src/examples/java/dream/examples/financial/Model1.java index 6dc26ab..938c90f 100644 --- a/Dream2/src/examples/java/dream/examples/financial/Model1.java +++ b/Dream2/src/examples/java/dream/examples/financial/Model1.java @@ -2,11 +2,9 @@ import dream.client.RemoteVar; import dream.client.Signal; -import dream.client.ValueChangeListener; -import dream.client.Var; import dream.common.Consts; -public class Model1 implements ValueChangeListener { +public class Model1 { public void start() { Consts.hostName = "Model1"; @@ -21,12 +19,7 @@ public void start() { } } , marketIndex, stockOpts); - f1.addValueChangeListener(this); - } - - @Override - public void notifyValueChanged(Integer newValue) { - System.out.println("New value for f1: " + newValue); + f1.change().addHandler((oldVal, newVal) -> System.out.println("New value for f1: " + newVal)); } public static void main(String[] args) { diff --git a/Dream2/src/examples/java/dream/examples/financial/Model2.java b/Dream2/src/examples/java/dream/examples/financial/Model2.java index c49fac6..a5124e9 100644 --- a/Dream2/src/examples/java/dream/examples/financial/Model2.java +++ b/Dream2/src/examples/java/dream/examples/financial/Model2.java @@ -2,10 +2,9 @@ import dream.client.RemoteVar; import dream.client.Signal; -import dream.client.ValueChangeListener; import dream.common.Consts; -public class Model2 implements ValueChangeListener { +public class Model2 { public void start() { Consts.hostName = "Model2"; @@ -20,12 +19,7 @@ public void start() { } } , marketIndex, stockOpts); - f2.addValueChangeListener(this); - } - - @Override - public void notifyValueChanged(Integer newValue) { - System.out.println("New value for f2: " + newValue); + f2.change().addHandler((oldVal, newVal) -> System.out.println("New value for f2: " + newVal)); } public static void main(String[] args) { diff --git a/Dream2/src/examples/java/dream/examples/financial/Model3.java b/Dream2/src/examples/java/dream/examples/financial/Model3.java index 6a06cde..c575ec6 100644 --- a/Dream2/src/examples/java/dream/examples/financial/Model3.java +++ b/Dream2/src/examples/java/dream/examples/financial/Model3.java @@ -2,10 +2,9 @@ import dream.client.RemoteVar; import dream.client.Signal; -import dream.client.ValueChangeListener; import dream.common.Consts; -public class Model3 implements ValueChangeListener { +public class Model3 { public void start() { Consts.hostName = "Model3"; @@ -20,12 +19,7 @@ public void start() { } } , marketIndex, news); - f3.addValueChangeListener(this); - } - - @Override - public void notifyValueChanged(Integer newValue) { - System.out.println("New value for f3: " + newValue); + f3.change().addHandler((oldVal, newVal) -> System.out.println("New value for f3: " + newVal)); } public static void main(String[] args) { diff --git a/Dream2/src/examples/java/dream/examples/local/Example1.java b/Dream2/src/examples/java/dream/examples/local/Example1.java index 21f0d5e..03f0632 100755 --- a/Dream2/src/examples/java/dream/examples/local/Example1.java +++ b/Dream2/src/examples/java/dream/examples/local/Example1.java @@ -20,12 +20,14 @@ public static void main(String args[]) { final Signal signalString = new Signal("signalString", () -> varString1.get() + varString2.get(), varString1, varString2); - signalInt.addValueChangeListener(val -> System.out.println("signalInt: " + val + " (correct value: 158)")); - signalDouble - .addValueChangeListener(val -> System.out.println("signalDouble: " + val + " (correct value: 4.8)")); - signalBool.addValueChangeListener(val -> System.out.println("signalBool: " + val + " (correct value: false)")); - signalString.addValueChangeListener( - val -> System.out.println("signalString: " + val + " (correct value: Hello World!)")); + signalInt.change() + .addHandler((oldVal, val) -> System.out.println("signalInt: " + val + " (correct value: 158)")); + signalDouble.change() + .addHandler((oldVal, val) -> System.out.println("signalDouble: " + val + " (correct value: 4.8)")); + signalBool.change() + .addHandler((oldVal, val) -> System.out.println("signalBool: " + val + " (correct value: false)")); + signalString.change().addHandler( + (oldVal, val) -> System.out.println("signalString: " + val + " (correct value: Hello World!)")); try { Thread.sleep(500); diff --git a/Dream2/src/examples/java/dream/examples/local/ExampleFilter.java b/Dream2/src/examples/java/dream/examples/local/ExampleFilter.java index 30c7464..22292e2 100644 --- a/Dream2/src/examples/java/dream/examples/local/ExampleFilter.java +++ b/Dream2/src/examples/java/dream/examples/local/ExampleFilter.java @@ -12,8 +12,8 @@ public static void main(String args[]) { final Signal signalInt2 = new Signal<>("signalInt2", () -> signalInt.get() + 1, signalInt.filter(val -> val > 20)); - signalInt.addValueChangeListener(val -> System.out.println("SignalInt: " + val)); - signalInt2.addValueChangeListener(val -> System.out.println("SignalInt2: " + val)); + signalInt.change().addHandler((oldVal, val) -> System.out.println("SignalInt: " + val)); + signalInt2.change().addHandler((oldVal, val) -> System.out.println("SignalInt2: " + val)); try { Thread.sleep(500); diff --git a/Dream2/src/examples/java/dream/examples/local/ExampleGlitch.java b/Dream2/src/examples/java/dream/examples/local/ExampleGlitch.java index 0e63610..70fe13b 100755 --- a/Dream2/src/examples/java/dream/examples/local/ExampleGlitch.java +++ b/Dream2/src/examples/java/dream/examples/local/ExampleGlitch.java @@ -17,7 +17,7 @@ public void launch() { final Signal mid2 = new Signal<>("mid2", () -> var.get() * 3, var); final Signal finalResult = new Signal<>("final", () -> mid1.get() + mid2.get(), mid1, mid2); - finalResult.addValueChangeListener(System.out::println); + finalResult.change().addHandler((oldVal, val) -> System.out.println(val)); try { Thread.sleep(500); diff --git a/Dream2/src/examples/java/dream/examples/local/ExampleList.java b/Dream2/src/examples/java/dream/examples/local/ExampleList.java index fa65fc3..4e8aa67 100755 --- a/Dream2/src/examples/java/dream/examples/local/ExampleList.java +++ b/Dream2/src/examples/java/dream/examples/local/ExampleList.java @@ -11,7 +11,7 @@ public static void main(String args[]) { final Var> varList = new Var<>("varList", new ArrayList()); final Signal signalInt = new Signal("signalInt", () -> 1000 + varList.get().size(), varList); - signalInt.addValueChangeListener(System.out::println); + signalInt.change().addHandler((oldVal, val) -> System.out.println(val)); System.out.println("Expected results: "); System.out.println(1001); diff --git a/Dream2/src/examples/java/dream/examples/remote/RemoteSignalExample.java b/Dream2/src/examples/java/dream/examples/remote/RemoteSignalExample.java index 036710f..f092d4f 100755 --- a/Dream2/src/examples/java/dream/examples/remote/RemoteSignalExample.java +++ b/Dream2/src/examples/java/dream/examples/remote/RemoteSignalExample.java @@ -43,9 +43,9 @@ public static void main(String args[]) { final Signal signal4 = new Signal("signal4", () -> remoteString1.get().length() + remoteList.get().size(), remoteString1, remoteList); - signal1.addValueChangeListener(val -> System.out.println("Signal1: " + val)); - signal2.addValueChangeListener(val -> System.out.println("Signal2: " + val)); - signal3.addValueChangeListener(val -> System.out.println("Signal3: " + val)); - signal4.addValueChangeListener(val -> System.out.println("Signal4: " + val)); + signal1.change().addHandler((oldVal, val) -> System.out.println("Signal1: " + val)); + signal2.change().addHandler((oldVal, val) -> System.out.println("Signal2: " + val)); + signal3.change().addHandler((oldVal, val) -> System.out.println("Signal3: " + val)); + signal4.change().addHandler((oldVal, val) -> System.out.println("Signal4: " + val)); } } diff --git a/Dream2/src/main/java/dream/client/ChangeEvent.java b/Dream2/src/main/java/dream/client/ChangeEvent.java new file mode 100644 index 0000000..5ccbf60 --- /dev/null +++ b/Dream2/src/main/java/dream/client/ChangeEvent.java @@ -0,0 +1,41 @@ +package dream.client; + +import java.util.HashSet; +import java.util.Set; + +import dream.common.packets.EventPacket; + +public class ChangeEvent implements UpdateConsumer { + + private T latest; + + private Set> handlers = new HashSet>(); + + public ChangeEvent(UpdateProducer p) { + p.registerUpdateConsumer(this, p.getConstraints()); + } + + public void addHandler(ChangeEventHandler handler) { + handlers.add(handler); + } + + public void removeHandler(ChangeEventHandler handler) { + handlers.remove(handler); + } + + private void notifyHandler(T oldValue) { + handlers.forEach(h -> h.handle(oldValue, this.latest)); + } + + @Override + public void updateFromProducer(EventPacket packet, UpdateProducer producer) { + T newVal = (T) packet.getEvent().getVal(); + if (latest == null || !latest.equals(newVal)) { + T old = latest; + this.latest = newVal; + notifyHandler(old); + } + producer.notifyUpdateFinished(); + } + +} \ No newline at end of file diff --git a/Dream2/src/main/java/dream/client/ChangeEventHandler.java b/Dream2/src/main/java/dream/client/ChangeEventHandler.java new file mode 100644 index 0000000..b3e54e2 --- /dev/null +++ b/Dream2/src/main/java/dream/client/ChangeEventHandler.java @@ -0,0 +1,6 @@ +package dream.client; + +@FunctionalInterface +public interface ChangeEventHandler { + public void handle(T oldVal, T newVal); +} diff --git a/Dream2/src/main/java/dream/client/Signal.java b/Dream2/src/main/java/dream/client/Signal.java index f664414..afaf11b 100755 --- a/Dream2/src/main/java/dream/client/Signal.java +++ b/Dream2/src/main/java/dream/client/Signal.java @@ -25,7 +25,6 @@ public class Signal implements TimeChangingValue, UpdateProducer, UpdateConsumer, LockApplicant { - private final Set> valueChangeListeners = new HashSet<>(); // Management of local subscribers private final Map> consumers = new HashMap<>(); @@ -104,10 +103,6 @@ private final void processUpdate(EventProducerPair update) { return; } - // Notify value change listeners - logger.finest("Notifying registered listeners of the change."); - valueChangeListeners.forEach(l -> l.notifyValueChanged(val)); - // Notify local and remote dependent objects logger.finest("Sending event to dependent objects."); final Event event = new Event(Consts.hostName, object, val); @@ -145,16 +140,6 @@ private final void processUpdate(EventProducerPair update) { } } - @Override - public void addValueChangeListener(ValueChangeListener listener) { - valueChangeListeners.add(listener); - } - - @Override - public void removeValueChangeListener(ValueChangeListener listener) { - valueChangeListeners.remove(listener); - } - @Override public final synchronized T evaluate() { return evaluation.get(); @@ -250,4 +235,9 @@ public final synchronized void notifyLockGranted(LockGrantPacket lockGrant) { notifyAll(); } + @Override + public ChangeEvent change() { + return new ChangeEvent(this); + } + } diff --git a/Dream2/src/main/java/dream/client/TimeChangingValue.java b/Dream2/src/main/java/dream/client/TimeChangingValue.java index 5e853f6..2bdedca 100755 --- a/Dream2/src/main/java/dream/client/TimeChangingValue.java +++ b/Dream2/src/main/java/dream/client/TimeChangingValue.java @@ -18,19 +18,11 @@ interface TimeChangingValue { public T evaluate(); /** - * Register a new ValueChangeListener. - * - * @param listener - * the listener to add. + * Create an event that fires every time the TimeChangingValue changes. It + * fires the tuple (oldVal, newVal) for the change. The first tuple is + * (null, newVal) + * + * @return a new ChangeEvent */ - public void addValueChangeListener(ValueChangeListener listener); - - /** - * Unregister a ValueChangeListener. - * - * @param listener - * the listener to remove. - */ - public void removeValueChangeListener(ValueChangeListener listener); - + public ChangeEvent change(); } diff --git a/Dream2/src/main/java/dream/client/ValueChangeListener.java b/Dream2/src/main/java/dream/client/ValueChangeListener.java deleted file mode 100755 index 9a7921d..0000000 --- a/Dream2/src/main/java/dream/client/ValueChangeListener.java +++ /dev/null @@ -1,11 +0,0 @@ -package dream.client; - -/** - * A ValueChangeListener can register to a time changing object O and gets - * notified whenever the value of O changes. - */ -public interface ValueChangeListener { - - public void notifyValueChanged(T newValue); - -} From d673b6fb4a666bcd94b321dfe43d061a1a745878 Mon Sep 17 00:00:00 2001 From: Tobias Becker Date: Fri, 12 Feb 2016 11:15:16 +0100 Subject: [PATCH 017/161] small test to localize an issue --- Dream2/src/test/java/dream/UnknownVar.java | 56 ++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 Dream2/src/test/java/dream/UnknownVar.java diff --git a/Dream2/src/test/java/dream/UnknownVar.java b/Dream2/src/test/java/dream/UnknownVar.java new file mode 100644 index 0000000..7dd1a37 --- /dev/null +++ b/Dream2/src/test/java/dream/UnknownVar.java @@ -0,0 +1,56 @@ +package dream; + +import org.junit.BeforeClass; +import org.junit.Test; + +import dream.client.RemoteVar; +import dream.client.Signal; +import dream.locking.LockManagerLauncher; +import dream.server.ServerLauncher; + +public class UnknownVar { + + private static boolean serverStarted = false; + private final static boolean lockManagerStarted = false; + + @BeforeClass + public static void setup() { + startServerIfNeeded(); + startTokenServiceIfNeeded(); + } + + @Test + public void testUnknownVar() { + RemoteVar listener = new RemoteVar("Server", "Variable"); + Signal listenerSignal = new Signal("listener", () -> { + if (listener.get() == null) + return ""; + else + return listener.get(); + } , listener); + listenerSignal.change().addHandler((o, n) -> System.out.println(o + "->" + n)); + } + + private final static void startServerIfNeeded() { + if (!serverStarted) { + ServerLauncher.start(); + serverStarted = true; + } + try { + Thread.sleep(500); + } catch (final InterruptedException e) { + e.printStackTrace(); + } + } + + private final static void startTokenServiceIfNeeded() { + if (!lockManagerStarted) { + LockManagerLauncher.start(); + } + try { + Thread.sleep(500); + } catch (final InterruptedException e) { + e.printStackTrace(); + } + } +} \ No newline at end of file From 5f032f719231624293b8e12ba6aa64a606f2506e Mon Sep 17 00:00:00 2001 From: Tobias Becker Date: Sat, 13 Feb 2016 19:46:45 +0100 Subject: [PATCH 018/161] fixed a nullpointerexception --- .../src/main/java/dream/common/utils/DependencyGraphUtils.java | 1 + 1 file changed, 1 insertion(+) diff --git a/Dream2/src/main/java/dream/common/utils/DependencyGraphUtils.java b/Dream2/src/main/java/dream/common/utils/DependencyGraphUtils.java index 2d49283..f655e20 100644 --- a/Dream2/src/main/java/dream/common/utils/DependencyGraphUtils.java +++ b/Dream2/src/main/java/dream/common/utils/DependencyGraphUtils.java @@ -111,6 +111,7 @@ private static final void computeRelevantSourcesFor(Set newNodes, Set newNodesToEvaluate = newNodes.stream().// filter(expr -> !depGraph.getSources().contains(expr)).// map(depGraph.getGraph()::get).// + filter(x -> x != null).// collect(HashSet::new, HashSet::addAll, HashSet::addAll); if (!newNodesToEvaluate.isEmpty()) { From 0651bac52f5cfee0c389c568736a91faf3b5b227 Mon Sep 17 00:00:00 2001 From: Tobias Becker Date: Sat, 13 Feb 2016 19:47:21 +0100 Subject: [PATCH 019/161] chat works with multiple clients --- .../java/dream/examples/chat/Chat.java | 52 +++++++------- .../java/dream/examples/chat/ChatServer.java | 69 ++++++------------- 2 files changed, 47 insertions(+), 74 deletions(-) diff --git a/Dream2/src/examples/java/dream/examples/chat/Chat.java b/Dream2/src/examples/java/dream/examples/chat/Chat.java index aa1fab1..f8e599b 100644 --- a/Dream2/src/examples/java/dream/examples/chat/Chat.java +++ b/Dream2/src/examples/java/dream/examples/chat/Chat.java @@ -1,11 +1,15 @@ package dream.examples.chat; import java.awt.EventQueue; +import java.util.ArrayList; +import java.util.Set; +import dream.client.DreamClient; import dream.client.RemoteVar; import dream.client.Signal; import dream.client.Var; import dream.common.Consts; +import javafx.util.Pair; public class Chat { @@ -19,39 +23,33 @@ public Chat(String username) throws Exception { Consts.hostName = userName; // Establish new session with server - RemoteVar var = new RemoteVar(ChatServer.NAME, ChatServer.NEW_VAR); - Signal setup = new Signal("setup", () -> { + RemoteVar> var = new RemoteVar>(ChatServer.NAME, + ChatServer.SERVER_REGISTERED_CLIENTS); + Signal> setup = new Signal>("setup", () -> { if (var.get() == null) - return ""; + return new ArrayList(); else return var.get(); } , var); setup.change().addHandler((o, n) -> { - if (!n.equals("")) { - setup(n); - } + if (n.contains(username)) + setup(); + System.out.println("reg clients: " + n); }); - System.out.println("Setup: Waiting for Setup information from Server ..."); - } - private void setup(String setup_var) { - System.out.println("Setup: Setup information received!"); - System.out.println("Setup: VAR: " + setup_var); - String serverVar = ChatServer.getRandom(); - // Consts.hostName = setup_id; - Var init = new Var(setup_var, "message@" + userName + "@" + serverVar); + myMessages = new Var("chat_message", ""); - try { - Thread.sleep(1000); - } catch (InterruptedException e) { - e.printStackTrace(); - } - // should have correct value now - System.out.println("init: " + init.get()); - Consts.hostName = userName; - myMessages = new Var("message", ""); + System.out.println("Setup: Waiting for Information from Server ..."); + } - remoteMessages = new RemoteVar(serverVar + "@" + ChatServer.NAME); + private void setup() { + if (gui != null) + return; + Set vars = DreamClient.instance.listVariables(); + String serverVar = vars.stream().map(x -> new Pair(x.split("@")[1], x.split("@")[0])).// Pair(Host,Var) + filter(x -> x.getKey().equals(ChatServer.NAME) && x.getValue().contains(userName)).// + reduce(null, (a, b) -> b).getValue(); + remoteMessages = new RemoteVar(ChatServer.NAME, serverVar); Signal display = new Signal("display", () -> { if (remoteMessages.get() != null) @@ -69,15 +67,17 @@ private void setup(String setup_var) { } protected void sendMessage() { - myMessages.set(userName + ":" + gui.getTypedText()); + myMessages.set(gui.getTypedText()); gui.displayMessage("You: " + gui.getTypedText()); gui.resetTypedText(); } public static void main(String[] args) { try { - if (args.length < 1) + if (args.length < 1) { System.out.println("username missing"); + return; + } // Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).setLevel(Level.ALL); EventQueue.invokeLater(new Runnable() { diff --git a/Dream2/src/examples/java/dream/examples/chat/ChatServer.java b/Dream2/src/examples/java/dream/examples/chat/ChatServer.java index f4b3006..0344aed 100644 --- a/Dream2/src/examples/java/dream/examples/chat/ChatServer.java +++ b/Dream2/src/examples/java/dream/examples/chat/ChatServer.java @@ -4,21 +4,21 @@ import java.security.SecureRandom; import java.util.ArrayList; import java.util.HashMap; -import java.util.List; import java.util.Map; +import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; +import dream.client.DreamClient; import dream.client.RemoteVar; import dream.client.Signal; import dream.client.Var; import dream.common.Consts; import dream.locking.LockManagerLauncher; import dream.server.ServerLauncher; +import javafx.util.Pair; public class ChatServer { - public static final String NEW_ID = "NewSessionID"; - public static final String NEW_VAR = "NewSessionVAR"; public static final String NAME = "ChatServer"; private boolean serverStarted = false; @@ -27,10 +27,10 @@ public class ChatServer { private Map> clientVars; private Var> clients; - private Var newSessionVAR; private static SecureRandom r = new SecureRandom(); - private List processedIDs = new ArrayList(); + public static final String SERVER_PREFIX = "server_"; + public static final String SERVER_REGISTERED_CLIENTS = SERVER_PREFIX + "RegisteredClients"; public static void main(String[] args) { new ChatServer().start(); @@ -39,53 +39,27 @@ public static void main(String[] args) { private void initServer() { Consts.hostName = NAME; - clients = new Var>("RegisteredClients", new ArrayList()); + clients = new Var>(SERVER_REGISTERED_CLIENTS, new ArrayList()); clientVars = new HashMap>(); - newSessionVAR = new Var(NEW_VAR, ""); - changeNewSession(); + detectNewSession(); } /** - * Provide new Host ID and Variable name and listen to it for a new client + * Look for new clients every 5 seconds */ - private void changeNewSession() { - String var = getRandom(); - RemoteVar listener = new RemoteVar("*", var); - Signal listenerSignal = new Signal("listener", () -> { - if (listener.get() == null) - return ""; - else - return listener.get(); - } , listener); - listenerSignal.change().addHandler((o, msg) -> { - System.out.println("new session handler"); - changeNewSession(); - processNewSession(var, msg); - }); + private void detectNewSession() { + Set vars = DreamClient.instance.listVariables(); + vars.stream().map(x -> new Pair(x.split("@")[1], x.split("@")[0])).// Pair(Host,Var) + filter(x -> !clientVars.keySet().contains(x.getKey()) && x.getValue().startsWith("chat_")).// + forEach(x -> createNewSessionFor(x.getKey(), x.getValue())); try { - Thread.sleep(1000); + Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } - newSessionVAR.set(var); - try { - Thread.sleep(10000); - } catch (InterruptedException e) { - e.printStackTrace(); - } - processedIDs.add(var); - changeNewSession(); - } + detectNewSession(); - private void processNewSession(String var, String msg) { - if (!processedIDs.contains(var)) { - processedIDs.add(var); - // variable@name@serverVar - String[] temp = msg.split("@", 3); - registerClient(temp[1], temp[0], temp[2]); - } else - System.out.println("New message(\"" + msg + "\") on already discarded channel *@" + var); } /** @@ -96,14 +70,11 @@ private void processNewSession(String var, String msg) { * the name of the client * @param clientVar * the name of the variable, must be of type String - * @param serverVar - * @return the name of the variable on which the server will provide - * messages to the client */ - public void registerClient(String clientName, String clientVar, String serverVar) { - System.out.println("Register: " + clientName + "(" + clientVar + ") -> " + serverVar); + private void createNewSessionFor(String clientName, String clientVar) { + // x = "chat_xxx" RemoteVar remote = new RemoteVar(clientName, clientVar); - Signal listen = new Signal(serverVar + "listen", () -> { + Signal listen = new Signal(SERVER_PREFIX + "listen", () -> { if (remote.get() == null) return ""; else @@ -114,10 +85,12 @@ public void registerClient(String clientName, String clientVar, String serverVar listen.change().addHandler((oldValue, newValue) -> clientWrote(clientName, newValue)); // create new var for sending messages to this client - clientVars.put(clientName, new Var(serverVar, "")); + String reply = getRandom(); + Var replyChannel = new Var(reply + clientName, clientName); // add client as registered clients.modify((old) -> old.add(clientName)); + clientVars.put(clientName, replyChannel); } /** From ac717e6c290381acec32d55cce8e76df9ad58335 Mon Sep 17 00:00:00 2001 From: Tobias Becker Date: Tue, 16 Feb 2016 19:05:13 +0100 Subject: [PATCH 020/161] added a online list --- .../java/dream/examples/chat/Chat.java | 35 ++--- .../java/dream/examples/chat/ChatGUI.java | 124 ++++++++++++++++-- .../java/dream/examples/chat/ChatServer.java | 25 ++-- .../dream/{ => examples}/chat/License.txt | 0 .../dream/{ => examples}/chat/status-busy.png | Bin .../{ => examples}/chat/status-offline.png | Bin .../{ => examples}/chat/status-online.png | Bin 7 files changed, 149 insertions(+), 35 deletions(-) rename Dream2/src/resources/dream/{ => examples}/chat/License.txt (100%) rename Dream2/src/resources/dream/{ => examples}/chat/status-busy.png (100%) rename Dream2/src/resources/dream/{ => examples}/chat/status-offline.png (100%) rename Dream2/src/resources/dream/{ => examples}/chat/status-online.png (100%) diff --git a/Dream2/src/examples/java/dream/examples/chat/Chat.java b/Dream2/src/examples/java/dream/examples/chat/Chat.java index f8e599b..fe1d985 100644 --- a/Dream2/src/examples/java/dream/examples/chat/Chat.java +++ b/Dream2/src/examples/java/dream/examples/chat/Chat.java @@ -23,32 +23,35 @@ public Chat(String username) throws Exception { Consts.hostName = userName; // Establish new session with server - RemoteVar> var = new RemoteVar>(ChatServer.NAME, + RemoteVar> registeredClients = new RemoteVar>(ChatServer.NAME, ChatServer.SERVER_REGISTERED_CLIENTS); - Signal> setup = new Signal>("setup", () -> { - if (var.get() == null) + Signal> onlineList = new Signal>("setup", () -> { + if (registeredClients.get() == null) return new ArrayList(); else - return var.get(); - } , var); - setup.change().addHandler((o, n) -> { - if (n.contains(username)) - setup(); - System.out.println("reg clients: " + n); + return registeredClients.get(); + } , registeredClients); + onlineList.change().addHandler((o, n) -> { + if (n.contains(username) && gui == null) { + System.out.println("Setup: Server Registration done!"); + setup(n); + } else + gui.setOnline(n); }); myMessages = new Var("chat_message", ""); - System.out.println("Setup: Waiting for Information from Server ..."); + System.out.println("Setup: Waiting for Registration to Server ..."); } - private void setup() { + private void setup(ArrayList online) { if (gui != null) return; Set vars = DreamClient.instance.listVariables(); String serverVar = vars.stream().map(x -> new Pair(x.split("@")[1], x.split("@")[0])).// Pair(Host,Var) filter(x -> x.getKey().equals(ChatServer.NAME) && x.getValue().contains(userName)).// reduce(null, (a, b) -> b).getValue(); + System.out.println("Setup: Listening to Main Chat provided by Server"); remoteMessages = new RemoteVar(ChatServer.NAME, serverVar); Signal display = new Signal("display", () -> { @@ -58,18 +61,19 @@ private void setup() { return ""; } , remoteMessages); + System.out.println("Setup: Starting GUI"); gui = new ChatGUI(userName); gui.setListener(this); + System.out.println("Setup: Initializing Online-List"); + gui.setOnline(online); display.change().addHandler((oldValue, newValue) -> { gui.displayMessage(newValue); }); } - protected void sendMessage() { - myMessages.set(gui.getTypedText()); - gui.displayMessage("You: " + gui.getTypedText()); - gui.resetTypedText(); + protected void sendMessage(String text) { + myMessages.set(text); } public static void main(String[] args) { @@ -78,7 +82,6 @@ public static void main(String[] args) { System.out.println("username missing"); return; } - // Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).setLevel(Level.ALL); EventQueue.invokeLater(new Runnable() { @Override diff --git a/Dream2/src/examples/java/dream/examples/chat/ChatGUI.java b/Dream2/src/examples/java/dream/examples/chat/ChatGUI.java index 80fba5f..84f0d7d 100644 --- a/Dream2/src/examples/java/dream/examples/chat/ChatGUI.java +++ b/Dream2/src/examples/java/dream/examples/chat/ChatGUI.java @@ -5,6 +5,12 @@ import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; +import java.awt.event.WindowEvent; +import java.awt.event.WindowListener; +import java.io.File; +import java.net.MalformedURLException; +import java.util.ArrayList; +import java.util.List; import javax.swing.DefaultListCellRenderer; import javax.swing.DefaultListModel; @@ -15,19 +21,24 @@ import javax.swing.JList; import javax.swing.JTextArea; import javax.swing.JTextField; +import javax.swing.ListSelectionModel; import javax.swing.SpringLayout; +import javax.swing.SwingUtilities; -public class ChatGUI extends JFrame { +public class ChatGUI extends JFrame implements WindowListener { private static final long serialVersionUID = 4659984914364067514L; private JTextArea msgs; private JTextField sendText; private JList statusList; + private DefaultListModel listModel; private Chat listener; public ChatGUI(String userName) { + this.addWindowListener(this); initUI(userName); + } public void setListener(Chat c) { @@ -49,6 +60,56 @@ public void displayMessage(String text) { msgs.append(System.lineSeparator() + text); } + private List lastOnline; + + public void setOnline(List online) { + System.out.println("listModel: " + listModel); + System.out.println("onlineList: " + online); + if (lastOnline != null) { + for (String s : lastOnline) { + if (!online.contains(s)) + displayMessage(s + " has left the Chat."); + } + for (String s : online) { + if (!lastOnline.contains(s)) + displayMessage(s + " has joined."); + } + } + lastOnline = online; + List offlineList = new ArrayList(); + for (int i = 0; i < listModel.size(); i++) { + if (!online.contains(listModel.get(i))) + offlineList.add(listModel.get(i)); + } + System.out.println("offlineList: " + offlineList); + SwingUtilities.invokeLater(new Runnable() { + + @Override + public void run() { + listModel.clear(); + for (String e : online) { + listModel.addElement(e); + } + for (String e : offlineList) + listModel.addElement(e); + System.out.println("listModel: " + listModel); + System.out.println("Interval: [0, " + (online.size() - 1) + "]"); + statusList.setSelectionInterval(0, online.size() - 1); + } + }); + + } + + public void removeOnline(String name) { + listModel.removeElement(name); + } + + private void sendText() { + listener.sendMessage(getTypedText()); + this.displayMessage("You: " + getTypedText()); + this.resetTypedText(); + } + private void initUI(String userName) { sendText = new JTextField(20); sendText.addKeyListener(new KeyListener() { @@ -64,27 +125,31 @@ public void keyReleased(KeyEvent e) { @Override public void keyPressed(KeyEvent e) { if (e.getKeyCode() == KeyEvent.VK_ENTER) - listener.sendMessage(); + sendText(); } }); JButton sendButton = new JButton("Send"); sendButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent event) { - listener.sendMessage(); + sendText(); } }); msgs = new JTextArea(5, 27); msgs.setEditable(false); msgs.setMaximumSize(null); - DefaultListModel listModel = new DefaultListModel(); - listModel.addElement("Alice"); - listModel.addElement("Bob"); + listModel = new DefaultListModel(); statusList = new JList(listModel); + statusList.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); statusList.setEnabled(false); statusList.setLayoutOrientation(JList.HORIZONTAL_WRAP); statusList.setVisibleRowCount(-1); + // statusList.setSelectionBackground(Color.GREEN); + // statusList.setSelectionForeground(Color.BLACK); + // statusList.setForeground(Color.BLACK); + // statusList.setBackground(Color.WHITE); + // statusList.setCellRenderer(new DefaultListCellRenderer()); statusList.setCellRenderer(new DefaultListCellRenderer() { @@ -100,21 +165,23 @@ public java.awt.Component getListCellRendererComponent(javax.swing.JList list boolean isSelected, boolean cellHasFocus) { String name = (String) value; - label.setIcon(createImageIcon("status-offline.png", "Offline")); label.setText(name); if (isSelected) { label.setBackground(backgroundSelectionColor); label.setForeground(textSelectionColor); + label.setIcon(createImageIcon("status-online.png", "Online")); } else { label.setBackground(backgroundNonSelectionColor); label.setForeground(textNonSelectionColor); + label.setIcon(createImageIcon("status-offline.png", "Offline")); } return label; }; }); + SpringLayout layout = new SpringLayout(); // put messages on (5,5) @@ -154,12 +221,47 @@ public java.awt.Component getListCellRendererComponent(javax.swing.JList list /** Returns an ImageIcon, or null if the path was invalid. */ protected ImageIcon createImageIcon(String path, String description) { - java.net.URL imgURL = getClass().getResource(path); - if (imgURL != null) { - return new ImageIcon(imgURL, description); - } else { + File img = new File("./src/resources/dream/examples/chat/" + path); + try { + java.net.URL imgURL = img.toURI().toURL(); + if (imgURL != null) { + return new ImageIcon(imgURL, description); + } else { + System.err.println("Couldn't find file: " + path); + return null; + } + } catch (MalformedURLException e) { System.err.println("Couldn't find file: " + path); return null; } } + + @Override + public void windowOpened(WindowEvent e) { + } + + @Override + public void windowIconified(WindowEvent e) { + } + + @Override + public void windowDeiconified(WindowEvent e) { + } + + @Override + public void windowDeactivated(WindowEvent e) { + } + + @Override + public void windowClosing(WindowEvent e) { + listener.sendMessage("/quit"); + } + + @Override + public void windowClosed(WindowEvent e) { + } + + @Override + public void windowActivated(WindowEvent e) { + } } diff --git a/Dream2/src/examples/java/dream/examples/chat/ChatServer.java b/Dream2/src/examples/java/dream/examples/chat/ChatServer.java index 0344aed..aa44958 100644 --- a/Dream2/src/examples/java/dream/examples/chat/ChatServer.java +++ b/Dream2/src/examples/java/dream/examples/chat/ChatServer.java @@ -6,8 +6,6 @@ import java.util.HashMap; import java.util.Map; import java.util.Set; -import java.util.logging.Level; -import java.util.logging.Logger; import dream.client.DreamClient; import dream.client.RemoteVar; @@ -100,12 +98,23 @@ private void createNewSessionFor(String clientName, String clientVar) { * @param text */ private void clientWrote(String name, String text) { - System.out.println("Server: " + name + " -> " + text); // TODO propagate to correct clients (chat rooms etc.) - clientVars.forEach((client, var) -> { - if (client != name) - var.set(name + ": " + text); - }); + if (text.startsWith("/")) { + // special commands + String[] temp = text.split(" ", 2); + String command = temp[0].substring(1, temp[0].length()); + String rest = temp.length > 1 ? temp[1] : ""; + if (command.equals("quit")) { + clients.modify((old) -> old.remove(name)); + } + System.out.println("Server: " + name + " USED " + command); + } else { + System.out.println("Server: " + name + " -> " + text); + clientVars.forEach((client, var) -> { + if (client != name) + var.set(name + ": " + text); + }); + } } /** @@ -119,7 +128,7 @@ public void start() { startServerIfNeeded(); startLockManagerIfNeeded(); - Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).setLevel(Level.ALL); + // Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).setLevel(Level.ALL); initServer(); while (true) { diff --git a/Dream2/src/resources/dream/chat/License.txt b/Dream2/src/resources/dream/examples/chat/License.txt similarity index 100% rename from Dream2/src/resources/dream/chat/License.txt rename to Dream2/src/resources/dream/examples/chat/License.txt diff --git a/Dream2/src/resources/dream/chat/status-busy.png b/Dream2/src/resources/dream/examples/chat/status-busy.png similarity index 100% rename from Dream2/src/resources/dream/chat/status-busy.png rename to Dream2/src/resources/dream/examples/chat/status-busy.png diff --git a/Dream2/src/resources/dream/chat/status-offline.png b/Dream2/src/resources/dream/examples/chat/status-offline.png similarity index 100% rename from Dream2/src/resources/dream/chat/status-offline.png rename to Dream2/src/resources/dream/examples/chat/status-offline.png diff --git a/Dream2/src/resources/dream/chat/status-online.png b/Dream2/src/resources/dream/examples/chat/status-online.png similarity index 100% rename from Dream2/src/resources/dream/chat/status-online.png rename to Dream2/src/resources/dream/examples/chat/status-online.png From 80337a11d53d87c9ff5eedaa7e23a34e4dfb45ef Mon Sep 17 00:00:00 2001 From: Tobias Becker Date: Tue, 16 Feb 2016 19:10:29 +0100 Subject: [PATCH 021/161] removed a merge artifact --- Dream2/src/examples/java/dream/examples/financial/Model1.java | 1 - 1 file changed, 1 deletion(-) diff --git a/Dream2/src/examples/java/dream/examples/financial/Model1.java b/Dream2/src/examples/java/dream/examples/financial/Model1.java index 7f012c1..0625bf1 100644 --- a/Dream2/src/examples/java/dream/examples/financial/Model1.java +++ b/Dream2/src/examples/java/dream/examples/financial/Model1.java @@ -2,7 +2,6 @@ import dream.client.RemoteVar; import dream.client.Signal; -import dream.client.ValueChangeListener; import dream.common.Consts; public class Model1 { From 48fe7cede8cc12813edd281e48f79393fa95ed24 Mon Sep 17 00:00:00 2001 From: Tobias Becker Date: Tue, 16 Feb 2016 19:35:47 +0100 Subject: [PATCH 022/161] added a whisper function --- .../java/dream/examples/chat/Chat.java | 23 +++++++++++++++++-- .../java/dream/examples/chat/ChatGUI.java | 3 ++- .../java/dream/examples/chat/ChatServer.java | 21 ++++++++++++++--- 3 files changed, 41 insertions(+), 6 deletions(-) diff --git a/Dream2/src/examples/java/dream/examples/chat/Chat.java b/Dream2/src/examples/java/dream/examples/chat/Chat.java index fe1d985..4742d3c 100644 --- a/Dream2/src/examples/java/dream/examples/chat/Chat.java +++ b/Dream2/src/examples/java/dream/examples/chat/Chat.java @@ -54,13 +54,31 @@ private void setup(ArrayList online) { System.out.println("Setup: Listening to Main Chat provided by Server"); remoteMessages = new RemoteVar(ChatServer.NAME, serverVar); - Signal display = new Signal("display", () -> { + Signal incoming = new Signal("incoming", () -> { if (remoteMessages.get() != null) return remoteMessages.get(); else return ""; } , remoteMessages); + Signal display = new Signal("display", () -> { + if (incoming.get().startsWith("/")) { + String[] temp = incoming.get().split(" ", 2); + String command = temp[0].substring(1, temp[0].length()); + String rest = temp.length > 1 ? temp[1] : ""; + // QUIT - for now only used to update the registeredClients list + // (aka who's online) + if (command.equalsIgnoreCase("W")) { + String[] temp1 = rest.split(" ", 2); + String sender = temp1[0]; + String message = temp1.length > 1 ? temp1[1] : ""; + return sender + " whispered: " + message; + } else + return null; + } else + return incoming.get(); + } , incoming); + System.out.println("Setup: Starting GUI"); gui = new ChatGUI(userName); gui.setListener(this); @@ -68,7 +86,8 @@ private void setup(ArrayList online) { System.out.println("Setup: Initializing Online-List"); gui.setOnline(online); display.change().addHandler((oldValue, newValue) -> { - gui.displayMessage(newValue); + if (newValue != null) + gui.displayMessage(newValue); }); } diff --git a/Dream2/src/examples/java/dream/examples/chat/ChatGUI.java b/Dream2/src/examples/java/dream/examples/chat/ChatGUI.java index 84f0d7d..52a0ea3 100644 --- a/Dream2/src/examples/java/dream/examples/chat/ChatGUI.java +++ b/Dream2/src/examples/java/dream/examples/chat/ChatGUI.java @@ -106,7 +106,8 @@ public void removeOnline(String name) { private void sendText() { listener.sendMessage(getTypedText()); - this.displayMessage("You: " + getTypedText()); + if (!getTypedText().startsWith("/")) + this.displayMessage("You: " + getTypedText()); this.resetTypedText(); } diff --git a/Dream2/src/examples/java/dream/examples/chat/ChatServer.java b/Dream2/src/examples/java/dream/examples/chat/ChatServer.java index aa44958..08d31a0 100644 --- a/Dream2/src/examples/java/dream/examples/chat/ChatServer.java +++ b/Dream2/src/examples/java/dream/examples/chat/ChatServer.java @@ -98,14 +98,29 @@ private void createNewSessionFor(String clientName, String clientVar) { * @param text */ private void clientWrote(String name, String text) { - // TODO propagate to correct clients (chat rooms etc.) if (text.startsWith("/")) { - // special commands String[] temp = text.split(" ", 2); String command = temp[0].substring(1, temp[0].length()); String rest = temp.length > 1 ? temp[1] : ""; - if (command.equals("quit")) { + // QUIT - for now only used to update the registeredClients list + // (aka who's online) + if (command.equalsIgnoreCase("QUIT") || command.equalsIgnoreCase("Q")) { clients.modify((old) -> old.remove(name)); + } else if (command.equalsIgnoreCase("PRIVMSG") || command.equalsIgnoreCase("W") + || command.equalsIgnoreCase("WHISPER")) { + String[] temp1 = rest.split(" ", 2); + String target = temp1[0]; + String message = temp1.length > 1 ? temp1[1] : ""; + if (message.isEmpty()) { + // no actual message + // TODO: Reply to sender or ignore + } else if (!clientVars.containsKey(target) || !clients.get().contains(target)) { + // target is not known to the server or target is offline + // TODO: Reply to sender or send if target comes online or + // ignore + } else { + clientVars.get(target).set("/w " + name + " " + message); + } } System.out.println("Server: " + name + " USED " + command); } else { From 1bdd396ad3c374c5dc491bbab523b9043707ca90 Mon Sep 17 00:00:00 2001 From: Alessandro Margara Date: Tue, 8 Mar 2016 15:42:09 +0100 Subject: [PATCH 023/161] More flexible configuration of links More flexible configuration of links that enable the usage of multiple links to connect nodes together. This makes it possible to better experiments the benefits of a distributed implementation. --- DreamSim/conf/dream.conf | 3 +- DreamSim/scripts/summarize.py | 5 +- DreamSim/scripts/summarizeSingleRun.py | 5 +- .../dream/experiments/DreamConfiguration.java | 305 ++++++------ .../SimulatedExperimentRunner.java | 471 +++++++++--------- .../measurement/ClientMeasurementPeerlet.java | 35 +- .../dream/measurement/DreamDelayModel.java | 32 ++ .../LockManagerMeasurementPeerlet.java | 24 +- .../measurement/ServerMeasurementPeerlet.java | 34 +- .../overlay/ClientAssociationGenerator.java | 187 +++---- DreamSim/src/dream/overlay/Link.java | 125 ++--- .../dream/overlay/TreeOverlayGenerator.java | 265 +++++----- .../network/delayloss/UniformDelayModel.java | 31 +- 13 files changed, 790 insertions(+), 732 deletions(-) create mode 100644 DreamSim/src/dream/measurement/DreamDelayModel.java diff --git a/DreamSim/conf/dream.conf b/DreamSim/conf/dream.conf index a6ff14f..56310c7 100755 --- a/DreamSim/conf/dream.conf +++ b/DreamSim/conf/dream.conf @@ -35,7 +35,8 @@ consistencyType = 6 # minCommunicationDelayInMs = 1 maxCommunicationDelayInMs = 5 -linkLength = 1 +numHopsPerLink = 1 +numHopsToLockManager = 2 # # LOAD GENERATION diff --git a/DreamSim/scripts/summarize.py b/DreamSim/scripts/summarize.py index 702aaad..643d321 100755 --- a/DreamSim/scripts/summarize.py +++ b/DreamSim/scripts/summarize.py @@ -254,9 +254,8 @@ def avgAll(filename, values): # Values default = [0] -centralized = [0] locality = [0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0] -numBrokers = [2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24] +numBrokers = [1, 3, 5, 10, 15, 20] numVars = [1, 4, 7, 10, 40, 70, 100] graphDepth = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] numGraphDependencies = [1, 2, 3, 4, 5, 6, 7, 8, 9] @@ -267,7 +266,6 @@ def avgAll(filename, values): # Invocations summarizeAll("default", default) -summarizeAll("centralized", centralized) summarizeAll("locality", locality) summarizeAll("numBrokers", numBrokers) summarizeAll("numVars", numVars) @@ -278,7 +276,6 @@ def avgAll(filename, values): summarizeAll("timeBetweenReads", timeBetweenReads) avgAll("default", default) -avgAll("centralized", centralized) avgAll("locality", locality) avgAll("numBrokers", numBrokers) avgAll("numVars", numVars) diff --git a/DreamSim/scripts/summarizeSingleRun.py b/DreamSim/scripts/summarizeSingleRun.py index f316d2e..7db4bf2 100644 --- a/DreamSim/scripts/summarizeSingleRun.py +++ b/DreamSim/scripts/summarizeSingleRun.py @@ -262,9 +262,8 @@ def prepareDefaultDelay(): # Values default = [0] -centralized = [0] locality = [0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0] -numBrokers = [2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24] +numBrokers = [1, 3, 5, 10, 15, 20] numVars = [1, 4, 7, 10, 40, 70, 100] graphDepth = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] numGraphDependencies = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] @@ -275,7 +274,6 @@ def prepareDefaultDelay(): # Invocations summarizeAll("default", default) -summarizeAll("centralized", centralized) summarizeAll("locality", locality) summarizeAll("numBrokers", numBrokers) summarizeAll("numVars", numVars) @@ -286,7 +284,6 @@ def prepareDefaultDelay(): summarizeAll("timeBetweenReads", timeBetweenReads) avgAll("default", default) -avgAll("centralized", centralized) avgAll("locality", locality) avgAll("numBrokers", numBrokers) avgAll("numVars", numVars) diff --git a/DreamSim/src/dream/experiments/DreamConfiguration.java b/DreamSim/src/dream/experiments/DreamConfiguration.java index 21a9249..4e42d2a 100755 --- a/DreamSim/src/dream/experiments/DreamConfiguration.java +++ b/DreamSim/src/dream/experiments/DreamConfiguration.java @@ -7,155 +7,160 @@ * This is a class for the JavaReact simulation configuration. */ public class DreamConfiguration extends Configuration { - public static final int CAUSAL = 1; - public static final int SINGLE_SOURCE_GLITCH_FREE = 2; - public static final int COMPLETE_GLITCH_FREE = 3; - public static final int COMPLETE_GLITCH_FREE_OPTIMIZED = 4; - public static final int ATOMIC = 5; - public static final int SIDUP = 6; - - private static DreamConfiguration singleton; - - public static DreamConfiguration get() { - if (singleton == null) { - singleton = new DreamConfiguration(); - } - return singleton; - } - - @Override - public void loadFromFile(String filename) throws ConfigurationException { - super.loadFromFile(filename); - // Put here validity checks on configuration values - // Launch a ConfigurationException if some of them is invalid - } - - /** - * Directory to save results - */ - public String resultsDir; - - /** - * Simulation time in seconds - */ - public int simulationTimeInSeconds; - - /** - * Duration of the epoch used to sample delays - */ - public int epochDuration; - - /** - * Random seed used during the simulation - */ - public int seed; - - /** - * Number of brokers - */ - public int numberOfBrokers; - - /** - * Number of clients - */ - public int numberOfClients; - - /** - * Initial broker topology 1 = LINEAR 2 = STAR 3 = SCALEFREE - */ - public int brokersTopologyType; - - /** - * Association for the components UNIFORM_LOWEST_ID=1 UNIFORM_HIGHEST_ID=2 - * UNIFORM_ALTERNATE_ID=3 UNIFORM_RANDOM_ID=4 - */ - public int clientsAssociationType; - - /** - * Percentage of brokers that are pure forwarders. - */ - public double percentageOfPureForwarders; - - /** - * Max communication delay for communication interface - */ - public double maxCommunicationDelayInMs; - - /** - * Min communication delay for communication interface - */ - public double minCommunicationDelayInMs; - - /** - * Real (physical) length of each virtual link - */ - public int linkLength; - - /** - * Consistency type CAUSAL=1, SINGLE_GLITCH_FREE=2, COMPLETE_GLITCH_FREE=3, - * COMPLETE_GLITCH_FREE_OPTIMIZED=4, ATOMIC=5, SID_UP=6 - */ - public int consistencyType; - - /** - * Number of sources (vars) in the dependency graph - */ - public int graphNumSources; - - /** - * Number of levels for each var - */ - public int graphDepth; - - /** - * Minimum number of nodes per level. - */ - public int graphMinNodesPerLevel; - - /** - * Maximum number of nodes per level. - */ - public int graphMaxNodesPerLevel; - - /** - * Maximum number of incoming edges (dependencies) for each node. - */ - public int graphMaxDependenciesPerNode; - - /** - * Probability for a node to be shared between two sources. - */ - public double graphNodeShareProbability; - - /** - * Probability for a node to share the same host with one of its dependent - * nodes. - */ - public double graphLocality; - - /** - * Min time between two updates to a var - */ - public int minTimeBetweenEventsInMs; - - /** - * Max time between two updates to a var - */ - public int maxTimeBetweenEventsInMs; - - /** - * Min time between two reads of a signal - */ - public int minTimeBetweenSignalReadsInMs; - - /** - * Max time between two reads of a signal - */ - public int maxTimeBetweenSignalReadsInMs; - - /** - * Duration of a read lock (how long the lock is retained) - */ - public int readLockDurationInMs; + public static final int CAUSAL = 1; + public static final int SINGLE_SOURCE_GLITCH_FREE = 2; + public static final int COMPLETE_GLITCH_FREE = 3; + public static final int COMPLETE_GLITCH_FREE_OPTIMIZED = 4; + public static final int ATOMIC = 5; + public static final int SIDUP = 6; + + private static DreamConfiguration singleton; + + public static DreamConfiguration get() { + if (singleton == null) { + singleton = new DreamConfiguration(); + } + return singleton; + } + + @Override + public void loadFromFile(String filename) throws ConfigurationException { + super.loadFromFile(filename); + // Put here validity checks on configuration values + // Launch a ConfigurationException if some of them is invalid + } + + /** + * Directory to save results + */ + public String resultsDir; + + /** + * Simulation time in seconds + */ + public int simulationTimeInSeconds; + + /** + * Duration of the epoch used to sample delays + */ + public int epochDuration; + + /** + * Random seed used during the simulation + */ + public int seed; + + /** + * Number of brokers + */ + public int numberOfBrokers; + + /** + * Number of clients + */ + public int numberOfClients; + + /** + * Initial broker topology 1 = LINEAR 2 = STAR 3 = SCALEFREE + */ + public int brokersTopologyType; + + /** + * Association for the components UNIFORM_LOWEST_ID=1 UNIFORM_HIGHEST_ID=2 + * UNIFORM_ALTERNATE_ID=3 UNIFORM_RANDOM_ID=4 + */ + public int clientsAssociationType; + + /** + * Percentage of brokers that are pure forwarders. + */ + public double percentageOfPureForwarders; + + /** + * Max communication delay for communication interface + */ + public double maxCommunicationDelayInMs; + + /** + * Min communication delay for communication interface + */ + public double minCommunicationDelayInMs; + + /** + * Real (physical) length of each virtual link + */ + public int numHopsPerLink; + + /** + * Real (physical) length of the connection to the lock manager + */ + public int numHopsToLockManager; + + /** + * Consistency type CAUSAL=1, SINGLE_GLITCH_FREE=2, COMPLETE_GLITCH_FREE=3, + * COMPLETE_GLITCH_FREE_OPTIMIZED=4, ATOMIC=5, SID_UP=6 + */ + public int consistencyType; + + /** + * Number of sources (vars) in the dependency graph + */ + public int graphNumSources; + + /** + * Number of levels for each var + */ + public int graphDepth; + + /** + * Minimum number of nodes per level. + */ + public int graphMinNodesPerLevel; + + /** + * Maximum number of nodes per level. + */ + public int graphMaxNodesPerLevel; + + /** + * Maximum number of incoming edges (dependencies) for each node. + */ + public int graphMaxDependenciesPerNode; + + /** + * Probability for a node to be shared between two sources. + */ + public double graphNodeShareProbability; + + /** + * Probability for a node to share the same host with one of its dependent + * nodes. + */ + public double graphLocality; + + /** + * Min time between two updates to a var + */ + public int minTimeBetweenEventsInMs; + + /** + * Max time between two updates to a var + */ + public int maxTimeBetweenEventsInMs; + + /** + * Min time between two reads of a signal + */ + public int minTimeBetweenSignalReadsInMs; + + /** + * Max time between two reads of a signal + */ + public int maxTimeBetweenSignalReadsInMs; + + /** + * Duration of a read lock (how long the lock is retained) + */ + public int readLockDurationInMs; } diff --git a/DreamSim/src/dream/experiments/SimulatedExperimentRunner.java b/DreamSim/src/dream/experiments/SimulatedExperimentRunner.java index 9610148..941be74 100755 --- a/DreamSim/src/dream/experiments/SimulatedExperimentRunner.java +++ b/DreamSim/src/dream/experiments/SimulatedExperimentRunner.java @@ -5,6 +5,7 @@ import dream.generator.GraphGenerator; import dream.generator.RandomGenerator; import dream.locking.LockManagerFactory; +import dream.measurement.DreamDelayModel; import dream.measurement.MeasurementLogger; import dream.overlay.ClientAssociationGenerator; import dream.overlay.TreeOverlayGenerator; @@ -15,7 +16,6 @@ import protopeer.SimulatedExperiment; import protopeer.network.NetworkInterfaceFactory; import protopeer.network.delayloss.DelayLossNetworkInterfaceFactory; -import protopeer.network.delayloss.UniformDelayModel; import protopeer.util.quantities.Time; /** @@ -23,261 +23,264 @@ * parameters and save the results for later processing. */ public class SimulatedExperimentRunner extends SimulatedExperiment { - private final static String CONFIGURATION_FILE = "conf/dream.conf"; + private final static String CONFIGURATION_FILE = "conf/dream.conf"; - private static final int protocols[] = { // - DreamConfiguration.CAUSAL, // - DreamConfiguration.SINGLE_SOURCE_GLITCH_FREE, // - DreamConfiguration.COMPLETE_GLITCH_FREE, // - DreamConfiguration.COMPLETE_GLITCH_FREE_OPTIMIZED, // - DreamConfiguration.ATOMIC, // - DreamConfiguration.SIDUP // - }; + private static final int protocols[] = { // + DreamConfiguration.CAUSAL, // + DreamConfiguration.SINGLE_SOURCE_GLITCH_FREE, // + DreamConfiguration.COMPLETE_GLITCH_FREE, // + DreamConfiguration.COMPLETE_GLITCH_FREE_OPTIMIZED, // + DreamConfiguration.ATOMIC, // + DreamConfiguration.SIDUP // + }; - public final void runExperiments() { - // runFromFile(0); - for (int seed = 0; seed < 10; seed++) { - runDefault(seed); - runDefaultCentralized(seed); - runLocality(seed); - runNumBrokers(seed); - runNumVars(seed); - runGraphDepth(seed); - runNumGraphDependencies(seed); - runGraphShareProbability(seed); - runTimeBetweenEvents(seed); - runTimeBetweenReads(seed); - } - } + public final void runExperiments() { + // runFromFile(0); + for (int seed = 0; seed < 10; seed++) { + runDefault(seed); + runLocality(seed); + runNumBrokers(seed); + runNumVars(seed); + runGraphDepth(seed); + runNumGraphDependencies(seed); + runGraphShareProbability(seed); + runTimeBetweenEvents(seed); + runTimeBetweenReads(seed); + } + } - @SuppressWarnings("unused") - private final void runFromFile(int seed) { - loadFromFile(); - DreamConfiguration.get().seed = seed; - runExperiment("fromFile", String.valueOf(seed), "0", getProtocolName(DreamConfiguration.get().consistencyType)); - } + @SuppressWarnings("unused") + private final void runFromFile(int seed) { + loadFromFile(); + DreamConfiguration.get().seed = seed; + runExperiment("fromFile", String.valueOf(seed), "0", getProtocolName(DreamConfiguration.get().consistencyType)); + } - private final void runDefault(int seed) { - loadFromFile(); - DreamConfiguration.get().seed = seed; - for (final int i : protocols) { - DreamConfiguration.get().consistencyType = i; - runExperiment("default", String.valueOf(seed), String.valueOf(0), getProtocolName(i)); - } - } + private final void runDefault(int seed) { + loadFromFile(); + DreamConfiguration.get().seed = seed; + for (final int i : protocols) { + DreamConfiguration.get().consistencyType = i; + runExperiment("default", String.valueOf(seed), String.valueOf(0), getProtocolName(i)); + } + } - private final void runDefaultCentralized(int seed) { - loadFromFile(); - DreamConfiguration.get().seed = seed; - DreamConfiguration.get().numberOfBrokers = 1; - DreamConfiguration.get().linkLength = 4; - DreamConfiguration.get().minCommunicationDelayInMs *= DreamConfiguration.get().linkLength; - DreamConfiguration.get().maxCommunicationDelayInMs *= DreamConfiguration.get().linkLength; - for (final int i : protocols) { - DreamConfiguration.get().consistencyType = i; - runExperiment("centralized", String.valueOf(seed), String.valueOf(0), getProtocolName(i)); - } - } + private final void runLocality(int seed) { + loadFromFile(); + DreamConfiguration.get().seed = seed; + for (final int i : protocols) { + DreamConfiguration.get().consistencyType = i; + for (int locality = 0; locality <= 100; locality += 10) { + final float floatLoc = (float) locality / 100; + DreamConfiguration.get().graphLocality = floatLoc; + runExperiment("locality", String.valueOf(seed), String.valueOf(floatLoc), getProtocolName(i)); + } + } + } - private final void runLocality(int seed) { - loadFromFile(); - DreamConfiguration.get().seed = seed; - for (final int i : protocols) { - DreamConfiguration.get().consistencyType = i; - for (int locality = 0; locality <= 100; locality += 10) { - final float floatLoc = (float) locality / 100; - DreamConfiguration.get().graphLocality = floatLoc; - runExperiment("locality", String.valueOf(seed), String.valueOf(floatLoc), getProtocolName(i)); - } - } - } + /** + * Here we keep the number of hops fixed, while increasing the number of + * brokers. + * + * A tree with n nodes has n-1 links. If each link consists of h hops, the + * tree has in total (n-1)*h hops. We want to keep this number identical in + * all the runs, by changing h (number of hops per link) together with n + * (number of nodes). + */ + private final void runNumBrokers(int seed) { + loadFromFile(); + final int totalNumHops = 20; + DreamConfiguration.get().seed = seed; + for (final int i : protocols) { + DreamConfiguration.get().consistencyType = i; + for (int numBrokers = 1; numBrokers <= 20;) { + DreamConfiguration.get().numberOfBrokers = numBrokers; + DreamConfiguration.get().numHopsPerLink = totalNumHops / numBrokers; + runExperiment("numBrokers", String.valueOf(seed), String.valueOf(numBrokers), getProtocolName(i)); + if (numBrokers < 5) { + numBrokers += 2; + } else { + numBrokers += 5; + } + } + } + } - private final void runNumBrokers(int seed) { - loadFromFile(); - DreamConfiguration.get().seed = seed; - for (final int i : protocols) { - DreamConfiguration.get().consistencyType = i; - for (int numBrokers = 2; numBrokers < 25; numBrokers += 2) { - DreamConfiguration.get().numberOfBrokers = numBrokers; - runExperiment("numBrokers", String.valueOf(seed), String.valueOf(numBrokers), getProtocolName(i)); - } - } - } + private final void runNumVars(int seed) { + loadFromFile(); + DreamConfiguration.get().seed = seed; + for (final int i : protocols) { + DreamConfiguration.get().consistencyType = i; + for (int numVars = 1; numVars <= 100;) { + DreamConfiguration.get().graphNumSources = numVars; + runExperiment("numVars", String.valueOf(seed), String.valueOf(numVars), getProtocolName(i)); + if (numVars < 10) { + numVars += 3; + } else { + numVars += 30; + } + } + } + } - private final void runNumVars(int seed) { - loadFromFile(); - DreamConfiguration.get().seed = seed; - for (final int i : protocols) { - DreamConfiguration.get().consistencyType = i; - for (int numVars = 1; numVars <= 100;) { - DreamConfiguration.get().graphNumSources = numVars; - runExperiment("numVars", String.valueOf(seed), String.valueOf(numVars), getProtocolName(i)); - if (numVars < 10) { - numVars += 3; - } else { - numVars += 30; - } - } - } - } + private final void runGraphDepth(int seed) { + loadFromFile(); + DreamConfiguration.get().seed = seed; + for (final int i : protocols) { + DreamConfiguration.get().consistencyType = i; + for (int depth = 1; depth <= 10; depth++) { + DreamConfiguration.get().graphDepth = depth; + runExperiment("graphDepth", String.valueOf(seed), String.valueOf(depth), getProtocolName(i)); + } + } + } - private final void runGraphDepth(int seed) { - loadFromFile(); - DreamConfiguration.get().seed = seed; - for (final int i : protocols) { - DreamConfiguration.get().consistencyType = i; - for (int depth = 1; depth <= 10; depth++) { - DreamConfiguration.get().graphDepth = depth; - runExperiment("graphDepth", String.valueOf(seed), String.valueOf(depth), getProtocolName(i)); - } - } - } + private final void runNumGraphDependencies(int seed) { + loadFromFile(); + DreamConfiguration.get().seed = seed; + for (final int i : protocols) { + DreamConfiguration.get().consistencyType = i; + for (int dep = 1; dep < 10; dep++) { + DreamConfiguration.get().graphMaxDependenciesPerNode = dep; + runExperiment("numGraphDependencies", String.valueOf(seed), String.valueOf(dep), getProtocolName(i)); + } + } + } - private final void runNumGraphDependencies(int seed) { - loadFromFile(); - DreamConfiguration.get().seed = seed; - for (final int i : protocols) { - DreamConfiguration.get().consistencyType = i; - for (int dep = 1; dep < 10; dep++) { - DreamConfiguration.get().graphMaxDependenciesPerNode = dep; - runExperiment("numGraphDependencies", String.valueOf(seed), String.valueOf(dep), getProtocolName(i)); - } - } - } + private final void runGraphShareProbability(int seed) { + loadFromFile(); + DreamConfiguration.get().seed = seed; + for (final int i : protocols) { + DreamConfiguration.get().consistencyType = i; + for (int share = 0; share <= 100; share += 10) { + final float shareFloat = (float) share / 100; + DreamConfiguration.get().graphNodeShareProbability = shareFloat; + runExperiment("graphShare", String.valueOf(seed), String.valueOf(shareFloat), getProtocolName(i)); + } + } + } - private final void runGraphShareProbability(int seed) { - loadFromFile(); - DreamConfiguration.get().seed = seed; - for (final int i : protocols) { - DreamConfiguration.get().consistencyType = i; - for (int share = 0; share <= 100; share += 10) { - final float shareFloat = (float) share / 100; - DreamConfiguration.get().graphNodeShareProbability = shareFloat; - runExperiment("graphShare", String.valueOf(seed), String.valueOf(shareFloat), getProtocolName(i)); - } - } - } + private final void runTimeBetweenEvents(int seed) { + loadFromFile(); + DreamConfiguration.get().seed = seed; + for (final int i : protocols) { + DreamConfiguration.get().consistencyType = i; + for (int timeBetweenEvents = 100; timeBetweenEvents <= 10000;) { + // TODO: change simulation time accordingly? + DreamConfiguration.get().epochDuration = 2 * timeBetweenEvents; + DreamConfiguration.get().minTimeBetweenEventsInMs = Math.min(1, timeBetweenEvents - 1); + DreamConfiguration.get().maxTimeBetweenEventsInMs = timeBetweenEvents + 1; + runExperiment("timeBetweenEvents", String.valueOf(seed), String.valueOf(timeBetweenEvents), getProtocolName(i)); + if (timeBetweenEvents < 1000) { + timeBetweenEvents += 300; + } else { + timeBetweenEvents += 3000; + } + } + } + } - private final void runTimeBetweenEvents(int seed) { - loadFromFile(); - DreamConfiguration.get().seed = seed; - for (final int i : protocols) { - DreamConfiguration.get().consistencyType = i; - for (int timeBetweenEvents = 100; timeBetweenEvents <= 10000;) { - // TODO: change simulation time accordingly? - DreamConfiguration.get().epochDuration = 2 * timeBetweenEvents; - DreamConfiguration.get().minTimeBetweenEventsInMs = Math.min(1, timeBetweenEvents - 1); - DreamConfiguration.get().maxTimeBetweenEventsInMs = timeBetweenEvents + 1; - runExperiment("timeBetweenEvents", String.valueOf(seed), String.valueOf(timeBetweenEvents), getProtocolName(i)); - if (timeBetweenEvents < 1000) { - timeBetweenEvents += 300; - } else { - timeBetweenEvents += 3000; - } - } - } - } + private final void runTimeBetweenReads(int seed) { + loadFromFile(); + DreamConfiguration.get().seed = seed; + for (final int i : protocols) { + DreamConfiguration.get().consistencyType = i; + for (int timeBetweenReads = 100; timeBetweenReads <= 10000;) { + DreamConfiguration.get().minTimeBetweenSignalReadsInMs = Math.min(1, timeBetweenReads - 1); + DreamConfiguration.get().maxTimeBetweenSignalReadsInMs = timeBetweenReads + 1; + runExperiment("timeBetweenReads", String.valueOf(seed), String.valueOf(timeBetweenReads), getProtocolName(i)); + if (timeBetweenReads < 1000) { + timeBetweenReads += 300; + } else { + timeBetweenReads += 3000; + } + } + } + } - private final void runTimeBetweenReads(int seed) { - loadFromFile(); - DreamConfiguration.get().seed = seed; - for (final int i : protocols) { - DreamConfiguration.get().consistencyType = i; - for (int timeBetweenReads = 100; timeBetweenReads <= 10000;) { - DreamConfiguration.get().minTimeBetweenSignalReadsInMs = Math.min(1, timeBetweenReads - 1); - DreamConfiguration.get().maxTimeBetweenSignalReadsInMs = timeBetweenReads + 1; - runExperiment("timeBetweenReads", String.valueOf(seed), String.valueOf(timeBetweenReads), getProtocolName(i)); - if (timeBetweenReads < 1000) { - timeBetweenReads += 300; - } else { - timeBetweenReads += 3000; - } - } - } - } + private final void loadFromFile() { + try { + DreamConfiguration.get().loadFromFile(CONFIGURATION_FILE); + } catch (final ConfigurationException e) { + e.printStackTrace(); + return; + } + } - private final void loadFromFile() { - try { - DreamConfiguration.get().loadFromFile(CONFIGURATION_FILE); - } catch (final ConfigurationException e) { - e.printStackTrace(); - return; - } - } + private final void runExperiment(String name, String seed, String value, String protocol) { + final String experimentName = name + "_" + seed + "_" + value + "_" + protocol; + final MeasurementLogger mLogger = MeasurementLogger.getLogger(); - private final void runExperiment(String name, String seed, String value, String protocol) { - final String experimentName = name + "_" + seed + "_" + value + "_" + protocol; - final MeasurementLogger mLogger = MeasurementLogger.getLogger(); + // Cleanup + RandomGenerator.reset(); + mLogger.resetCounters(); + TreeOverlayGenerator.get().clean(); + ClientAssociationGenerator.get().clean(); + GraphGenerator.get().clean(); + TrafficGeneratorPeerlet.resetCount(); - // Cleanup - RandomGenerator.reset(); - mLogger.resetCounters(); - TreeOverlayGenerator.get().clean(); - ClientAssociationGenerator.get().clean(); - GraphGenerator.get().clean(); - TrafficGeneratorPeerlet.resetCount(); + // Init environment and experiment + Experiment.initEnvironment(); + final SimulatedExperimentRunner experiment = new SimulatedExperimentRunner(); + experiment.init(); - // Init environment and experiment - Experiment.initEnvironment(); - final SimulatedExperimentRunner experiment = new SimulatedExperimentRunner(); - experiment.init(); + // Init the servers + int numPeers = 0; + final PeerFactory brokerPeerFactory = new ServerFactory(); + experiment.initPeers(numPeers + 1, DreamConfiguration.get().numberOfBrokers, brokerPeerFactory); + numPeers += DreamConfiguration.get().numberOfBrokers; - // Init the servers - int numPeers = 0; - final PeerFactory brokerPeerFactory = new ServerFactory(); - experiment.initPeers(numPeers + 1, DreamConfiguration.get().numberOfBrokers, brokerPeerFactory); - numPeers += DreamConfiguration.get().numberOfBrokers; + // Init the clients + final PeerFactory componentPeerFactory = new ClientFactory(); + experiment.initPeers(numPeers + 1, DreamConfiguration.get().numberOfClients, componentPeerFactory); + numPeers += DreamConfiguration.get().numberOfClients; - // Init the clients - final PeerFactory componentPeerFactory = new ClientFactory(); - experiment.initPeers(numPeers + 1, DreamConfiguration.get().numberOfClients, componentPeerFactory); - numPeers += DreamConfiguration.get().numberOfClients; + // Init the lock manager + if (DreamConfiguration.get().consistencyType == DreamConfiguration.COMPLETE_GLITCH_FREE || // + DreamConfiguration.get().consistencyType == DreamConfiguration.COMPLETE_GLITCH_FREE_OPTIMIZED || // + DreamConfiguration.get().consistencyType == DreamConfiguration.ATOMIC || // + DreamConfiguration.get().consistencyType == DreamConfiguration.SIDUP) { + final PeerFactory lockManagerFactory = new LockManagerFactory(); + experiment.initPeers(numPeers + 1, 1, lockManagerFactory); + numPeers++; + } - // Init the lock manager - if (DreamConfiguration.get().consistencyType == DreamConfiguration.COMPLETE_GLITCH_FREE || // - DreamConfiguration.get().consistencyType == DreamConfiguration.COMPLETE_GLITCH_FREE_OPTIMIZED || // - DreamConfiguration.get().consistencyType == DreamConfiguration.ATOMIC || // - DreamConfiguration.get().consistencyType == DreamConfiguration.SIDUP) { - final PeerFactory lockManagerFactory = new LockManagerFactory(); - experiment.initPeers(numPeers + 1, 1, lockManagerFactory); - numPeers++; - } + // Start peers + experiment.startPeers(1, numPeers); - // Start peers - experiment.startPeers(1, numPeers); + // Run the simulation + System.out.println("Starting experiment " + experimentName); + experiment.runSimulation(Time.inSeconds(DreamConfiguration.get().simulationTimeInSeconds)); + System.out.println("Experiment " + experimentName + " complete"); + mLogger.printResults(experimentName + "_"); + System.out.println("Results written on file"); + } - // Run the simulation - System.out.println("Starting experiment " + experimentName); - experiment.runSimulation(Time.inSeconds(DreamConfiguration.get().simulationTimeInSeconds)); - System.out.println("Experiment " + experimentName + " complete"); - mLogger.printResults(experimentName + "_"); - System.out.println("Results written on file"); - } + private final String getProtocolName(int id) { + switch (id) { + case DreamConfiguration.CAUSAL: + return "causal"; + case DreamConfiguration.SINGLE_SOURCE_GLITCH_FREE: + return "single_glitch_free"; + case DreamConfiguration.COMPLETE_GLITCH_FREE: + return "complete_glitch_free"; + case DreamConfiguration.COMPLETE_GLITCH_FREE_OPTIMIZED: + return "complete_glitch_free_optimized"; + case DreamConfiguration.ATOMIC: + return "atomic"; + case DreamConfiguration.SIDUP: + return "sid_up"; + default: + assert false : id; + return null; + } + } - private final String getProtocolName(int id) { - switch (id) { - case DreamConfiguration.CAUSAL: - return "causal"; - case DreamConfiguration.SINGLE_SOURCE_GLITCH_FREE: - return "single_glitch_free"; - case DreamConfiguration.COMPLETE_GLITCH_FREE: - return "complete_glitch_free"; - case DreamConfiguration.COMPLETE_GLITCH_FREE_OPTIMIZED: - return "complete_glitch_free_optimized"; - case DreamConfiguration.ATOMIC: - return "atomic"; - case DreamConfiguration.SIDUP: - return "sid_up"; - default: - assert false : id; - return null; - } - } - - @Override - public NetworkInterfaceFactory createNetworkInterfaceFactory() { - return new DelayLossNetworkInterfaceFactory(getEventScheduler(), // - new UniformDelayModel(DreamConfiguration.get().minCommunicationDelayInMs, DreamConfiguration.get().maxCommunicationDelayInMs)); - } + @Override + public NetworkInterfaceFactory createNetworkInterfaceFactory() { + return new DelayLossNetworkInterfaceFactory(getEventScheduler(), // + new DreamDelayModel(DreamConfiguration.get().minCommunicationDelayInMs, + DreamConfiguration.get().maxCommunicationDelayInMs)); + } } diff --git a/DreamSim/src/dream/measurement/ClientMeasurementPeerlet.java b/DreamSim/src/dream/measurement/ClientMeasurementPeerlet.java index 222a76c..246cbce 100755 --- a/DreamSim/src/dream/measurement/ClientMeasurementPeerlet.java +++ b/DreamSim/src/dream/measurement/ClientMeasurementPeerlet.java @@ -2,27 +2,38 @@ import dream.experiments.DreamConfiguration; import protopeer.BasePeerlet; +import protopeer.Experiment; import protopeer.Peer; import protopeer.network.Message; +import protopeer.network.NetworkAddress; /** * This peerlet, installed in every client, collects and prints information * about the network traffic. */ public class ClientMeasurementPeerlet extends BasePeerlet { - private MeasurementLogger mLogger; + private MeasurementLogger mLogger; - @Override - public void init(Peer peer) { - super.init(peer); - mLogger = MeasurementLogger.getLogger(); - } + @Override + public void init(Peer peer) { + super.init(peer); + mLogger = MeasurementLogger.getLogger(); + } - @Override - public void handleOutgoingMessage(Message msg) { - for (int i = 0; i < DreamConfiguration.get().linkLength; i++) { - mLogger.saveMessage(msg); - } - } + @Override + public void handleOutgoingMessage(Message msg) { + final int numHops = destinationIsLockManager(msg) // + ? DreamConfiguration.get().numHopsToLockManager // + : DreamConfiguration.get().numHopsPerLink; + for (int i = 0; i < numHops; i++) { + mLogger.saveMessage(msg); + } + } + + private final boolean destinationIsLockManager(Message msg) { + final int lockManagerId = DreamConfiguration.get().numberOfBrokers + DreamConfiguration.get().numberOfClients + 1; + final NetworkAddress lockManagerAddress = Experiment.getSingleton().getAddressToBindTo(lockManagerId); + return msg.getDestinationAddress().equals(lockManagerAddress); + } } diff --git a/DreamSim/src/dream/measurement/DreamDelayModel.java b/DreamSim/src/dream/measurement/DreamDelayModel.java new file mode 100644 index 0000000..787d496 --- /dev/null +++ b/DreamSim/src/dream/measurement/DreamDelayModel.java @@ -0,0 +1,32 @@ +package dream.measurement; + +import dream.experiments.DreamConfiguration; +import protopeer.Experiment; +import protopeer.network.Message; +import protopeer.network.NetworkAddress; +import protopeer.network.delayloss.UniformDelayModel; +import protopeer.util.RandomnessSource; + +public class DreamDelayModel extends UniformDelayModel { + + public DreamDelayModel(double minDelay, double maxDelay) { + super(minDelay, maxDelay); + } + + @Override + public double getDelay(NetworkAddress sourceAddress, NetworkAddress destinationAddress, Message message) { + final double linkDelay = minDelay + RandomnessSource.getNextNetworkDouble() * (maxDelay - minDelay); + final int numLinks = communicationInvolvesLockManager(sourceAddress, destinationAddress) // + ? DreamConfiguration.get().numHopsToLockManager // + : DreamConfiguration.get().numHopsPerLink; + return linkDelay * numLinks; + } + + private final boolean communicationInvolvesLockManager(NetworkAddress sourceAddress, + NetworkAddress destinationAddress) { + final int lockManagerId = DreamConfiguration.get().numberOfBrokers + DreamConfiguration.get().numberOfClients + 1; + final NetworkAddress lockManagerAddress = Experiment.getSingleton().getAddressToBindTo(lockManagerId); + return sourceAddress.equals(lockManagerAddress) || destinationAddress.equals(lockManagerAddress); + } + +} diff --git a/DreamSim/src/dream/measurement/LockManagerMeasurementPeerlet.java b/DreamSim/src/dream/measurement/LockManagerMeasurementPeerlet.java index 8e850c2..661ace9 100644 --- a/DreamSim/src/dream/measurement/LockManagerMeasurementPeerlet.java +++ b/DreamSim/src/dream/measurement/LockManagerMeasurementPeerlet.java @@ -10,19 +10,19 @@ * about the network traffic. */ public class LockManagerMeasurementPeerlet extends BasePeerlet { - private MeasurementLogger mLogger; + private MeasurementLogger mLogger; - @Override - public void init(Peer peer) { - super.init(peer); - mLogger = MeasurementLogger.getLogger(); - } + @Override + public void init(Peer peer) { + super.init(peer); + mLogger = MeasurementLogger.getLogger(); + } - @Override - public void handleOutgoingMessage(Message msg) { - for (int i = 0; i < DreamConfiguration.get().linkLength; i++) { - mLogger.saveMessage(msg); - } - } + @Override + public void handleOutgoingMessage(Message msg) { + for (int i = 0; i < DreamConfiguration.get().numHopsToLockManager; i++) { + mLogger.saveMessage(msg); + } + } } diff --git a/DreamSim/src/dream/measurement/ServerMeasurementPeerlet.java b/DreamSim/src/dream/measurement/ServerMeasurementPeerlet.java index 92d0d09..69315b5 100755 --- a/DreamSim/src/dream/measurement/ServerMeasurementPeerlet.java +++ b/DreamSim/src/dream/measurement/ServerMeasurementPeerlet.java @@ -2,27 +2,37 @@ import dream.experiments.DreamConfiguration; import protopeer.BasePeerlet; +import protopeer.Experiment; import protopeer.Peer; import protopeer.network.Message; +import protopeer.network.NetworkAddress; /** * This peerlet, installed in every server, collects and prints information * about the network traffic. */ public class ServerMeasurementPeerlet extends BasePeerlet { - private MeasurementLogger mLogger; + private MeasurementLogger mLogger; - @Override - public void init(Peer peer) { - super.init(peer); - mLogger = MeasurementLogger.getLogger(); - } + @Override + public void init(Peer peer) { + super.init(peer); + mLogger = MeasurementLogger.getLogger(); + } - @Override - public void handleOutgoingMessage(Message msg) { - for (int i = 0; i < DreamConfiguration.get().linkLength; i++) { - mLogger.saveMessage(msg); - } - } + @Override + public void handleOutgoingMessage(Message msg) { + final int numHops = destinationIsLockManager(msg) // + ? DreamConfiguration.get().numHopsToLockManager // + : DreamConfiguration.get().numHopsPerLink; + for (int i = 0; i < numHops; i++) { + mLogger.saveMessage(msg); + } + } + private final boolean destinationIsLockManager(Message msg) { + final int lockManagerId = DreamConfiguration.get().numberOfBrokers + DreamConfiguration.get().numberOfClients + 1; + final NetworkAddress lockManagerAddress = Experiment.getSingleton().getAddressToBindTo(lockManagerId); + return msg.getDestinationAddress().equals(lockManagerAddress); + } } diff --git a/DreamSim/src/dream/overlay/ClientAssociationGenerator.java b/DreamSim/src/dream/overlay/ClientAssociationGenerator.java index ed259a5..03fc57c 100755 --- a/DreamSim/src/dream/overlay/ClientAssociationGenerator.java +++ b/DreamSim/src/dream/overlay/ClientAssociationGenerator.java @@ -18,109 +18,110 @@ * @author Daniel Dubois */ public class ClientAssociationGenerator implements IClientAssociationGenerator { - public final int UNIFORM_LOWEST_ID = 1; - public final int UNIFORM_HIGHEST_ID = 2; - public final int UNIFORM_ALTERNATE_ID = 3; - public final int UNIFORM_RANDOM_ID = 4; + public final int UNIFORM_LOWEST_ID = 1; + public final int UNIFORM_HIGHEST_ID = 2; + public final int UNIFORM_ALTERNATE_ID = 3; + public final int UNIFORM_RANDOM_ID = 4; - private static IClientAssociationGenerator instance; + private static IClientAssociationGenerator instance; - private final int type; - private final double percentageOfPureForwarders; + private final int type; + private final double percentageOfPureForwarders; - private final Set association = new HashSet<>(); - private boolean associationGenerated = false; + private final Set association = new HashSet<>(); + private boolean associationGenerated = false; - public static final IClientAssociationGenerator get() { - if (instance == null) { - instance = new ClientAssociationGenerator(); - } - return instance; - } + public static final IClientAssociationGenerator get() { + if (instance == null) { + instance = new ClientAssociationGenerator(); + } + return instance; + } - @Override - public void clean() { - association.clear(); - associationGenerated = false; - } + @Override + public void clean() { + association.clear(); + associationGenerated = false; + } - public ClientAssociationGenerator() { - this.type = DreamConfiguration.get().clientsAssociationType; - this.percentageOfPureForwarders = DreamConfiguration.get().percentageOfPureForwarders; - if (percentageOfPureForwarders < 0 || percentageOfPureForwarders > 1) { - throw new IllegalArgumentException("percentageOfPureForwarders is not valid for ComponentAssociationGenerator"); - } - switch (type) { - case UNIFORM_LOWEST_ID: - case UNIFORM_HIGHEST_ID: - case UNIFORM_ALTERNATE_ID: - case UNIFORM_RANDOM_ID: - break; - default: - throw new IllegalArgumentException("Type is not valid for ComponentAssociationGenerator"); - } - } + public ClientAssociationGenerator() { + this.type = DreamConfiguration.get().clientsAssociationType; + this.percentageOfPureForwarders = DreamConfiguration.get().percentageOfPureForwarders; + if (percentageOfPureForwarders < 0 || percentageOfPureForwarders > 1) { + throw new IllegalArgumentException("percentageOfPureForwarders is not valid for ComponentAssociationGenerator"); + } + switch (type) { + case UNIFORM_LOWEST_ID: + case UNIFORM_HIGHEST_ID: + case UNIFORM_ALTERNATE_ID: + case UNIFORM_RANDOM_ID: + break; + default: + throw new IllegalArgumentException("Type is not valid for ComponentAssociationGenerator"); + } + } - @Override - public Set getAssociation() { - if (!associationGenerated) { - associationGenerated = true; - final DreamConfiguration conf = DreamConfiguration.get(); - final int minBroker = 1; - final int maxBroker = conf.numberOfBrokers; - final int minClient = maxBroker + 1; - final int maxClient = minClient + conf.numberOfClients - 1; - final Random random = RandomnessSource.getRandom(RandomnessSourceType.TOPOLOGY); + @Override + public Set getAssociation() { + if (!associationGenerated) { + associationGenerated = true; + final DreamConfiguration conf = DreamConfiguration.get(); + final int minBroker = 1; + final int maxBroker = conf.numberOfBrokers; + final int minClient = maxBroker + 1; + final int maxClient = minClient + conf.numberOfClients - 1; + final Random random = RandomnessSource.getRandom(RandomnessSourceType.TOPOLOGY); - final List brokers = IntStream.rangeClosed(minBroker, maxBroker)// - .mapToObj(i -> new Node(i))// - .collect(Collectors.toList()); + final List brokers = IntStream.rangeClosed(minBroker, maxBroker)// + .mapToObj(i -> new Node(i))// + .collect(Collectors.toList()); - final List clients = IntStream.rangeClosed(minClient, maxClient)// - .mapToObj(i -> new Node(i))// - .collect(Collectors.toList()); + final List clients = IntStream.rangeClosed(minClient, maxClient)// + .mapToObj(i -> new Node(i))// + .collect(Collectors.toList()); - int brokersToBeRemoved = new Double(brokers.size() * percentageOfPureForwarders).intValue(); - if (brokers.size() - brokersToBeRemoved > clients.size()) { - brokersToBeRemoved = brokers.size() - clients.size(); - } - switch (type) { - case UNIFORM_LOWEST_ID: - for (int i = brokers.size() - 1; i >= 0 && brokersToBeRemoved > 0; i--) { - brokers.remove(i); - brokersToBeRemoved--; - } - break; - case UNIFORM_RANDOM_ID: - Collections.shuffle(brokers, random); - case UNIFORM_HIGHEST_ID: - while (brokersToBeRemoved > 0) { - brokers.remove(0); - brokersToBeRemoved--; - } - break; - case UNIFORM_ALTERNATE_ID: - while (brokersToBeRemoved > 0) { - for (int i = brokers.size() - 1; i >= 0 && brokersToBeRemoved > 0; i = i - 2) { - brokers.remove(i); - brokersToBeRemoved--; - } - } - break; - } - getAssociation(brokers, clients); - } - return association; - } + int brokersToBeRemoved = new Double(brokers.size() * percentageOfPureForwarders).intValue(); + if (brokers.size() - brokersToBeRemoved > clients.size()) { + brokersToBeRemoved = brokers.size() - clients.size(); + } + switch (type) { + case UNIFORM_LOWEST_ID: + for (int i = brokers.size() - 1; i >= 0 && brokersToBeRemoved > 0; i--) { + brokers.remove(i); + brokersToBeRemoved--; + } + break; + case UNIFORM_RANDOM_ID: + Collections.shuffle(brokers, random); + case UNIFORM_HIGHEST_ID: + while (brokersToBeRemoved > 0) { + brokers.remove(0); + brokersToBeRemoved--; + } + break; + case UNIFORM_ALTERNATE_ID: + while (brokersToBeRemoved > 0) { + for (int i = brokers.size() - 1; i >= 0 && brokersToBeRemoved > 0; i = i - 2) { + brokers.remove(i); + brokersToBeRemoved--; + } + } + break; + } + getAssociation(brokers, clients); + } + return association; + } - private void getAssociation(final List brokers, final List components) { - int i = 0; - while (association.size() < components.size()) { - // TODO: was links.add(new Link(brokers.get(i%components.size()), - // components.get(i))); - association.add(new Link(brokers.get(i % brokers.size()), components.get(i))); - i++; - } - } + private void getAssociation(final List brokers, final List components) { + int i = 0; + while (association.size() < components.size()) { + // TODO: was links.add(new Link(brokers.get(i%components.size()), + // components.get(i))); + association + .add(new Link(brokers.get(i % brokers.size()), components.get(i), DreamConfiguration.get().numHopsPerLink)); + i++; + } + } } diff --git a/DreamSim/src/dream/overlay/Link.java b/DreamSim/src/dream/overlay/Link.java index f56b6c6..eed9c3a 100755 --- a/DreamSim/src/dream/overlay/Link.java +++ b/DreamSim/src/dream/overlay/Link.java @@ -2,75 +2,80 @@ import java.io.Serializable; -/** - * This class represents a generic Link between two nodes. - * - * @author Daniel Dubois - * - */ public class Link implements Serializable { - private static final long serialVersionUID = -8557693370724758574L; + private static final long serialVersionUID = -8557693370724758574L; - private final Node node1; - private final Node node2; + private final Node node1; + private final Node node2; + private final int numHops; - /** - * Create a new link representation between node1 and node2. Note: the first endpoint will always have the smallest - * id, thus if node2 has a lower id than node1 the two nodes will be SWAPPED! - * - * @param node1 First endpoint of the link - * @param node2 Second endpoint of the link - */ - public Link(Node node1, Node node2) { + /** + * Create a new link between node1 and node2 consisting of numHops hops. + * + * The first node will always have the smallest id, thus if node2 has a lower + * id than node1 the two nodes will be SWAPPED! + * + * @param node1 + * First node of the link + * @param node2 + * Second node of the link + * @param numHops + * Number of hops in the link + */ + public Link(Node node1, Node node2, int numHops) { + this.node1 = node1.getId() < node2.getId() ? node1 : node2; + this.node2 = node1.getId() < node2.getId() ? node2 : node1; + this.numHops = numHops; + } - if (node1.getId() < node2.getId()) { - this.node1 = node1; - this.node2 = node2; - } else { - this.node2 = node1; - this.node1 = node2; - } - } + /** + * Get the first endpoint of this link. + * + * @return the first endpoint of this link + */ + public final Node getNode1() { + return node1; + } - /** - * Get the first endpoint of this link. - * - * @return the first endpoint of this link - */ - public Node getNode1() { - return node1; - } + /** + * Get the second endpoint of this link. + * + * @return the second endpoint of this link + */ + public final Node getNode2() { + return node2; + } - /** - * Get the second endpoint of this link. - * - * @return the second endpoint of this link - */ - public Node getNode2() { - return node2; - } + /** + * Return the number of hops in the link. + * + * @return the number of hops in the link. + */ + public final int getNumHops() { + return numHops; + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } else if (o instanceof Link) { - Link link = (Link) o; - return link.getNode1().equals(node1) && link.getNode2().equals(node2); - } else { - return false; - } - } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } else if (o instanceof Link) { + final Link link = (Link) o; + return link.getNode1().equals(node1) && link.getNode2().equals(node2); + } else { + return false; + } + } - @Override - public int hashCode() { - return Integer.MIN_VALUE + node1.getId() + 1000001 * node2.getId(); - } + @Override + public int hashCode() { + return Integer.MIN_VALUE + node1.getId() + 1000001 * node2.getId(); + } - @Override - public String toString() { - return "(" + node1 + " - " + node2 + ")"; - } + @Override + public String toString() { + return "(" + node1 + " - " + node2 + ")"; + } } diff --git a/DreamSim/src/dream/overlay/TreeOverlayGenerator.java b/DreamSim/src/dream/overlay/TreeOverlayGenerator.java index a198587..8b39fc6 100755 --- a/DreamSim/src/dream/overlay/TreeOverlayGenerator.java +++ b/DreamSim/src/dream/overlay/TreeOverlayGenerator.java @@ -13,143 +13,132 @@ import protopeer.util.RandomnessSourceType; public class TreeOverlayGenerator implements IOverlayGenerator { - public static final int LINEAR = 1; - public static final int STAR = 2; - public static final int SCALEFREE = 3; - - private final int type; - - private static TreeOverlayGenerator instance = null; - private final Set links = new HashSet<>(); - private boolean linksGenerated = false; - - public static final IOverlayGenerator get() { - if (instance == null) { - instance = new TreeOverlayGenerator(); - } - return instance; - } - - private TreeOverlayGenerator() { - this.type = DreamConfiguration.get().brokersTopologyType; - switch (type) { - case LINEAR: - case STAR: - case SCALEFREE: - break; - default: - throw new IllegalArgumentException("Unknown Tree Topology Type"); - } - } - - @Override - public void clean() { - linksGenerated = false; - links.clear(); - } - - @Override - public Set generateOverlay() { - final DreamConfiguration conf = DreamConfiguration.get(); - final int brokersMin = 1; - final int brokersMax = conf.numberOfBrokers; - final Random random = RandomnessSource.getRandom(RandomnessSourceType.TOPOLOGY); - - if (!linksGenerated) { - linksGenerated = true; - final Set nodes = IntStream.rangeClosed(brokersMin, brokersMax)// - .mapToObj(Node::new)// - .collect(Collectors.toSet()); - - switch (type) { - case LINEAR: - return generateLinearOverlay(nodes, random); - case STAR: - return generateStarOverlay(nodes, random); - case SCALEFREE: - return generateScaleFreeOverlay(nodes, random); - } - } - return links; - } - - private Set generateLinearOverlay(Set nodes, Random random) { - Node previousNode = null; - Node currentNode = null; - for (final Node n : nodes) { - previousNode = currentNode; - currentNode = n; - if (previousNode != null && currentNode != null) { - links.add(new Link(previousNode, currentNode)); - } - } - return links; - } - - private Set generateStarOverlay(Set nodes, Random random) { - Node firstNode = null; - for (final Node n : nodes) { - if (n.getId() == 1) { - firstNode = n; - break; - } - } - for (final Node n : nodes) { - if (n.equals(firstNode)) { - continue; - } - links.add(new Link(firstNode, n)); - } - return links; - } - - private Set generateScaleFreeOverlay(Set nodes, Random random) { - final Integer nlinks = nodes.size() - 1; - if (nodes.size() < 2) { - return links; - } - final Integer d = 2 * nlinks / nodes.size(); - final List tmpNodes = new ArrayList(nodes); - final List addedNodes = new ArrayList(); - for (Integer i = 0; i <= d; i++) { - addedNodes.add(tmpNodes.remove(0)); - } - for (Integer i = 0; i < d; i++) { - for (Integer j = i + 1; j <= d; j++) { - links.add(new Link(addedNodes.get(i), addedNodes.get(j))); - } - } - while (tmpNodes.size() > 0) { - final Node sourceNode = tmpNodes.remove(0); - final Node destinationNode = getCandidateNode(addedNodes, links, random); - links.add(new Link(sourceNode, destinationNode)); - addedNodes.add(sourceNode); - } - return links; - } - - private Node getCandidateNode(List tmpNodes, Set links, Random random) { - final Double p = random.nextDouble(); - Double previous_p = 0.0; - Double current_p = 0.0; - for (Integer i = 0; i < tmpNodes.size(); i++) { - previous_p = current_p; - current_p += getDegree(tmpNodes.get(i), links) / (2.0 * links.size()); - if (p >= previous_p && p <= current_p) { - return tmpNodes.get(i); - } - } - return null; - } - - private Integer getDegree(Node node, Set links) { - Integer degree = 0; - for (final Link l : links) { - if (l.getNode1().equals(node) || l.getNode2().equals(node)) { - degree++; - } - } - return degree; - } + public static final int LINEAR = 1; + public static final int STAR = 2; + public static final int SCALEFREE = 3; + + private final int type; + + private static TreeOverlayGenerator instance = null; + private final Set links = new HashSet<>(); + private boolean linksGenerated = false; + + public static final IOverlayGenerator get() { + if (instance == null) { + instance = new TreeOverlayGenerator(); + } + return instance; + } + + private TreeOverlayGenerator() { + this.type = DreamConfiguration.get().brokersTopologyType; + switch (type) { + case LINEAR: + case STAR: + case SCALEFREE: + break; + default: + throw new IllegalArgumentException("Unknown Tree Topology Type"); + } + } + + @Override + public void clean() { + linksGenerated = false; + links.clear(); + } + + @Override + public Set generateOverlay() { + final DreamConfiguration conf = DreamConfiguration.get(); + final int brokersMin = 1; + final int brokersMax = conf.numberOfBrokers; + final Random random = RandomnessSource.getRandom(RandomnessSourceType.TOPOLOGY); + + if (!linksGenerated) { + linksGenerated = true; + final Set nodes = IntStream.rangeClosed(brokersMin, brokersMax)// + .mapToObj(Node::new)// + .collect(Collectors.toSet()); + + switch (type) { + case LINEAR: + generateLinearOverlay(nodes, random); + break; + case STAR: + generateStarOverlay(nodes, random); + break; + case SCALEFREE: + generateScaleFreeOverlay(nodes, random); + break; + } + } + return links; + } + + private void generateLinearOverlay(Set nodes, Random random) { + if (nodes.size() < 2) { + return; + } + final List nodesList = new ArrayList(nodes); + for (int i = 1; i < nodesList.size(); i++) { + links.add(new Link(nodesList.get(i - 1), nodesList.get(i), DreamConfiguration.get().numHopsPerLink)); + } + } + + private void generateStarOverlay(Set nodes, Random random) { + final Node firstNode = nodes.stream() // + .filter(n -> n.getId() == 1) // + .findFirst() // + .get(); + + nodes.stream() // + .filter(n -> !n.equals(firstNode)) // + .forEach(n -> links.add(new Link(firstNode, n, DreamConfiguration.get().numHopsPerLink))); + } + + private void generateScaleFreeOverlay(Set nodes, Random random) { + if (nodes.size() < 2) { + return; + } + final int nlinks = nodes.size() - 1; + final int d = 2 * nlinks / nodes.size(); + + final List nodesList = new ArrayList<>(nodes); + final List tmpNodes = nodesList.stream().skip(d + 1).collect(Collectors.toList()); + final List addedNodes = nodesList.stream().limit(d + 1).collect(Collectors.toList()); + + for (Integer i = 0; i < d; i++) { + for (Integer j = i + 1; j <= d; j++) { + links.add(new Link(addedNodes.get(i), addedNodes.get(j), DreamConfiguration.get().numHopsPerLink)); + } + } + while (tmpNodes.size() > 0) { + final Node sourceNode = tmpNodes.remove(0); + final Node destinationNode = getCandidateNode(addedNodes, links, random); + links.add(new Link(sourceNode, destinationNode, DreamConfiguration.get().numHopsPerLink)); + addedNodes.add(sourceNode); + } + } + + private Node getCandidateNode(List tmpNodes, Set links, Random random) { + final Double p = random.nextDouble(); + Double previous_p = 0.0; + Double current_p = 0.0; + for (Integer i = 0; i < tmpNodes.size(); i++) { + previous_p = current_p; + current_p += getDegree(tmpNodes.get(i), links) / (2.0 * links.size()); + if (p >= previous_p && p <= current_p) { + return tmpNodes.get(i); + } + } + return null; + } + + private long getDegree(Node node, Set links) { + return links.stream() // + .filter(l -> l.getNode1().equals(node) || l.getNode2().equals(node)) // + .count(); + } } diff --git a/DreamSim/src/protopeer/network/delayloss/UniformDelayModel.java b/DreamSim/src/protopeer/network/delayloss/UniformDelayModel.java index 270c8c0..cea7804 100755 --- a/DreamSim/src/protopeer/network/delayloss/UniformDelayModel.java +++ b/DreamSim/src/protopeer/network/delayloss/UniformDelayModel.java @@ -1,33 +1,34 @@ package protopeer.network.delayloss; -import java.util.*; +import java.util.Collection; -import protopeer.network.*; -import protopeer.util.*; +import protopeer.network.IntegerNetworkAddress; +import protopeer.network.Message; +import protopeer.network.NetworkAddress; +import protopeer.util.RandomnessSource; /** - * + * * A lossless network model in which the message delay is uniformely randomly * selected between some minimal and some maximal value. - * - * + * + * */ public class UniformDelayModel implements DelayLossNetworkModel { private int allocatedNodeAddress = 1; - private double minDelay; - - private double maxDelay; + protected final double minDelay; + protected final double maxDelay; /** * Creates a delay model that delays messages by a delay uniformely randomly * chosen between minDelay and maxDelay - * + * * @param minDelay - * minimum message delay in milliseconds + * minimum message delay in milliseconds * @param maxDelay - * maximum message delay in milliseconds + * maximum message delay in milliseconds */ public UniformDelayModel(double minDelay, double maxDelay) { super(); @@ -35,10 +36,12 @@ public UniformDelayModel(double minDelay, double maxDelay) { this.maxDelay = maxDelay; } + @Override public NetworkAddress allocateAddress() { return new IntegerNetworkAddress(allocatedNodeAddress++); } + @Override public double getDelay(NetworkAddress sourceAddress, NetworkAddress destinationAddress, Message message) { return minDelay + RandomnessSource.getNextNetworkDouble() * (maxDelay - minDelay); } @@ -47,10 +50,12 @@ public double getDelay(NetworkAddress sourceAddress, NetworkAddress destinationA * This network model has an practically unbounded number of addresses that * can be allocated. */ + @Override public int getNumAvailableAddresses() { return Integer.MAX_VALUE - allocatedNodeAddress; } + @Override public void deallocateAddress(NetworkAddress address) { } @@ -58,6 +63,7 @@ public void deallocateAddress(NetworkAddress address) { /** * This network model is lossless. */ + @Override public boolean getLoss(NetworkAddress sourceAddress, NetworkAddress destinationAddress, Message message) { return false; } @@ -65,6 +71,7 @@ public boolean getLoss(NetworkAddress sourceAddress, NetworkAddress destinationA /** * Not implemented in this network model */ + @Override public Collection getAddressesReachableByBroadcast(NetworkAddress srouceAddress) { throw new RuntimeException("Not implemented"); } From ef2f3906d98890b442bb15aed1edb85b2fbf00c6 Mon Sep 17 00:00:00 2001 From: Alessandro Margara Date: Tue, 8 Mar 2016 15:47:23 +0100 Subject: [PATCH 024/161] Added ui preferences --- Dream2/.settings/org.eclipse.jdt.ui.prefs | 62 +++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 Dream2/.settings/org.eclipse.jdt.ui.prefs diff --git a/Dream2/.settings/org.eclipse.jdt.ui.prefs b/Dream2/.settings/org.eclipse.jdt.ui.prefs new file mode 100644 index 0000000..07ccfe5 --- /dev/null +++ b/Dream2/.settings/org.eclipse.jdt.ui.prefs @@ -0,0 +1,62 @@ +eclipse.preferences.version=1 +editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true +formatter_profile=_Dream +formatter_settings_version=12 +sp_cleanup.add_default_serial_version_id=true +sp_cleanup.add_generated_serial_version_id=false +sp_cleanup.add_missing_annotations=true +sp_cleanup.add_missing_deprecated_annotations=true +sp_cleanup.add_missing_methods=false +sp_cleanup.add_missing_nls_tags=false +sp_cleanup.add_missing_override_annotations=true +sp_cleanup.add_missing_override_annotations_interface_methods=true +sp_cleanup.add_serial_version_id=false +sp_cleanup.always_use_blocks=true +sp_cleanup.always_use_parentheses_in_expressions=false +sp_cleanup.always_use_this_for_non_static_field_access=false +sp_cleanup.always_use_this_for_non_static_method_access=false +sp_cleanup.convert_functional_interfaces=false +sp_cleanup.convert_to_enhanced_for_loop=false +sp_cleanup.correct_indentation=false +sp_cleanup.format_source_code=true +sp_cleanup.format_source_code_changes_only=false +sp_cleanup.insert_inferred_type_arguments=false +sp_cleanup.make_local_variable_final=true +sp_cleanup.make_parameters_final=false +sp_cleanup.make_private_fields_final=true +sp_cleanup.make_type_abstract_if_missing_method=false +sp_cleanup.make_variable_declarations_final=false +sp_cleanup.never_use_blocks=false +sp_cleanup.never_use_parentheses_in_expressions=true +sp_cleanup.on_save_use_additional_actions=false +sp_cleanup.organize_imports=true +sp_cleanup.qualify_static_field_accesses_with_declaring_class=false +sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true +sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true +sp_cleanup.qualify_static_member_accesses_with_declaring_class=false +sp_cleanup.qualify_static_method_accesses_with_declaring_class=false +sp_cleanup.remove_private_constructors=true +sp_cleanup.remove_redundant_type_arguments=true +sp_cleanup.remove_trailing_whitespaces=false +sp_cleanup.remove_trailing_whitespaces_all=true +sp_cleanup.remove_trailing_whitespaces_ignore_empty=false +sp_cleanup.remove_unnecessary_casts=true +sp_cleanup.remove_unnecessary_nls_tags=false +sp_cleanup.remove_unused_imports=false +sp_cleanup.remove_unused_local_variables=false +sp_cleanup.remove_unused_private_fields=true +sp_cleanup.remove_unused_private_members=false +sp_cleanup.remove_unused_private_methods=true +sp_cleanup.remove_unused_private_types=true +sp_cleanup.sort_members=false +sp_cleanup.sort_members_all=false +sp_cleanup.use_anonymous_class_creation=false +sp_cleanup.use_blocks=false +sp_cleanup.use_blocks_only_for_return_and_throw=false +sp_cleanup.use_lambda=true +sp_cleanup.use_parentheses_in_expressions=false +sp_cleanup.use_this_for_non_static_field_access=false +sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true +sp_cleanup.use_this_for_non_static_method_access=false +sp_cleanup.use_this_for_non_static_method_access_only_if_necessary=true +sp_cleanup.use_type_arguments=false From b97db47611c6dc8b25a6b22dbb4c5c87b2a43c2a Mon Sep 17 00:00:00 2001 From: Alessandro Margara Date: Tue, 8 Mar 2016 16:21:32 +0100 Subject: [PATCH 025/161] Added Eclipse preferences as in DreamSim --- DreamSim/.settings/org.eclipse.jdt.ui.prefs | 62 +++ .../dream/client/ClientEventForwarder.java | 461 +++++++++--------- DreamSim/src/dream/client/ClientFactory.java | 18 +- .../dream/client/ClientSubscriptionTable.java | 93 ++-- .../src/dream/client/ConnectionManager.java | 157 +++--- DreamSim/src/dream/client/LockApplicant.java | 14 +- DreamSim/src/dream/client/QueueManager.java | 136 +++--- DreamSim/src/dream/client/Signal.java | 285 +++++------ DreamSim/src/dream/client/Subscriber.java | 14 +- .../dream/client/TrafficGeneratorPeerlet.java | 186 +++---- DreamSim/src/dream/client/Var.java | 114 +++-- DreamSim/src/dream/common/Consts.java | 46 +- DreamSim/src/dream/common/Outbox.java | 38 +- .../common/packets/AdvertisementPacket.java | 70 +-- .../src/dream/common/packets/EventPacket.java | 145 +++--- .../common/packets/SubscriptionPacket.java | 58 +-- .../dream/common/packets/content/AdvType.java | 2 +- .../common/packets/content/Advertisement.java | 46 +- .../dream/common/packets/content/Event.java | 40 +- .../dream/common/packets/content/SubType.java | 2 +- .../common/packets/content/Subscription.java | 46 +- .../discovery/LockManagerHelloPacket.java | 4 +- .../packets/discovery/ServerHelloPacket.java | 4 +- .../packets/locking/LockGrantPacket.java | 58 +-- .../packets/locking/LockReleasePacket.java | 36 +- .../packets/locking/LockRequestPacket.java | 119 ++--- .../common/packets/locking/LockType.java | 2 +- .../packets/overlay/AddBrokerMessage.java | 54 +- .../packets/overlay/RemoveBrokerMessage.java | 46 +- .../packets/overlay/ReplaceBrokerMessage.java | 80 +-- .../utils/AtomicDependencyDetector.java | 34 +- .../CompleteGlitchFreeDependencyDetector.java | 156 +++--- .../common/utils/DependencyDetector.java | 10 +- .../dream/common/utils/DependencyGraph.java | 56 +-- .../common/utils/DependencyGraphUtils.java | 194 ++++---- .../common/utils/FinalNodesDetector.java | 74 +-- .../utils/IntraSourceDependencyDetector.java | 120 ++--- .../dream/common/utils/LocalityDetector.java | 138 +++--- .../common/utils/WaitRecommendations.java | 59 +-- .../src/dream/generator/GraphGenerator.java | 354 +++++++------- .../generator/GraphGeneratorListener.java | 18 +- .../src/dream/generator/RandomGenerator.java | 28 +- .../src/dream/locking/DreamLockManager.java | 314 ++++++------ DreamSim/src/dream/locking/LockManager.java | 34 +- .../src/dream/locking/LockManagerFactory.java | 14 +- .../dream/locking/LockManagerForwarder.java | 88 ++-- .../src/dream/locking/SidUpLockManager.java | 82 ++-- .../dream/measurement/MeasurementLogger.java | 232 ++++----- .../overlay/IClientAssociationGenerator.java | 16 +- .../src/dream/overlay/IOverlayGenerator.java | 22 +- .../src/dream/overlay/IOverlayPeerlet.java | 34 +- DreamSim/src/dream/overlay/Node.java | 79 +-- .../src/dream/overlay/OverlayPeerlet.java | 138 +++--- .../src/dream/server/AdvertisementTable.java | 66 +-- .../dream/server/ServerEventForwarder.java | 186 +++---- DreamSim/src/dream/server/ServerFactory.java | 16 +- .../src/dream/server/SubscriptionTable.java | 92 ++-- 57 files changed, 2591 insertions(+), 2499 deletions(-) create mode 100644 DreamSim/.settings/org.eclipse.jdt.ui.prefs diff --git a/DreamSim/.settings/org.eclipse.jdt.ui.prefs b/DreamSim/.settings/org.eclipse.jdt.ui.prefs new file mode 100644 index 0000000..07ccfe5 --- /dev/null +++ b/DreamSim/.settings/org.eclipse.jdt.ui.prefs @@ -0,0 +1,62 @@ +eclipse.preferences.version=1 +editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true +formatter_profile=_Dream +formatter_settings_version=12 +sp_cleanup.add_default_serial_version_id=true +sp_cleanup.add_generated_serial_version_id=false +sp_cleanup.add_missing_annotations=true +sp_cleanup.add_missing_deprecated_annotations=true +sp_cleanup.add_missing_methods=false +sp_cleanup.add_missing_nls_tags=false +sp_cleanup.add_missing_override_annotations=true +sp_cleanup.add_missing_override_annotations_interface_methods=true +sp_cleanup.add_serial_version_id=false +sp_cleanup.always_use_blocks=true +sp_cleanup.always_use_parentheses_in_expressions=false +sp_cleanup.always_use_this_for_non_static_field_access=false +sp_cleanup.always_use_this_for_non_static_method_access=false +sp_cleanup.convert_functional_interfaces=false +sp_cleanup.convert_to_enhanced_for_loop=false +sp_cleanup.correct_indentation=false +sp_cleanup.format_source_code=true +sp_cleanup.format_source_code_changes_only=false +sp_cleanup.insert_inferred_type_arguments=false +sp_cleanup.make_local_variable_final=true +sp_cleanup.make_parameters_final=false +sp_cleanup.make_private_fields_final=true +sp_cleanup.make_type_abstract_if_missing_method=false +sp_cleanup.make_variable_declarations_final=false +sp_cleanup.never_use_blocks=false +sp_cleanup.never_use_parentheses_in_expressions=true +sp_cleanup.on_save_use_additional_actions=false +sp_cleanup.organize_imports=true +sp_cleanup.qualify_static_field_accesses_with_declaring_class=false +sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true +sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true +sp_cleanup.qualify_static_member_accesses_with_declaring_class=false +sp_cleanup.qualify_static_method_accesses_with_declaring_class=false +sp_cleanup.remove_private_constructors=true +sp_cleanup.remove_redundant_type_arguments=true +sp_cleanup.remove_trailing_whitespaces=false +sp_cleanup.remove_trailing_whitespaces_all=true +sp_cleanup.remove_trailing_whitespaces_ignore_empty=false +sp_cleanup.remove_unnecessary_casts=true +sp_cleanup.remove_unnecessary_nls_tags=false +sp_cleanup.remove_unused_imports=false +sp_cleanup.remove_unused_local_variables=false +sp_cleanup.remove_unused_private_fields=true +sp_cleanup.remove_unused_private_members=false +sp_cleanup.remove_unused_private_methods=true +sp_cleanup.remove_unused_private_types=true +sp_cleanup.sort_members=false +sp_cleanup.sort_members_all=false +sp_cleanup.use_anonymous_class_creation=false +sp_cleanup.use_blocks=false +sp_cleanup.use_blocks_only_for_return_and_throw=false +sp_cleanup.use_lambda=true +sp_cleanup.use_parentheses_in_expressions=false +sp_cleanup.use_this_for_non_static_field_access=false +sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true +sp_cleanup.use_this_for_non_static_method_access=false +sp_cleanup.use_this_for_non_static_method_access_only_if_necessary=true +sp_cleanup.use_type_arguments=false diff --git a/DreamSim/src/dream/client/ClientEventForwarder.java b/DreamSim/src/dream/client/ClientEventForwarder.java index 56375f3..555c187 100755 --- a/DreamSim/src/dream/client/ClientEventForwarder.java +++ b/DreamSim/src/dream/client/ClientEventForwarder.java @@ -29,234 +29,237 @@ import protopeer.util.quantities.Time; class ClientEventForwarder extends BasePeerlet { - private ConnectionManager connectionManager; - private final ClientSubscriptionTable subTable = new ClientSubscriptionTable(); - private DreamConfiguration conf; - - // Dependency detectors - private final CompleteGlitchFreeDependencyDetector completeGlitchDepDetector = CompleteGlitchFreeDependencyDetector.instance; - private final AtomicDependencyDetector atomicDepDetector = AtomicDependencyDetector.instance; - private final FinalNodesDetector finalNodesDetector = FinalNodesDetector.instance; - - // Lock applicants waiting for a grant - private final Map lockApplicants = new HashMap<>(); - - private final Logger logger = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME); - - @Override - public void init(Peer peer) { - super.init(peer); - conf = DreamConfiguration.get(); - connectionManager = (ConnectionManager) peer.getPeerletOfType(ConnectionManager.class); - } - - @Override - public void handleIncomingMessage(Message packet) { - if (packet instanceof AdvertisementPacket) { - logger.finer("Received an advertisement packet " + packet); - processAdvertisementFromServer((AdvertisementPacket) packet); - } else if (packet instanceof SubscriptionPacket) { - logger.fine("Received a subscription packet " + packet); - processSubscriptionFromServer((SubscriptionPacket) packet); - } else if (packet instanceof EventPacket) { - logger.finer("Received an event packet " + packet); - processEventFromServer((EventPacket) packet); - } else if (packet instanceof LockGrantPacket) { - logger.finer("Received lock grant packet " + packet); - processLockGrant((LockGrantPacket) packet); - } - } - - final void sendEvent(UUID id, Event ev, double creationTime, String initialVar) { - logger.finer("Sending an event " + ev); - Set lockReleaseNodes; - switch (conf.consistencyType) { - case DreamConfiguration.COMPLETE_GLITCH_FREE: - case DreamConfiguration.COMPLETE_GLITCH_FREE_OPTIMIZED: - lockReleaseNodes = completeGlitchDepDetector.getNodesToLockFor(initialVar); - break; - case DreamConfiguration.ATOMIC: - case DreamConfiguration.SIDUP: - lockReleaseNodes = finalNodesDetector.getFinalNodesFor(initialVar); - break; - default: - lockReleaseNodes = new HashSet<>(); - } - - final EventPacket evPkt = new EventPacket(ev, id, creationTime, initialVar); - evPkt.setLockReleaseNodes(lockReleaseNodes); - subTable.getMatchingSubscribers(ev).forEach(s -> s.notifyEventReceived(evPkt)); - if (subTable.needsToDeliverToServer(ev)) { - connectionManager.sendEvent(evPkt); - } - } - - /** - * Return false if the lock request is not needed - */ - final void sendReadOnlyLockRequest(String node, LockApplicant applicant) { - if (conf.consistencyType != DreamConfiguration.ATOMIC) { - assert false : conf.consistencyType; - logger.warning("Invoked sendReadOnlyLockRequest() even if the consistency level does not require it."); - return; - } - - logger.finer("Invoked sendReadOnlyLockRequest for node " + node); - final Set nodesToLock = new HashSet<>(); - nodesToLock.add(node); - - final UUID lockId = UUID.randomUUID(); - final LockRequestPacket reqPkt = new LockRequestPacket(getPeer().getNetworkAddress(), lockId, nodesToLock, nodesToLock, LockType.READ_ONLY); - lockApplicants.put(lockId, applicant); - - connectionManager.sendLockRequest(reqPkt); - } - - /** - * Return false if the lock request is not needed - */ - final boolean sendReadWriteLockRequest(String source, UUID packetID, LockApplicant applicant) { - if (conf.consistencyType != DreamConfiguration.COMPLETE_GLITCH_FREE && // - conf.consistencyType != DreamConfiguration.COMPLETE_GLITCH_FREE_OPTIMIZED && // - conf.consistencyType != DreamConfiguration.ATOMIC && // - conf.consistencyType != DreamConfiguration.SIDUP) { - assert false : conf.consistencyType; - logger.warning("Invoked sendReadWriteLockRequest() even if the consistency level does not require it."); - return false; - } - - logger.finer("Invoked sendReadWriteLockRequest for source " + source); - Set nodesToLock; - switch (conf.consistencyType) { - case DreamConfiguration.COMPLETE_GLITCH_FREE: - case DreamConfiguration.COMPLETE_GLITCH_FREE_OPTIMIZED: - nodesToLock = completeGlitchDepDetector.getNodesToLockFor(source); - break; - case DreamConfiguration.ATOMIC: - nodesToLock = atomicDepDetector.getNodesToLockFor(source); - break; - case DreamConfiguration.SIDUP: - nodesToLock = new HashSet<>(); - nodesToLock.add(source); - break; - default: - assert false : conf.consistencyType; - logger.warning("Invoked sendReadWriteLockRequest() even if the consistency level does not require it."); - return false; - } - - final Set releaseNodes = getLockReleaseNodesFor(source); - - if (nodesToLock.isEmpty()) { - return false; - } - - final LockRequestPacket reqPkt = new LockRequestPacket(getPeer().getNetworkAddress(), packetID, nodesToLock, releaseNodes, LockType.READ_WRITE); - lockApplicants.put(packetID, applicant); - - connectionManager.sendLockRequest(reqPkt); - return true; - } - - final Set getLockReleaseNodesFor(String source) { - switch (conf.consistencyType) { - case DreamConfiguration.COMPLETE_GLITCH_FREE: - case DreamConfiguration.COMPLETE_GLITCH_FREE_OPTIMIZED: - return completeGlitchDepDetector.getNodesToLockFor(source); - case DreamConfiguration.ATOMIC: - case DreamConfiguration.SIDUP: - return finalNodesDetector.getFinalNodesFor(source); - default: - return new HashSet<>(); - } - } - - final void sendLockRelease(UUID lockID) { - if (conf.consistencyType != DreamConfiguration.COMPLETE_GLITCH_FREE && // - conf.consistencyType != DreamConfiguration.COMPLETE_GLITCH_FREE_OPTIMIZED && // - conf.consistencyType != DreamConfiguration.ATOMIC && // - conf.consistencyType != DreamConfiguration.SIDUP) { - assert false : conf.consistencyType; - logger.warning("Invoked sendLockRelease() even if the consistency level does not require it."); - return; - } - - connectionManager.sendLockRelease(new LockReleasePacket(lockID)); - } - - final void advertise(Advertisement adv) { - logger.fine("Sending advertisement " + adv); - connectionManager.sendAdvertisement(adv); - } - - final void unadvertise(Advertisement adv) { - logger.fine("Sending unadvertisement " + adv); - connectionManager.sendUnadvertisement(adv); - } - - final void advertise(Advertisement adv, Set subs) { - logger.fine("Sending advertisement " + adv + " with subscriptions " + subs); - connectionManager.sendAdvertisement(adv, subs); - } - - final void unadvertise(Advertisement adv, Set subs, boolean isPublic) { - logger.fine("Sending unadvertisement " + adv + " with subscriptions " + subs); - connectionManager.sendUnadvertisement(adv); - } - - final void addSubscription(Subscriber subscriber, Subscription subscription) { - logger.fine("Adding subscription " + subscription); - subTable.addSubscription(subscriber, subscription); - if (!isLocal(subscription)) { - final Timer timer = getPeer().getClock().createNewTimer(); - timer.addTimerListener(t -> { - connectionManager.sendSubscription(subscription); - }); - timer.schedule(Time.inSeconds(Consts.delayBeforeSendingSubscriptions)); - } - } - - final void removeSubscription(Subscriber subscriber, Subscription subscription) { - logger.fine("Removing subscription " + subscription); - subTable.addSubscription(subscriber, subscription); - if (!isLocal(subscription)) { - connectionManager.sendSubscription(subscription); - } - } - - private final boolean isLocal(Subscription sub) { - final int clientId = ((TrafficGeneratorPeerlet) getPeer().getPeerletOfType(TrafficGeneratorPeerlet.class)).getClientId(); - final String hostId = Consts.hostPrefix + clientId; - return sub.getHostId().equals(hostId); - } - - private final void processEventFromServer(EventPacket evPkt) { - subTable.getMatchingSubscribers(evPkt.getEvent()).forEach(sub -> sub.notifyEventReceived(evPkt)); - } - - private final void processAdvertisementFromServer(AdvertisementPacket advPkt) { - // Nothing to do: in the simulation the update detectors are implemented as - // a singleton object shared between nodes - } - - private final void processSubscriptionFromServer(SubscriptionPacket subPkt) { - switch (subPkt.getSubType()) { - case SUB: - subTable.addServerSubscription(subPkt.getSubscription()); - break; - case UNSUB: - subTable.removeServerSubscription(subPkt.getSubscription()); - break; - default: - assert false : subPkt.getSubType(); - } - } - - private final void processLockGrant(LockGrantPacket lockGrant) { - final UUID lockID = lockGrant.getLockID(); - assert lockApplicants.containsKey(lockID); - final LockApplicant applicant = lockApplicants.remove(lockID); - applicant.notifyLockGranted(lockGrant); - } + private ConnectionManager connectionManager; + private final ClientSubscriptionTable subTable = new ClientSubscriptionTable(); + private DreamConfiguration conf; + + // Dependency detectors + private final CompleteGlitchFreeDependencyDetector completeGlitchDepDetector = CompleteGlitchFreeDependencyDetector.instance; + private final AtomicDependencyDetector atomicDepDetector = AtomicDependencyDetector.instance; + private final FinalNodesDetector finalNodesDetector = FinalNodesDetector.instance; + + // Lock applicants waiting for a grant + private final Map lockApplicants = new HashMap<>(); + + private final Logger logger = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME); + + @Override + public void init(Peer peer) { + super.init(peer); + conf = DreamConfiguration.get(); + connectionManager = (ConnectionManager) peer.getPeerletOfType(ConnectionManager.class); + } + + @Override + public void handleIncomingMessage(Message packet) { + if (packet instanceof AdvertisementPacket) { + logger.finer("Received an advertisement packet " + packet); + processAdvertisementFromServer((AdvertisementPacket) packet); + } else if (packet instanceof SubscriptionPacket) { + logger.fine("Received a subscription packet " + packet); + processSubscriptionFromServer((SubscriptionPacket) packet); + } else if (packet instanceof EventPacket) { + logger.finer("Received an event packet " + packet); + processEventFromServer((EventPacket) packet); + } else if (packet instanceof LockGrantPacket) { + logger.finer("Received lock grant packet " + packet); + processLockGrant((LockGrantPacket) packet); + } + } + + final void sendEvent(UUID id, Event ev, double creationTime, String initialVar) { + logger.finer("Sending an event " + ev); + Set lockReleaseNodes; + switch (conf.consistencyType) { + case DreamConfiguration.COMPLETE_GLITCH_FREE: + case DreamConfiguration.COMPLETE_GLITCH_FREE_OPTIMIZED: + lockReleaseNodes = completeGlitchDepDetector.getNodesToLockFor(initialVar); + break; + case DreamConfiguration.ATOMIC: + case DreamConfiguration.SIDUP: + lockReleaseNodes = finalNodesDetector.getFinalNodesFor(initialVar); + break; + default: + lockReleaseNodes = new HashSet<>(); + } + + final EventPacket evPkt = new EventPacket(ev, id, creationTime, initialVar); + evPkt.setLockReleaseNodes(lockReleaseNodes); + subTable.getMatchingSubscribers(ev).forEach(s -> s.notifyEventReceived(evPkt)); + if (subTable.needsToDeliverToServer(ev)) { + connectionManager.sendEvent(evPkt); + } + } + + /** + * Return false if the lock request is not needed + */ + final void sendReadOnlyLockRequest(String node, LockApplicant applicant) { + if (conf.consistencyType != DreamConfiguration.ATOMIC) { + assert false : conf.consistencyType; + logger.warning("Invoked sendReadOnlyLockRequest() even if the consistency level does not require it."); + return; + } + + logger.finer("Invoked sendReadOnlyLockRequest for node " + node); + final Set nodesToLock = new HashSet<>(); + nodesToLock.add(node); + + final UUID lockId = UUID.randomUUID(); + final LockRequestPacket reqPkt = new LockRequestPacket(getPeer().getNetworkAddress(), lockId, nodesToLock, + nodesToLock, LockType.READ_ONLY); + lockApplicants.put(lockId, applicant); + + connectionManager.sendLockRequest(reqPkt); + } + + /** + * Return false if the lock request is not needed + */ + final boolean sendReadWriteLockRequest(String source, UUID packetID, LockApplicant applicant) { + if (conf.consistencyType != DreamConfiguration.COMPLETE_GLITCH_FREE && // + conf.consistencyType != DreamConfiguration.COMPLETE_GLITCH_FREE_OPTIMIZED && // + conf.consistencyType != DreamConfiguration.ATOMIC && // + conf.consistencyType != DreamConfiguration.SIDUP) { + assert false : conf.consistencyType; + logger.warning("Invoked sendReadWriteLockRequest() even if the consistency level does not require it."); + return false; + } + + logger.finer("Invoked sendReadWriteLockRequest for source " + source); + Set nodesToLock; + switch (conf.consistencyType) { + case DreamConfiguration.COMPLETE_GLITCH_FREE: + case DreamConfiguration.COMPLETE_GLITCH_FREE_OPTIMIZED: + nodesToLock = completeGlitchDepDetector.getNodesToLockFor(source); + break; + case DreamConfiguration.ATOMIC: + nodesToLock = atomicDepDetector.getNodesToLockFor(source); + break; + case DreamConfiguration.SIDUP: + nodesToLock = new HashSet<>(); + nodesToLock.add(source); + break; + default: + assert false : conf.consistencyType; + logger.warning("Invoked sendReadWriteLockRequest() even if the consistency level does not require it."); + return false; + } + + final Set releaseNodes = getLockReleaseNodesFor(source); + + if (nodesToLock.isEmpty()) { + return false; + } + + final LockRequestPacket reqPkt = new LockRequestPacket(getPeer().getNetworkAddress(), packetID, nodesToLock, + releaseNodes, LockType.READ_WRITE); + lockApplicants.put(packetID, applicant); + + connectionManager.sendLockRequest(reqPkt); + return true; + } + + final Set getLockReleaseNodesFor(String source) { + switch (conf.consistencyType) { + case DreamConfiguration.COMPLETE_GLITCH_FREE: + case DreamConfiguration.COMPLETE_GLITCH_FREE_OPTIMIZED: + return completeGlitchDepDetector.getNodesToLockFor(source); + case DreamConfiguration.ATOMIC: + case DreamConfiguration.SIDUP: + return finalNodesDetector.getFinalNodesFor(source); + default: + return new HashSet<>(); + } + } + + final void sendLockRelease(UUID lockID) { + if (conf.consistencyType != DreamConfiguration.COMPLETE_GLITCH_FREE && // + conf.consistencyType != DreamConfiguration.COMPLETE_GLITCH_FREE_OPTIMIZED && // + conf.consistencyType != DreamConfiguration.ATOMIC && // + conf.consistencyType != DreamConfiguration.SIDUP) { + assert false : conf.consistencyType; + logger.warning("Invoked sendLockRelease() even if the consistency level does not require it."); + return; + } + + connectionManager.sendLockRelease(new LockReleasePacket(lockID)); + } + + final void advertise(Advertisement adv) { + logger.fine("Sending advertisement " + adv); + connectionManager.sendAdvertisement(adv); + } + + final void unadvertise(Advertisement adv) { + logger.fine("Sending unadvertisement " + adv); + connectionManager.sendUnadvertisement(adv); + } + + final void advertise(Advertisement adv, Set subs) { + logger.fine("Sending advertisement " + adv + " with subscriptions " + subs); + connectionManager.sendAdvertisement(adv, subs); + } + + final void unadvertise(Advertisement adv, Set subs, boolean isPublic) { + logger.fine("Sending unadvertisement " + adv + " with subscriptions " + subs); + connectionManager.sendUnadvertisement(adv); + } + + final void addSubscription(Subscriber subscriber, Subscription subscription) { + logger.fine("Adding subscription " + subscription); + subTable.addSubscription(subscriber, subscription); + if (!isLocal(subscription)) { + final Timer timer = getPeer().getClock().createNewTimer(); + timer.addTimerListener(t -> { + connectionManager.sendSubscription(subscription); + }); + timer.schedule(Time.inSeconds(Consts.delayBeforeSendingSubscriptions)); + } + } + + final void removeSubscription(Subscriber subscriber, Subscription subscription) { + logger.fine("Removing subscription " + subscription); + subTable.addSubscription(subscriber, subscription); + if (!isLocal(subscription)) { + connectionManager.sendSubscription(subscription); + } + } + + private final boolean isLocal(Subscription sub) { + final int clientId = ((TrafficGeneratorPeerlet) getPeer().getPeerletOfType(TrafficGeneratorPeerlet.class)) + .getClientId(); + final String hostId = Consts.hostPrefix + clientId; + return sub.getHostId().equals(hostId); + } + + private final void processEventFromServer(EventPacket evPkt) { + subTable.getMatchingSubscribers(evPkt.getEvent()).forEach(sub -> sub.notifyEventReceived(evPkt)); + } + + private final void processAdvertisementFromServer(AdvertisementPacket advPkt) { + // Nothing to do: in the simulation the update detectors are implemented as + // a singleton object shared between nodes + } + + private final void processSubscriptionFromServer(SubscriptionPacket subPkt) { + switch (subPkt.getSubType()) { + case SUB: + subTable.addServerSubscription(subPkt.getSubscription()); + break; + case UNSUB: + subTable.removeServerSubscription(subPkt.getSubscription()); + break; + default: + assert false : subPkt.getSubType(); + } + } + + private final void processLockGrant(LockGrantPacket lockGrant) { + final UUID lockID = lockGrant.getLockID(); + assert lockApplicants.containsKey(lockID); + final LockApplicant applicant = lockApplicants.remove(lockID); + applicant.notifyLockGranted(lockGrant); + } } diff --git a/DreamSim/src/dream/client/ClientFactory.java b/DreamSim/src/dream/client/ClientFactory.java index 121c884..84824de 100755 --- a/DreamSim/src/dream/client/ClientFactory.java +++ b/DreamSim/src/dream/client/ClientFactory.java @@ -7,14 +7,14 @@ public class ClientFactory implements PeerFactory { - @Override - public Peer createPeer(int peerIndex, Experiment experiment) { - final Peer newPeer = new Peer(peerIndex); - newPeer.addPeerlet(new ConnectionManager()); - newPeer.addPeerlet(new ClientEventForwarder()); - newPeer.addPeerlet(new ClientMeasurementPeerlet()); - newPeer.addPeerlet(new TrafficGeneratorPeerlet()); - return newPeer; - } + @Override + public Peer createPeer(int peerIndex, Experiment experiment) { + final Peer newPeer = new Peer(peerIndex); + newPeer.addPeerlet(new ConnectionManager()); + newPeer.addPeerlet(new ClientEventForwarder()); + newPeer.addPeerlet(new ClientMeasurementPeerlet()); + newPeer.addPeerlet(new TrafficGeneratorPeerlet()); + return newPeer; + } } diff --git a/DreamSim/src/dream/client/ClientSubscriptionTable.java b/DreamSim/src/dream/client/ClientSubscriptionTable.java index 598af2f..43fb5ee 100755 --- a/DreamSim/src/dream/client/ClientSubscriptionTable.java +++ b/DreamSim/src/dream/client/ClientSubscriptionTable.java @@ -12,61 +12,62 @@ import dream.common.packets.content.Subscription; final class ClientSubscriptionTable { - private final Map> subs = new HashMap>(); - private final Collection serverSubscriptions = new ArrayList(); + private final Map> subs = new HashMap>(); + private final Collection serverSubscriptions = new ArrayList(); - final void addSubscription(Subscriber subscriber, Subscription sub) { - Collection subsList = subs.get(subscriber); - if (subsList == null) { - subsList = new ArrayList(); - subs.put(subscriber, subsList); - } - subsList.add(sub); - } + final void addSubscription(Subscriber subscriber, Subscription sub) { + Collection subsList = subs.get(subscriber); + if (subsList == null) { + subsList = new ArrayList(); + subs.put(subscriber, subsList); + } + subsList.add(sub); + } - final void addSubscriptions(Subscriber subscriber, Collection subs) { - subs.forEach(sub -> addSubscription(subscriber, sub)); - } + final void addSubscriptions(Subscriber subscriber, Collection subs) { + subs.forEach(sub -> addSubscription(subscriber, sub)); + } - final void addServerSubscription(Subscription sub) { - serverSubscriptions.add(sub); - } + final void addServerSubscription(Subscription sub) { + serverSubscriptions.add(sub); + } - final void removeSubscription(Subscriber subscriber, Subscription sub) { - final Collection subscriptions = subs.get(subscriber); - if (subscriptions == null) { - return; - } - subscriptions.remove(sub); - if (subscriptions.isEmpty()) { - subs.remove(subscriber); - } - } + final void removeSubscription(Subscriber subscriber, Subscription sub) { + final Collection subscriptions = subs.get(subscriber); + if (subscriptions == null) { + return; + } + subscriptions.remove(sub); + if (subscriptions.isEmpty()) { + subs.remove(subscriber); + } + } - final void removeSubscriptions(Subscriber subscriber, Collection subs) { - subs.forEach(sub -> removeSubscription(subscriber, sub)); - } + final void removeSubscriptions(Subscriber subscriber, Collection subs) { + subs.forEach(sub -> removeSubscription(subscriber, sub)); + } - final void removeServerSubscription(Subscription sub) { - serverSubscriptions.remove(sub); - } + final void removeServerSubscription(Subscription sub) { + serverSubscriptions.remove(sub); + } - final Set getMatchingSubscribers(Event ev) { - return getSubscribersWithAnySubscriptionMatching(sub -> sub.isSatisfiedBy(ev)); - } + final Set getMatchingSubscribers(Event ev) { + return getSubscribersWithAnySubscriptionMatching(sub -> sub.isSatisfiedBy(ev)); + } - private final Set getSubscribersWithAnySubscriptionMatching(Predicate predicate) { - final Predicate hasSubscriptionMatchingOnlySignature = subscriber -> subs.get(subscriber).stream().anyMatch(predicate); - return subs.keySet().stream().filter(hasSubscriptionMatchingOnlySignature).collect(Collectors.toSet()); - } + private final Set getSubscribersWithAnySubscriptionMatching(Predicate predicate) { + final Predicate hasSubscriptionMatchingOnlySignature = subscriber -> subs.get(subscriber).stream() + .anyMatch(predicate); + return subs.keySet().stream().filter(hasSubscriptionMatchingOnlySignature).collect(Collectors.toSet()); + } - final boolean needsToDeliverToServer(Event ev) { - return serverSubscriptions.stream().anyMatch(sub -> sub.isSatisfiedBy(ev)); - } + final boolean needsToDeliverToServer(Event ev) { + return serverSubscriptions.stream().anyMatch(sub -> sub.isSatisfiedBy(ev)); + } - @Override - public String toString() { - return "ClientSubscriptionTable [\n subs=" + subs + "\n serverSubscriptions=" + serverSubscriptions + "\n]"; - } + @Override + public String toString() { + return "ClientSubscriptionTable [\n subs=" + subs + "\n serverSubscriptions=" + serverSubscriptions + "\n]"; + } } diff --git a/DreamSim/src/dream/client/ConnectionManager.java b/DreamSim/src/dream/client/ConnectionManager.java index 7a1ec9f..936c6b5 100755 --- a/DreamSim/src/dream/client/ConnectionManager.java +++ b/DreamSim/src/dream/client/ConnectionManager.java @@ -23,83 +23,84 @@ import protopeer.network.NetworkAddress; class ConnectionManager extends BasePeerlet { - private NetworkAddress server = null; - private NetworkAddress lockManager = null; - private DreamConfiguration conf; - - private final Logger logger = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME); - - @Override - public void init(Peer peer) { - super.init(peer); - conf = DreamConfiguration.get(); - final IClientAssociationGenerator associationGenerator = ClientAssociationGenerator.get(); - final Set componentAssociations = associationGenerator.getAssociation(); - for (final Link l : componentAssociations) { - if (l.getNode2().getId() == peer.getIndexNumber()) { - server = Experiment.getSingleton().getAddressToBindTo(l.getNode1().getId()); - logger.fine("Associating component " + peer.getIndexNumber() + " to " + server); - break; - } - } - lockManager = Experiment.getSingleton().getAddressToBindTo(conf.numberOfBrokers + conf.numberOfClients + 1); - if (server == null) { - logger.warning("server is null"); - } - if (lockManager == null) { - logger.warning("lock manager is null"); - } - } - - final void sendEvent(EventPacket pkt) { - sendToServer(EventPacket.subject, pkt); - } - - final void sendSubscription(Subscription sub) { - final SubscriptionPacket pkt = new SubscriptionPacket(sub, SubType.SUB); - sendToServer(SubscriptionPacket.subject, pkt); - } - - final void sendUnsubscription(Subscription sub) { - final SubscriptionPacket pkt = new SubscriptionPacket(sub, SubType.UNSUB); - sendToServer(SubscriptionPacket.subject, pkt); - } - - final void sendAdvertisement(Advertisement adv) { - sendAdvertisement(adv, AdvType.ADV, null); - } - - final void sendAdvertisement(Advertisement adv, Set subs) { - sendAdvertisement(adv, AdvType.ADV, subs); - } - - final void sendUnadvertisement(Advertisement adv) { - sendAdvertisement(adv, AdvType.UNADV, null); - } - - final void sendUnadvertisement(Advertisement adv, Set subs) { - sendAdvertisement(adv, AdvType.UNADV, subs); - } - - final void sendLockRequest(LockRequestPacket req) { - sendToLockManager(LockRequestPacket.subject, req); - } - - final void sendLockRelease(LockReleasePacket rel) { - sendToLockManager(LockReleasePacket.subject, rel); - } - - private final void sendAdvertisement(Advertisement adv, AdvType advType, Set subs) { - final AdvertisementPacket pkt = subs != null ? new AdvertisementPacket(adv, advType, subs) : new AdvertisementPacket(adv, advType); - sendToServer(AdvertisementPacket.subject, pkt); - } - - private final void sendToServer(String subject, Message packet) { - getPeer().sendMessage(server, packet); - } - - private final void sendToLockManager(String subject, Message packet) { - getPeer().sendMessage(lockManager, packet); - } + private NetworkAddress server = null; + private NetworkAddress lockManager = null; + private DreamConfiguration conf; + + private final Logger logger = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME); + + @Override + public void init(Peer peer) { + super.init(peer); + conf = DreamConfiguration.get(); + final IClientAssociationGenerator associationGenerator = ClientAssociationGenerator.get(); + final Set componentAssociations = associationGenerator.getAssociation(); + for (final Link l : componentAssociations) { + if (l.getNode2().getId() == peer.getIndexNumber()) { + server = Experiment.getSingleton().getAddressToBindTo(l.getNode1().getId()); + logger.fine("Associating component " + peer.getIndexNumber() + " to " + server); + break; + } + } + lockManager = Experiment.getSingleton().getAddressToBindTo(conf.numberOfBrokers + conf.numberOfClients + 1); + if (server == null) { + logger.warning("server is null"); + } + if (lockManager == null) { + logger.warning("lock manager is null"); + } + } + + final void sendEvent(EventPacket pkt) { + sendToServer(EventPacket.subject, pkt); + } + + final void sendSubscription(Subscription sub) { + final SubscriptionPacket pkt = new SubscriptionPacket(sub, SubType.SUB); + sendToServer(SubscriptionPacket.subject, pkt); + } + + final void sendUnsubscription(Subscription sub) { + final SubscriptionPacket pkt = new SubscriptionPacket(sub, SubType.UNSUB); + sendToServer(SubscriptionPacket.subject, pkt); + } + + final void sendAdvertisement(Advertisement adv) { + sendAdvertisement(adv, AdvType.ADV, null); + } + + final void sendAdvertisement(Advertisement adv, Set subs) { + sendAdvertisement(adv, AdvType.ADV, subs); + } + + final void sendUnadvertisement(Advertisement adv) { + sendAdvertisement(adv, AdvType.UNADV, null); + } + + final void sendUnadvertisement(Advertisement adv, Set subs) { + sendAdvertisement(adv, AdvType.UNADV, subs); + } + + final void sendLockRequest(LockRequestPacket req) { + sendToLockManager(LockRequestPacket.subject, req); + } + + final void sendLockRelease(LockReleasePacket rel) { + sendToLockManager(LockReleasePacket.subject, rel); + } + + private final void sendAdvertisement(Advertisement adv, AdvType advType, Set subs) { + final AdvertisementPacket pkt = subs != null ? new AdvertisementPacket(adv, advType, subs) + : new AdvertisementPacket(adv, advType); + sendToServer(AdvertisementPacket.subject, pkt); + } + + private final void sendToServer(String subject, Message packet) { + getPeer().sendMessage(server, packet); + } + + private final void sendToLockManager(String subject, Message packet) { + getPeer().sendMessage(lockManager, packet); + } } diff --git a/DreamSim/src/dream/client/LockApplicant.java b/DreamSim/src/dream/client/LockApplicant.java index d891785..8fbb122 100644 --- a/DreamSim/src/dream/client/LockApplicant.java +++ b/DreamSim/src/dream/client/LockApplicant.java @@ -7,12 +7,12 @@ */ interface LockApplicant { - /** - * Method invoked when a lock is granted. - * - * @param lockGrant - * the granted lock. - */ - void notifyLockGranted(LockGrantPacket lockGrant); + /** + * Method invoked when a lock is granted. + * + * @param lockGrant + * the granted lock. + */ + void notifyLockGranted(LockGrantPacket lockGrant); } diff --git a/DreamSim/src/dream/client/QueueManager.java b/DreamSim/src/dream/client/QueueManager.java index 20530c0..b033190 100755 --- a/DreamSim/src/dream/client/QueueManager.java +++ b/DreamSim/src/dream/client/QueueManager.java @@ -19,82 +19,82 @@ * to make sure that no glitch can occur. */ class QueueManager { - // WaitingElements, partitioned by id - private final Map waitingElements = new HashMap(); - // Candidate results to deliver (they can be effectively delivered only there - // are no waiting elements). - private final Set pendingResults = new HashSet<>(); + // WaitingElements, partitioned by id + private final Map waitingElements = new HashMap(); + // Candidate results to deliver (they can be effectively delivered only there + // are no waiting elements). + private final Set pendingResults = new HashSet<>(); - final List processEventPacket(EventPacket evPkt, String expression) { - if (DreamConfiguration.get().consistencyType == DreamConfiguration.CAUSAL) { - final List result = new ArrayList<>(); - result.add(evPkt); - return result; - } + final List processEventPacket(EventPacket evPkt, String expression) { + if (DreamConfiguration.get().consistencyType == DreamConfiguration.CAUSAL) { + final List result = new ArrayList<>(); + result.add(evPkt); + return result; + } - final UUID id = evPkt.getId(); - final Set waitingRecommendations = // - IntraSourceDependencyDetector.instance.// - getWaitRecommendations(evPkt.getEvent(), evPkt.getSource()).// - stream().filter(wr -> wr.getExpression().equals(expression)).// - collect(Collectors.toSet()); + final UUID id = evPkt.getId(); + final Set waitingRecommendations = // + IntraSourceDependencyDetector.instance.// + getWaitRecommendations(evPkt.getEvent(), evPkt.getSource()).// + stream().filter(wr -> wr.getExpression().equals(expression)).// + collect(Collectors.toSet()); - if (waitingElements.containsKey(id)) { - final WaitingElement elem = waitingElements.get(id); - elem.processEvent(evPkt); - if (elem.hasFinishedWaiting()) { - elem.getReceivedEvents().forEach(pendingResults::add); - waitingElements.remove(id); - } - } else { - final Set expressionsToWaitFor = getExpressionsToWaitFor(waitingRecommendations); - if (!expressionsToWaitFor.isEmpty()) { - final WaitingElement elem = new WaitingElement(expressionsToWaitFor, evPkt); - waitingElements.put(id, elem); - } else { - pendingResults.add(evPkt); - } - } + if (waitingElements.containsKey(id)) { + final WaitingElement elem = waitingElements.get(id); + elem.processEvent(evPkt); + if (elem.hasFinishedWaiting()) { + elem.getReceivedEvents().forEach(pendingResults::add); + waitingElements.remove(id); + } + } else { + final Set expressionsToWaitFor = getExpressionsToWaitFor(waitingRecommendations); + if (!expressionsToWaitFor.isEmpty()) { + final WaitingElement elem = new WaitingElement(expressionsToWaitFor, evPkt); + waitingElements.put(id, elem); + } else { + pendingResults.add(evPkt); + } + } - final List result = new ArrayList<>(); - if (!waitingElements.containsKey(id)) { - result.addAll(pendingResults); - pendingResults.clear(); - } - return result; - } + final List result = new ArrayList<>(); + if (!waitingElements.containsKey(id)) { + result.addAll(pendingResults); + pendingResults.clear(); + } + return result; + } - private final Set getExpressionsToWaitFor(Set recommendations) { - return recommendations.stream().// - map(wr -> wr.getRecommendations()).// - collect(HashSet::new, HashSet::addAll, HashSet::addAll); - } + private final Set getExpressionsToWaitFor(Set recommendations) { + return recommendations.stream().// + map(wr -> wr.getRecommendations()).// + collect(HashSet::new, HashSet::addAll, HashSet::addAll); + } - private class WaitingElement { - // Set of expressions we are waiting for before delivering the events with - // the given id - private final Set waitingFor = new HashSet<>(); - // Set of events received with the given id - private final Set receivedEvents = new HashSet<>(); + private class WaitingElement { + // Set of expressions we are waiting for before delivering the events with + // the given id + private final Set waitingFor = new HashSet<>(); + // Set of events received with the given id + private final Set receivedEvents = new HashSet<>(); - WaitingElement(Set waitingFor, EventPacket evPkt) { - this.waitingFor.addAll(waitingFor); - receivedEvents.add(evPkt); - } + WaitingElement(Set waitingFor, EventPacket evPkt) { + this.waitingFor.addAll(waitingFor); + receivedEvents.add(evPkt); + } - final void processEvent(EventPacket evPkt) { - final String signature = evPkt.getEvent().getSignature(); - assert waitingFor.contains(signature); - waitingFor.remove(signature); - receivedEvents.add(evPkt); - } + final void processEvent(EventPacket evPkt) { + final String signature = evPkt.getEvent().getSignature(); + assert waitingFor.contains(signature); + waitingFor.remove(signature); + receivedEvents.add(evPkt); + } - final boolean hasFinishedWaiting() { - return waitingFor.isEmpty(); - } + final boolean hasFinishedWaiting() { + return waitingFor.isEmpty(); + } - final Set getReceivedEvents() { - return receivedEvents; - } - } + final Set getReceivedEvents() { + return receivedEvents; + } + } } diff --git a/DreamSim/src/dream/client/Signal.java b/DreamSim/src/dream/client/Signal.java index 5ddc8d1..9e196c6 100755 --- a/DreamSim/src/dream/client/Signal.java +++ b/DreamSim/src/dream/client/Signal.java @@ -20,144 +20,151 @@ import protopeer.util.quantities.Time; public class Signal implements LockApplicant, Subscriber { - private final ClientEventForwarder forwarder; - private final QueueManager queueManager = new QueueManager(); - DreamConfiguration conf = DreamConfiguration.get(); - - private final Peer peer; - private final String host; - private final String object; - - private final Queue pendingEvents = new LinkedList<>(); - - private final Logger logger = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME); - - public Signal(Peer peer, String host, String object, Set subs) { - this.peer = peer; - this.host = host; - this.object = object; - forwarder = (ClientEventForwarder) peer.getPeerletOfType(ClientEventForwarder.class); - forwarder.advertise(new Advertisement(host, object), subs); - subs.forEach(s -> forwarder.addSubscription(this, s)); - } - - @Override - public void notifyEventReceived(EventPacket evPkt) { - logger.finest("processTask method invoked with " + evPkt); - final List eventsList = queueManager.processEventPacket(evPkt, object + "@" + host); - logger.finest("The queueManager returned the following pairs " + eventsList); - - if (!eventsList.isEmpty()) { - logger.finest("Actual update"); - // Extract information from any of the packets - final EventPacket anyPkt = eventsList.stream().findAny().get(); - - // Save event delay - // TODO: check we are using the intended delay semantics - eventsList.forEach(ev -> { - final double delay = peer.getClock().getCurrentTime() - ev.getCreationTime(); - MeasurementLogger.getLogger().saveDelay(delay); - }); - - // Notify local and remote dependent objects - logger.finest("Sending event to dependent objects."); - final Event event = new Event(host, object); - - // Notify remote subscribers (acquiring locks if needed) - final EventPacket packet = new EventPacket(event, anyPkt.getId(), anyPkt.getCreationTime(), anyPkt.getSource()); - pendingEvents.add(packet); - if (pendingEvents.size() == 1) { - processNextEvent(); - } - - // Release locks, if needed - if (anyPkt.getLockReleaseNodes().contains(object + "@" + host) // - && (conf.consistencyType == DreamConfiguration.COMPLETE_GLITCH_FREE && LocalityDetector.instance.sourceRequiresLock(anyPkt.getSource()) || // - conf.consistencyType == DreamConfiguration.COMPLETE_GLITCH_FREE_OPTIMIZED && LocalityDetector.instance.sourceRequiresLock(anyPkt.getSource()) || // - conf.consistencyType == DreamConfiguration.ATOMIC && LocalityDetector.instance.sourceRequiresLock(anyPkt.getSource()) || // - conf.consistencyType == DreamConfiguration.SIDUP)) { - forwarder.sendLockRelease(anyPkt.getId()); - } - - } else { - logger.finest(object + ": update call but waiting: " + evPkt); - } - - } - - private final void processNextEvent() { - if (!pendingEvents.isEmpty()) { - final EventPacket nextPacket = pendingEvents.peek(); - // A signal needs to acquire a lock only in the case of optimized complete - // glitch freedom - if (conf.consistencyType == DreamConfiguration.COMPLETE_GLITCH_FREE_OPTIMIZED && // - nextPacket.getLockRequestingNode().equals(object + "@" + host)) { - final boolean lockRequired = forwarder.sendReadWriteLockRequest(nextPacket.getSource(), nextPacket.getId(), this); - if (!lockRequired) { - sendNextEventPacket(); - processNextEvent(); - } - } - // Otherwise the update can be immediately processed - else { - sendNextEventPacket(); - processNextEvent(); - } - } - } - - private final void sendNextEventPacket() { - assert !pendingEvents.isEmpty(); - final EventPacket p = pendingEvents.poll(); - forwarder.sendEvent(p.getId(), p.getEvent(), p.getCreationTime(), p.getSource()); - } - - public void atomicRead() { - if (LocalityDetector.instance.nodeRequiresReadLock(object + "@" + host)) { - acquireReadLock(); - } - } - - private final synchronized void acquireReadLock() { - if (conf.consistencyType != DreamConfiguration.ATOMIC) { - logger.warning("Trying to acquire read lock byt consistency is not atomic"); - } else { - forwarder.sendReadOnlyLockRequest(object + "@" + host, this); - } - } - - private final synchronized void releaseLock(UUID lockID) { - if (conf.consistencyType != DreamConfiguration.ATOMIC) { - logger.warning("Trying to acquire read lock byt consistency is not atomic"); - } else { - forwarder.sendLockRelease(lockID); - } - } - - @Override - public final synchronized void notifyLockGranted(LockGrantPacket lockGrant) { - final UUID lockID = lockGrant.getLockID(); - switch (lockGrant.getType()) { - // Reply to a read access to the value of the signal - case READ_ONLY: - // This is possible only in the case of atomic consistency - assert conf.consistencyType == DreamConfiguration.ATOMIC; - final int lockDuration = DreamConfiguration.get().readLockDurationInMs; - final Timer timer = peer.getClock().createNewTimer(); - timer.addTimerListener(t -> releaseLock(lockID)); - timer.schedule(Time.inMilliseconds(lockDuration)); - break; - // Reply to a read-write lock (update to the signal) - case READ_WRITE: - // This is possible only in the case of optimized complete glitch freedom - assert conf.consistencyType == DreamConfiguration.COMPLETE_GLITCH_FREE_OPTIMIZED; - assert !pendingEvents.isEmpty(); - sendNextEventPacket(); - processNextEvent(); - break; - default: - assert false : lockGrant.getType(); - } - } + private final ClientEventForwarder forwarder; + private final QueueManager queueManager = new QueueManager(); + DreamConfiguration conf = DreamConfiguration.get(); + + private final Peer peer; + private final String host; + private final String object; + + private final Queue pendingEvents = new LinkedList<>(); + + private final Logger logger = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME); + + public Signal(Peer peer, String host, String object, Set subs) { + this.peer = peer; + this.host = host; + this.object = object; + forwarder = (ClientEventForwarder) peer.getPeerletOfType(ClientEventForwarder.class); + forwarder.advertise(new Advertisement(host, object), subs); + subs.forEach(s -> forwarder.addSubscription(this, s)); + } + + @Override + public void notifyEventReceived(EventPacket evPkt) { + logger.finest("processTask method invoked with " + evPkt); + final List eventsList = queueManager.processEventPacket(evPkt, object + "@" + host); + logger.finest("The queueManager returned the following pairs " + eventsList); + + if (!eventsList.isEmpty()) { + logger.finest("Actual update"); + // Extract information from any of the packets + final EventPacket anyPkt = eventsList.stream().findAny().get(); + + // Save event delay + // TODO: check we are using the intended delay semantics + eventsList.forEach(ev -> { + final double delay = peer.getClock().getCurrentTime() - ev.getCreationTime(); + MeasurementLogger.getLogger().saveDelay(delay); + }); + + // Notify local and remote dependent objects + logger.finest("Sending event to dependent objects."); + final Event event = new Event(host, object); + + // Notify remote subscribers (acquiring locks if needed) + final EventPacket packet = new EventPacket(event, anyPkt.getId(), anyPkt.getCreationTime(), anyPkt.getSource()); + pendingEvents.add(packet); + if (pendingEvents.size() == 1) { + processNextEvent(); + } + + // Release locks, if needed + if (anyPkt.getLockReleaseNodes().contains(object + "@" + host) // + && (conf.consistencyType == DreamConfiguration.COMPLETE_GLITCH_FREE + && LocalityDetector.instance.sourceRequiresLock(anyPkt.getSource()) + || // + conf.consistencyType == DreamConfiguration.COMPLETE_GLITCH_FREE_OPTIMIZED + && LocalityDetector.instance.sourceRequiresLock(anyPkt.getSource()) + || // + conf.consistencyType == DreamConfiguration.ATOMIC + && LocalityDetector.instance.sourceRequiresLock(anyPkt.getSource()) + || // + conf.consistencyType == DreamConfiguration.SIDUP)) { + forwarder.sendLockRelease(anyPkt.getId()); + } + + } else { + logger.finest(object + ": update call but waiting: " + evPkt); + } + + } + + private final void processNextEvent() { + if (!pendingEvents.isEmpty()) { + final EventPacket nextPacket = pendingEvents.peek(); + // A signal needs to acquire a lock only in the case of optimized complete + // glitch freedom + if (conf.consistencyType == DreamConfiguration.COMPLETE_GLITCH_FREE_OPTIMIZED && // + nextPacket.getLockRequestingNode().equals(object + "@" + host)) { + final boolean lockRequired = forwarder.sendReadWriteLockRequest(nextPacket.getSource(), nextPacket.getId(), + this); + if (!lockRequired) { + sendNextEventPacket(); + processNextEvent(); + } + } + // Otherwise the update can be immediately processed + else { + sendNextEventPacket(); + processNextEvent(); + } + } + } + + private final void sendNextEventPacket() { + assert !pendingEvents.isEmpty(); + final EventPacket p = pendingEvents.poll(); + forwarder.sendEvent(p.getId(), p.getEvent(), p.getCreationTime(), p.getSource()); + } + + public void atomicRead() { + if (LocalityDetector.instance.nodeRequiresReadLock(object + "@" + host)) { + acquireReadLock(); + } + } + + private final synchronized void acquireReadLock() { + if (conf.consistencyType != DreamConfiguration.ATOMIC) { + logger.warning("Trying to acquire read lock byt consistency is not atomic"); + } else { + forwarder.sendReadOnlyLockRequest(object + "@" + host, this); + } + } + + private final synchronized void releaseLock(UUID lockID) { + if (conf.consistencyType != DreamConfiguration.ATOMIC) { + logger.warning("Trying to acquire read lock byt consistency is not atomic"); + } else { + forwarder.sendLockRelease(lockID); + } + } + + @Override + public final synchronized void notifyLockGranted(LockGrantPacket lockGrant) { + final UUID lockID = lockGrant.getLockID(); + switch (lockGrant.getType()) { + // Reply to a read access to the value of the signal + case READ_ONLY: + // This is possible only in the case of atomic consistency + assert conf.consistencyType == DreamConfiguration.ATOMIC; + final int lockDuration = DreamConfiguration.get().readLockDurationInMs; + final Timer timer = peer.getClock().createNewTimer(); + timer.addTimerListener(t -> releaseLock(lockID)); + timer.schedule(Time.inMilliseconds(lockDuration)); + break; + // Reply to a read-write lock (update to the signal) + case READ_WRITE: + // This is possible only in the case of optimized complete glitch freedom + assert conf.consistencyType == DreamConfiguration.COMPLETE_GLITCH_FREE_OPTIMIZED; + assert !pendingEvents.isEmpty(); + sendNextEventPacket(); + processNextEvent(); + break; + default: + assert false : lockGrant.getType(); + } + } } diff --git a/DreamSim/src/dream/client/Subscriber.java b/DreamSim/src/dream/client/Subscriber.java index f79c141..be94527 100644 --- a/DreamSim/src/dream/client/Subscriber.java +++ b/DreamSim/src/dream/client/Subscriber.java @@ -8,11 +8,11 @@ */ public interface Subscriber { - /** - * Notifies the subscriber that the given event has occurred. - * - * @param event - * the occurred event. - */ - public void notifyEventReceived(EventPacket event); + /** + * Notifies the subscriber that the given event has occurred. + * + * @param event + * the occurred event. + */ + public void notifyEventReceived(EventPacket event); } diff --git a/DreamSim/src/dream/client/TrafficGeneratorPeerlet.java b/DreamSim/src/dream/client/TrafficGeneratorPeerlet.java index bd667d3..0fef009 100755 --- a/DreamSim/src/dream/client/TrafficGeneratorPeerlet.java +++ b/DreamSim/src/dream/client/TrafficGeneratorPeerlet.java @@ -16,98 +16,98 @@ import protopeer.util.quantities.Time; public class TrafficGeneratorPeerlet extends BasePeerlet implements GraphGeneratorListener { - private static int clientIdCount = 0; - - private int clientId; - - public static final void resetCount() { - clientIdCount = 0; - } - - @Override - public void init(Peer peer) { - super.init(peer); - clientId = clientIdCount; - clientIdCount++; - startGraphGeneration(); - registerToGraphGenerator(); - notifyGraph(); - } - - @Override - public void notifyVar(String name) { - final Var var = new Var(getPeer(), name.split("@")[1], name.split("@")[0]); - final Timer timer = getPeer().getClock().createNewTimer(); - - timer.addTimerListener(t -> { - var.modify(); - t.schedule(Time.inMilliseconds(getTimeBetweenVarUpdateInMs())); - }); - - timer.schedule(Time.inSeconds(Consts.startSendingEventsAtSecond)); - } - - @Override - public void notifySignal(String signalName, Set dependencies) { - final Set subs = dependencies.stream()// - .map(dep -> new Subscription(dep.split("@")[1], dep.split("@")[0]))// - .collect(Collectors.toSet()); - final Signal s = new Signal(getPeer(), signalName.split("@")[1], signalName.split("@")[0], subs); - - if (DreamConfiguration.get().consistencyType == DreamConfiguration.ATOMIC) { - final Timer timer = getPeer().getClock().createNewTimer(); - - timer.addTimerListener(t -> { - s.atomicRead(); - t.schedule(Time.inMilliseconds(getTimeBetweenSignalReadInMs())); - }); - - timer.schedule(Time.inSeconds(Consts.startReadingSignalsAtSecond)); - } - } - - public int getClientId() { - return clientId; - } - - private final void registerToGraphGenerator() { - final Timer registerGraphTimer = getPeer().getClock().createNewTimer(); - registerGraphTimer.addTimerListener(timer -> { - GraphGenerator.get().addGraphGeneratorListener(TrafficGeneratorPeerlet.this, clientId); - }); - registerGraphTimer.schedule(Time.inSeconds(Consts.registerToGraphsGeneratorAtSecond)); - } - - private final void startGraphGeneration() { - final Timer graphGenTimer = getPeer().getClock().createNewTimer(); - graphGenTimer.addTimerListener(timer -> { - GraphGenerator.get().generateGraphs(clientId); - }); - graphGenTimer.schedule(Time.inSeconds(Consts.startGraphCreationAtSecond)); - } - - private final void notifyGraph() { - final Timer notifyGraphTimer = getPeer().getClock().createNewTimer(); - notifyGraphTimer.addTimerListener(timer -> { - GraphGenerator.get().notifyListeners(clientId); - }); - notifyGraphTimer.schedule(Time.inSeconds(Consts.startNotifyGraphAtSecond)); - } - - private final double getTimeBetweenVarUpdateInMs() { - final DreamConfiguration conf = DreamConfiguration.get(); - final Random rand = RandomGenerator.get(); - final int minTime = conf.minTimeBetweenEventsInMs; - final int maxTime = conf.maxTimeBetweenEventsInMs; - return minTime == maxTime ? maxTime : minTime + rand.nextInt(maxTime - minTime + 1); - } - - private final double getTimeBetweenSignalReadInMs() { - final DreamConfiguration conf = DreamConfiguration.get(); - final Random rand = RandomGenerator.get(); - final int minTime = conf.minTimeBetweenSignalReadsInMs; - final int maxTime = conf.maxTimeBetweenSignalReadsInMs; - return minTime == maxTime ? maxTime : minTime + rand.nextInt(maxTime - minTime + 1); - } + private static int clientIdCount = 0; + + private int clientId; + + public static final void resetCount() { + clientIdCount = 0; + } + + @Override + public void init(Peer peer) { + super.init(peer); + clientId = clientIdCount; + clientIdCount++; + startGraphGeneration(); + registerToGraphGenerator(); + notifyGraph(); + } + + @Override + public void notifyVar(String name) { + final Var var = new Var(getPeer(), name.split("@")[1], name.split("@")[0]); + final Timer timer = getPeer().getClock().createNewTimer(); + + timer.addTimerListener(t -> { + var.modify(); + t.schedule(Time.inMilliseconds(getTimeBetweenVarUpdateInMs())); + }); + + timer.schedule(Time.inSeconds(Consts.startSendingEventsAtSecond)); + } + + @Override + public void notifySignal(String signalName, Set dependencies) { + final Set subs = dependencies.stream()// + .map(dep -> new Subscription(dep.split("@")[1], dep.split("@")[0]))// + .collect(Collectors.toSet()); + final Signal s = new Signal(getPeer(), signalName.split("@")[1], signalName.split("@")[0], subs); + + if (DreamConfiguration.get().consistencyType == DreamConfiguration.ATOMIC) { + final Timer timer = getPeer().getClock().createNewTimer(); + + timer.addTimerListener(t -> { + s.atomicRead(); + t.schedule(Time.inMilliseconds(getTimeBetweenSignalReadInMs())); + }); + + timer.schedule(Time.inSeconds(Consts.startReadingSignalsAtSecond)); + } + } + + public int getClientId() { + return clientId; + } + + private final void registerToGraphGenerator() { + final Timer registerGraphTimer = getPeer().getClock().createNewTimer(); + registerGraphTimer.addTimerListener(timer -> { + GraphGenerator.get().addGraphGeneratorListener(TrafficGeneratorPeerlet.this, clientId); + }); + registerGraphTimer.schedule(Time.inSeconds(Consts.registerToGraphsGeneratorAtSecond)); + } + + private final void startGraphGeneration() { + final Timer graphGenTimer = getPeer().getClock().createNewTimer(); + graphGenTimer.addTimerListener(timer -> { + GraphGenerator.get().generateGraphs(clientId); + }); + graphGenTimer.schedule(Time.inSeconds(Consts.startGraphCreationAtSecond)); + } + + private final void notifyGraph() { + final Timer notifyGraphTimer = getPeer().getClock().createNewTimer(); + notifyGraphTimer.addTimerListener(timer -> { + GraphGenerator.get().notifyListeners(clientId); + }); + notifyGraphTimer.schedule(Time.inSeconds(Consts.startNotifyGraphAtSecond)); + } + + private final double getTimeBetweenVarUpdateInMs() { + final DreamConfiguration conf = DreamConfiguration.get(); + final Random rand = RandomGenerator.get(); + final int minTime = conf.minTimeBetweenEventsInMs; + final int maxTime = conf.maxTimeBetweenEventsInMs; + return minTime == maxTime ? maxTime : minTime + rand.nextInt(maxTime - minTime + 1); + } + + private final double getTimeBetweenSignalReadInMs() { + final DreamConfiguration conf = DreamConfiguration.get(); + final Random rand = RandomGenerator.get(); + final int minTime = conf.minTimeBetweenSignalReadsInMs; + final int maxTime = conf.maxTimeBetweenSignalReadsInMs; + return minTime == maxTime ? maxTime : minTime + rand.nextInt(maxTime - minTime + 1); + } } diff --git a/DreamSim/src/dream/client/Var.java b/DreamSim/src/dream/client/Var.java index 494828a..e3347cc 100755 --- a/DreamSim/src/dream/client/Var.java +++ b/DreamSim/src/dream/client/Var.java @@ -13,66 +13,72 @@ import protopeer.Peer; public class Var implements LockApplicant { - private final ClientEventForwarder forwarder; - DreamConfiguration conf; + private final ClientEventForwarder forwarder; + DreamConfiguration conf; - private final Peer peer; - private final String host; - private final String object; + private final Peer peer; + private final String host; + private final String object; - private final Queue pendingEvents = new LinkedList<>(); + private final Queue pendingEvents = new LinkedList<>(); - public Var(Peer peer, String host, String object) { - this.peer = peer; - this.forwarder = (ClientEventForwarder) peer.getPeerletOfType(ClientEventForwarder.class); - this.host = host; - this.object = object; - conf = DreamConfiguration.get(); - forwarder.advertise(new Advertisement(host, object)); - } + public Var(Peer peer, String host, String object) { + this.peer = peer; + this.forwarder = (ClientEventForwarder) peer.getPeerletOfType(ClientEventForwarder.class); + this.host = host; + this.object = object; + conf = DreamConfiguration.get(); + forwarder.advertise(new Advertisement(host, object)); + } - public final void modify() { - final EventPacket ev = new EventPacket(new Event(host, object), UUID.randomUUID(), peer.getClock().getCurrentTime(), object + "@" + host); - pendingEvents.add(ev); - if (pendingEvents.size() == 1) { - processNextEvent(); - } - } + public final void modify() { + final EventPacket ev = new EventPacket(new Event(host, object), UUID.randomUUID(), peer.getClock().getCurrentTime(), + object + "@" + host); + pendingEvents.add(ev); + if (pendingEvents.size() == 1) { + processNextEvent(); + } + } - private final void processNextEvent() { - if (!pendingEvents.isEmpty()) { - final EventPacket packet = pendingEvents.peek(); - // In the case of complete glitch freedom or atomic consistency, we - // possibly need to acquire a lock before processing the update - if (conf.consistencyType == DreamConfiguration.COMPLETE_GLITCH_FREE && LocalityDetector.instance.sourceRequiresLock(object + "@" + host) || // - conf.consistencyType == DreamConfiguration.ATOMIC && LocalityDetector.instance.sourceRequiresLock(object + "@" + host) || // - conf.consistencyType == DreamConfiguration.SIDUP || // - conf.consistencyType == DreamConfiguration.COMPLETE_GLITCH_FREE_OPTIMIZED && packet.getLockRequestingNode().equals(object + "@" + host)) { - final boolean lockRequired = forwarder.sendReadWriteLockRequest(packet.getSource(), packet.getId(), this); - if (!lockRequired) { - sendNextEventPacket(); - processNextEvent(); - } - } - // Otherwise the update can be immediately processed - else { - sendNextEventPacket(); - processNextEvent(); - } - } - } + private final void processNextEvent() { + if (!pendingEvents.isEmpty()) { + final EventPacket packet = pendingEvents.peek(); + // In the case of complete glitch freedom or atomic consistency, we + // possibly need to acquire a lock before processing the update + if (conf.consistencyType == DreamConfiguration.COMPLETE_GLITCH_FREE + && LocalityDetector.instance.sourceRequiresLock(object + "@" + host) + || // + conf.consistencyType == DreamConfiguration.ATOMIC + && LocalityDetector.instance.sourceRequiresLock(object + "@" + host) + || // + conf.consistencyType == DreamConfiguration.SIDUP || // + conf.consistencyType == DreamConfiguration.COMPLETE_GLITCH_FREE_OPTIMIZED + && packet.getLockRequestingNode().equals(object + "@" + host)) { + final boolean lockRequired = forwarder.sendReadWriteLockRequest(packet.getSource(), packet.getId(), this); + if (!lockRequired) { + sendNextEventPacket(); + processNextEvent(); + } + } + // Otherwise the update can be immediately processed + else { + sendNextEventPacket(); + processNextEvent(); + } + } + } - private final void sendNextEventPacket() { - assert !pendingEvents.isEmpty(); - final EventPacket p = pendingEvents.poll(); - forwarder.sendEvent(p.getId(), p.getEvent(), p.getCreationTime(), p.getEvent().getSignature()); - } + private final void sendNextEventPacket() { + assert !pendingEvents.isEmpty(); + final EventPacket p = pendingEvents.poll(); + forwarder.sendEvent(p.getId(), p.getEvent(), p.getCreationTime(), p.getEvent().getSignature()); + } - @Override - public void notifyLockGranted(LockGrantPacket lockGrant) { - assert !pendingEvents.isEmpty(); - sendNextEventPacket(); - processNextEvent(); - } + @Override + public void notifyLockGranted(LockGrantPacket lockGrant) { + assert !pendingEvents.isEmpty(); + sendNextEventPacket(); + processNextEvent(); + } } diff --git a/DreamSim/src/dream/common/Consts.java b/DreamSim/src/dream/common/Consts.java index 5a98422..d9aa78d 100644 --- a/DreamSim/src/dream/common/Consts.java +++ b/DreamSim/src/dream/common/Consts.java @@ -8,30 +8,30 @@ import java.util.logging.Logger; public final class Consts { - private static final String LOGGING_PROPERTIES_FILE_NAME = "logging.properties"; + private static final String LOGGING_PROPERTIES_FILE_NAME = "logging.properties"; - public static final String hostPrefix = "host"; - public static final String objPrefix = "obj"; + public static final String hostPrefix = "host"; + public static final String objPrefix = "obj"; - public static final double startGraphCreationAtSecond = 1; - public static final double registerToGraphsGeneratorAtSecond = 2; - public static final double startNotifyGraphAtSecond = 3; - public static final double delayBeforeSendingSubscriptions = 1; - public static final double startSendingEventsAtSecond = 10; - public static final double startReadingSignalsAtSecond = 10; + public static final double startGraphCreationAtSecond = 1; + public static final double registerToGraphsGeneratorAtSecond = 2; + public static final double startNotifyGraphAtSecond = 3; + public static final double delayBeforeSendingSubscriptions = 1; + public static final double startSendingEventsAtSecond = 10; + public static final double startReadingSignalsAtSecond = 10; - static { - /** - * Read logging properties - */ - final LogManager manager = LogManager.getLogManager(); - try { - manager.readConfiguration(new FileInputStream(new File(LOGGING_PROPERTIES_FILE_NAME))); - } catch (SecurityException | IOException e) { - e.printStackTrace(); - } - final Logger logger = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME); - final ConsoleHandler consoleHandler = new ConsoleHandler(); - logger.addHandler(consoleHandler); - } + static { + /** + * Read logging properties + */ + final LogManager manager = LogManager.getLogManager(); + try { + manager.readConfiguration(new FileInputStream(new File(LOGGING_PROPERTIES_FILE_NAME))); + } catch (SecurityException | IOException e) { + e.printStackTrace(); + } + final Logger logger = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME); + final ConsoleHandler consoleHandler = new ConsoleHandler(); + logger.addHandler(consoleHandler); + } } diff --git a/DreamSim/src/dream/common/Outbox.java b/DreamSim/src/dream/common/Outbox.java index a059eb8..52f9ce0 100755 --- a/DreamSim/src/dream/common/Outbox.java +++ b/DreamSim/src/dream/common/Outbox.java @@ -30,24 +30,24 @@ import protopeer.network.NetworkAddress; public class Outbox { - private final Map> packetsToSend = new HashMap>(); - - // Subject is not used, but introduced to preserve API compatibility - public void add(String subject, Message packet, Collection recipients) { - Collection addressSet = packetsToSend.get(packet); - if (addressSet == null) { - addressSet = new HashSet(); - packetsToSend.put(packet, addressSet); - } - addressSet.addAll(recipients); - } - - public Set getPacketsToSend() { - return packetsToSend.keySet(); - } - - public Collection getRecipientsFor(Message packet) { - return packetsToSend.get(packet); - } + private final Map> packetsToSend = new HashMap>(); + + // Subject is not used, but introduced to preserve API compatibility + public void add(String subject, Message packet, Collection recipients) { + Collection addressSet = packetsToSend.get(packet); + if (addressSet == null) { + addressSet = new HashSet(); + packetsToSend.put(packet, addressSet); + } + addressSet.addAll(recipients); + } + + public Set getPacketsToSend() { + return packetsToSend.keySet(); + } + + public Collection getRecipientsFor(Message packet) { + return packetsToSend.get(packet); + } } diff --git a/DreamSim/src/dream/common/packets/AdvertisementPacket.java b/DreamSim/src/dream/common/packets/AdvertisementPacket.java index 43ef396..8470ec5 100755 --- a/DreamSim/src/dream/common/packets/AdvertisementPacket.java +++ b/DreamSim/src/dream/common/packets/AdvertisementPacket.java @@ -17,48 +17,48 @@ * includes the set of subscriptions that define such dependencies. */ public class AdvertisementPacket extends Message implements Serializable { - private static final long serialVersionUID = 5219175796450319466L; - public static final String subject = "__DREAM_ADVERTISEMENT_PACKET_SUBJECT"; + private static final long serialVersionUID = 5219175796450319466L; + public static final String subject = "__DREAM_ADVERTISEMENT_PACKET_SUBJECT"; - private final Advertisement advertisement; - private final AdvType advType; - private final Set subscriptions = new HashSet<>(); + private final Advertisement advertisement; + private final AdvType advType; + private final Set subscriptions = new HashSet<>(); - public AdvertisementPacket(Advertisement advertisement, AdvType advType, Set subscriptions) { - this(advertisement, advType); - this.subscriptions.addAll(subscriptions); - } + public AdvertisementPacket(Advertisement advertisement, AdvType advType, Set subscriptions) { + this(advertisement, advType); + this.subscriptions.addAll(subscriptions); + } - public AdvertisementPacket(Advertisement advertisement, AdvType advType) { - this.advertisement = advertisement; - this.advType = advType; - } + public AdvertisementPacket(Advertisement advertisement, AdvType advType) { + this.advertisement = advertisement; + this.advType = advType; + } - public Advertisement getAdvertisement() { - return advertisement; - } + public Advertisement getAdvertisement() { + return advertisement; + } - public AdvType getAdvType() { - return advType; - } + public AdvType getAdvType() { + return advType; + } - public final Set getSubscriptions() { - return subscriptions; - } + public final Set getSubscriptions() { + return subscriptions; + } - @Override - public Data getSize() { - // TODO: estimate the real size - return Data.inKByte(1); - } + @Override + public Data getSize() { + // TODO: estimate the real size + return Data.inKByte(1); + } - @Override - public String toString() { - if (subscriptions != null) { - return advType + ": " + advertisement.toString() + " - Depending on: " + subscriptions; - } else { - return advType + ": " + advertisement.toString(); - } - } + @Override + public String toString() { + if (subscriptions != null) { + return advType + ": " + advertisement.toString() + " - Depending on: " + subscriptions; + } else { + return advType + ": " + advertisement.toString(); + } + } } diff --git a/DreamSim/src/dream/common/packets/EventPacket.java b/DreamSim/src/dream/common/packets/EventPacket.java index af30c0b..53adb3e 100755 --- a/DreamSim/src/dream/common/packets/EventPacket.java +++ b/DreamSim/src/dream/common/packets/EventPacket.java @@ -15,77 +15,78 @@ * Packet used to deliver events, which notify about some state change. */ public class EventPacket extends Message implements Serializable { - private static final long serialVersionUID = 8208653909787190211L; - public static final String subject = "__DREAM_PUBLICATION_PACKET_SUBJECT"; - - private final Event event; - - // Uniquely identifies a propagation. In the case of complete glitch free and - // atomic consistency, it is identical to the id of the read lock associated - // to the propagation. - private final UUID id; - - // Creation time - private final double creationTime; - - // Original source of the change - private final String source; - - // Node that should request the lock for the propagation, if any - private final String lockRequestingNode; - - // Nodes that should release the lock for the propagation, if any - private final Set lockReleaseNodes = new HashSet<>(); - - public EventPacket(Event event, UUID id, double creationTime, String source) { - this.event = event; - this.id = id; - this.creationTime = creationTime; - this.source = source; - this.lockRequestingNode = // - DreamConfiguration.get().consistencyType == DreamConfiguration.COMPLETE_GLITCH_FREE_OPTIMIZED // - ? CompleteGlitchFreeDependencyDetector.instance.getLockRequestNodeFor(source) // - : source; - } - - public final Event getEvent() { - return event; - } - - public final UUID getId() { - return id; - } - - public final double getCreationTime() { - return creationTime; - } - - public final String getSource() { - return source; - } - - public final String getLockRequestingNode() { - return lockRequestingNode; - } - - public final void setLockReleaseNodes(Set lockReleaseNodes) { - this.lockReleaseNodes.addAll(lockReleaseNodes); - lockReleaseNodes.remove(lockRequestingNode); - } - - public final Set getLockReleaseNodes() { - return lockReleaseNodes; - } - - @Override - public Data getSize() { - // TODO: estimate the real size - return Data.inKByte(1); - } - - @Override - public String toString() { - return "EventPacket [event=" + event + ", id=" + id + ", source=" + source + ", lockReleaseNodes=" + lockReleaseNodes + "]"; - } + private static final long serialVersionUID = 8208653909787190211L; + public static final String subject = "__DREAM_PUBLICATION_PACKET_SUBJECT"; + + private final Event event; + + // Uniquely identifies a propagation. In the case of complete glitch free and + // atomic consistency, it is identical to the id of the read lock associated + // to the propagation. + private final UUID id; + + // Creation time + private final double creationTime; + + // Original source of the change + private final String source; + + // Node that should request the lock for the propagation, if any + private final String lockRequestingNode; + + // Nodes that should release the lock for the propagation, if any + private final Set lockReleaseNodes = new HashSet<>(); + + public EventPacket(Event event, UUID id, double creationTime, String source) { + this.event = event; + this.id = id; + this.creationTime = creationTime; + this.source = source; + this.lockRequestingNode = // + DreamConfiguration.get().consistencyType == DreamConfiguration.COMPLETE_GLITCH_FREE_OPTIMIZED // + ? CompleteGlitchFreeDependencyDetector.instance.getLockRequestNodeFor(source) // + : source; + } + + public final Event getEvent() { + return event; + } + + public final UUID getId() { + return id; + } + + public final double getCreationTime() { + return creationTime; + } + + public final String getSource() { + return source; + } + + public final String getLockRequestingNode() { + return lockRequestingNode; + } + + public final void setLockReleaseNodes(Set lockReleaseNodes) { + this.lockReleaseNodes.addAll(lockReleaseNodes); + lockReleaseNodes.remove(lockRequestingNode); + } + + public final Set getLockReleaseNodes() { + return lockReleaseNodes; + } + + @Override + public Data getSize() { + // TODO: estimate the real size + return Data.inKByte(1); + } + + @Override + public String toString() { + return "EventPacket [event=" + event + ", id=" + id + ", source=" + source + ", lockReleaseNodes=" + + lockReleaseNodes + "]"; + } } diff --git a/DreamSim/src/dream/common/packets/SubscriptionPacket.java b/DreamSim/src/dream/common/packets/SubscriptionPacket.java index 606fa28..1896011 100755 --- a/DreamSim/src/dream/common/packets/SubscriptionPacket.java +++ b/DreamSim/src/dream/common/packets/SubscriptionPacket.java @@ -12,34 +12,34 @@ * specific events. */ public class SubscriptionPacket extends Message implements Serializable { - private static final long serialVersionUID = -9026500933220636540L; - public static final String subject = "__DREAM_SUBSCRIPTION_PACKET_SUBJECT"; - - private final Subscription subscription; - private final SubType subType; - - public SubscriptionPacket(Subscription subscription, SubType subType) { - this.subscription = subscription; - this.subType = subType; - } - - public final SubType getSubType() { - return subType; - } - - public final Subscription getSubscription() { - return subscription; - } - - @Override - public Data getSize() { - // TODO: estimate the real size - return Data.inKByte(1); - } - - @Override - public String toString() { - return subType + " " + subscription.toString(); - } + private static final long serialVersionUID = -9026500933220636540L; + public static final String subject = "__DREAM_SUBSCRIPTION_PACKET_SUBJECT"; + + private final Subscription subscription; + private final SubType subType; + + public SubscriptionPacket(Subscription subscription, SubType subType) { + this.subscription = subscription; + this.subType = subType; + } + + public final SubType getSubType() { + return subType; + } + + public final Subscription getSubscription() { + return subscription; + } + + @Override + public Data getSize() { + // TODO: estimate the real size + return Data.inKByte(1); + } + + @Override + public String toString() { + return subType + " " + subscription.toString(); + } } diff --git a/DreamSim/src/dream/common/packets/content/AdvType.java b/DreamSim/src/dream/common/packets/content/AdvType.java index 3aa2541..a6653f2 100755 --- a/DreamSim/src/dream/common/packets/content/AdvType.java +++ b/DreamSim/src/dream/common/packets/content/AdvType.java @@ -1,5 +1,5 @@ package dream.common.packets.content; public enum AdvType { - ADV, UNADV + ADV, UNADV } diff --git a/DreamSim/src/dream/common/packets/content/Advertisement.java b/DreamSim/src/dream/common/packets/content/Advertisement.java index 240c49f..985bb18 100755 --- a/DreamSim/src/dream/common/packets/content/Advertisement.java +++ b/DreamSim/src/dream/common/packets/content/Advertisement.java @@ -3,35 +3,35 @@ import java.io.Serializable; public class Advertisement implements Serializable { - private static final long serialVersionUID = -6636280874981657399L; + private static final long serialVersionUID = -6636280874981657399L; - private final String hostId; - private final String objectId; + private final String hostId; + private final String objectId; - public Advertisement(String hostId, String objectId) { - this.hostId = hostId; - this.objectId = objectId; - } + public Advertisement(String hostId, String objectId) { + this.hostId = hostId; + this.objectId = objectId; + } - public boolean isSatisfiedBy(Subscription sub) { - return hostId.equals(sub.getHostId()) && objectId.equals(sub.getObjectId()); - } + public boolean isSatisfiedBy(Subscription sub) { + return hostId.equals(sub.getHostId()) && objectId.equals(sub.getObjectId()); + } - public final String getObjectId() { - return objectId; - } + public final String getObjectId() { + return objectId; + } - public final String getHostId() { - return hostId; - } + public final String getHostId() { + return hostId; + } - public final String getSignature() { - return objectId + "@" + hostId; - } + public final String getSignature() { + return objectId + "@" + hostId; + } - @Override - public String toString() { - return "Advertisement [" + getSignature() + "]"; - } + @Override + public String toString() { + return "Advertisement [" + getSignature() + "]"; + } } diff --git a/DreamSim/src/dream/common/packets/content/Event.java b/DreamSim/src/dream/common/packets/content/Event.java index 6916a6c..98ee64e 100755 --- a/DreamSim/src/dream/common/packets/content/Event.java +++ b/DreamSim/src/dream/common/packets/content/Event.java @@ -3,31 +3,31 @@ import java.io.Serializable; public class Event implements Serializable { - private static final long serialVersionUID = 831217881290695190L; + private static final long serialVersionUID = 831217881290695190L; - private final String objectId; - private final String hostId; + private final String objectId; + private final String hostId; - public Event(String hostId, String objectId) { - this.hostId = hostId; - this.objectId = objectId; - } + public Event(String hostId, String objectId) { + this.hostId = hostId; + this.objectId = objectId; + } - public final String getObjectId() { - return objectId; - } + public final String getObjectId() { + return objectId; + } - public final String getHostId() { - return hostId; - } + public final String getHostId() { + return hostId; + } - public final String getSignature() { - return objectId + "@" + hostId; - } + public final String getSignature() { + return objectId + "@" + hostId; + } - @Override - public String toString() { - return "Event [" + getSignature() + "]"; - } + @Override + public String toString() { + return "Event [" + getSignature() + "]"; + } } diff --git a/DreamSim/src/dream/common/packets/content/SubType.java b/DreamSim/src/dream/common/packets/content/SubType.java index a328524..53fa264 100755 --- a/DreamSim/src/dream/common/packets/content/SubType.java +++ b/DreamSim/src/dream/common/packets/content/SubType.java @@ -1,5 +1,5 @@ package dream.common.packets.content; public enum SubType { - SUB, UNSUB + SUB, UNSUB } diff --git a/DreamSim/src/dream/common/packets/content/Subscription.java b/DreamSim/src/dream/common/packets/content/Subscription.java index 6b59356..d526c9a 100755 --- a/DreamSim/src/dream/common/packets/content/Subscription.java +++ b/DreamSim/src/dream/common/packets/content/Subscription.java @@ -3,35 +3,35 @@ import java.io.Serializable; public class Subscription implements Serializable { - private static final long serialVersionUID = -3452847781395458670L; + private static final long serialVersionUID = -3452847781395458670L; - private final String objectId; - private final String hostId; + private final String objectId; + private final String hostId; - public Subscription(String hostId, String objectId) { - this.hostId = hostId; - this.objectId = objectId; - } + public Subscription(String hostId, String objectId) { + this.hostId = hostId; + this.objectId = objectId; + } - public final boolean isSatisfiedBy(Event ev) { - return ev.getHostId().equals(hostId) && ev.getObjectId().equals(objectId); - } + public final boolean isSatisfiedBy(Event ev) { + return ev.getHostId().equals(hostId) && ev.getObjectId().equals(objectId); + } - public final String getObjectId() { - return objectId; - } + public final String getObjectId() { + return objectId; + } - public final String getHostId() { - return hostId; - } + public final String getHostId() { + return hostId; + } - public final String getSignature() { - return objectId + "@" + hostId; - } + public final String getSignature() { + return objectId + "@" + hostId; + } - @Override - public String toString() { - return "Subscription[" + getSignature() + "]"; - } + @Override + public String toString() { + return "Subscription[" + getSignature() + "]"; + } } diff --git a/DreamSim/src/dream/common/packets/discovery/LockManagerHelloPacket.java b/DreamSim/src/dream/common/packets/discovery/LockManagerHelloPacket.java index 1f3cd04..9c94dd1 100644 --- a/DreamSim/src/dream/common/packets/discovery/LockManagerHelloPacket.java +++ b/DreamSim/src/dream/common/packets/discovery/LockManagerHelloPacket.java @@ -7,7 +7,7 @@ * connects to it. */ public class LockManagerHelloPacket implements Serializable { - private static final long serialVersionUID = -7563002622651694641L; + private static final long serialVersionUID = -7563002622651694641L; - public static final String subject = "__DREAM_LOCK_MANAGER_HELLO_PACKET_SUBJECT"; + public static final String subject = "__DREAM_LOCK_MANAGER_HELLO_PACKET_SUBJECT"; } diff --git a/DreamSim/src/dream/common/packets/discovery/ServerHelloPacket.java b/DreamSim/src/dream/common/packets/discovery/ServerHelloPacket.java index 6e6b227..35f98f0 100644 --- a/DreamSim/src/dream/common/packets/discovery/ServerHelloPacket.java +++ b/DreamSim/src/dream/common/packets/discovery/ServerHelloPacket.java @@ -7,7 +7,7 @@ * connects to it. */ public class ServerHelloPacket implements Serializable { - private static final long serialVersionUID = 3429557416466895040L; + private static final long serialVersionUID = 3429557416466895040L; - public static final String subject = "__DREAM_SERVER_HELLO_PACKET_SUBJECT"; + public static final String subject = "__DREAM_SERVER_HELLO_PACKET_SUBJECT"; } diff --git a/DreamSim/src/dream/common/packets/locking/LockGrantPacket.java b/DreamSim/src/dream/common/packets/locking/LockGrantPacket.java index 106b606..abf1218 100644 --- a/DreamSim/src/dream/common/packets/locking/LockGrantPacket.java +++ b/DreamSim/src/dream/common/packets/locking/LockGrantPacket.java @@ -7,34 +7,34 @@ import protopeer.util.quantities.Data; public class LockGrantPacket extends Message implements Serializable { - private static final long serialVersionUID = -3499224800050816098L; - public static final String subject = "__DREAM_LOCK_GRANT_PACKET_SUBJECT"; - - private final UUID lockID; - private final LockType type; - - public LockGrantPacket(LockRequestPacket reqPkt) { - this.lockID = reqPkt.getLockID(); - this.type = reqPkt.getType(); - } - - public final UUID getLockID() { - return lockID; - } - - public final LockType getType() { - return type; - } - - @Override - public Data getSize() { - // TODO: estimate the real size - return Data.inKByte(1); - } - - @Override - public String toString() { - return "LockGrantPacket [lockID=" + lockID + "]"; - } + private static final long serialVersionUID = -3499224800050816098L; + public static final String subject = "__DREAM_LOCK_GRANT_PACKET_SUBJECT"; + + private final UUID lockID; + private final LockType type; + + public LockGrantPacket(LockRequestPacket reqPkt) { + this.lockID = reqPkt.getLockID(); + this.type = reqPkt.getType(); + } + + public final UUID getLockID() { + return lockID; + } + + public final LockType getType() { + return type; + } + + @Override + public Data getSize() { + // TODO: estimate the real size + return Data.inKByte(1); + } + + @Override + public String toString() { + return "LockGrantPacket [lockID=" + lockID + "]"; + } } diff --git a/DreamSim/src/dream/common/packets/locking/LockReleasePacket.java b/DreamSim/src/dream/common/packets/locking/LockReleasePacket.java index 586cd8c..55ef18e 100644 --- a/DreamSim/src/dream/common/packets/locking/LockReleasePacket.java +++ b/DreamSim/src/dream/common/packets/locking/LockReleasePacket.java @@ -11,28 +11,28 @@ * assumes that requests are sequential (e.g., generated from a single server). */ public class LockReleasePacket extends Message implements Serializable { - private static final long serialVersionUID = -1523880233653918696L; - public static final String subject = "__DREAM_LOCK_RELEASE_PACKET_SUBJECT"; + private static final long serialVersionUID = -1523880233653918696L; + public static final String subject = "__DREAM_LOCK_RELEASE_PACKET_SUBJECT"; - private final UUID lockID; + private final UUID lockID; - public LockReleasePacket(UUID lockID) { - this.lockID = lockID; - } + public LockReleasePacket(UUID lockID) { + this.lockID = lockID; + } - public final UUID getLockID() { - return lockID; - } + public final UUID getLockID() { + return lockID; + } - @Override - public Data getSize() { - // TODO: estimate the real size - return Data.inKByte(1); - } + @Override + public Data getSize() { + // TODO: estimate the real size + return Data.inKByte(1); + } - @Override - public String toString() { - return "LockReleasePacket [lockID=" + lockID + "]"; - } + @Override + public String toString() { + return "LockReleasePacket [lockID=" + lockID + "]"; + } } diff --git a/DreamSim/src/dream/common/packets/locking/LockRequestPacket.java b/DreamSim/src/dream/common/packets/locking/LockRequestPacket.java index a2bc07a..7e4699f 100644 --- a/DreamSim/src/dream/common/packets/locking/LockRequestPacket.java +++ b/DreamSim/src/dream/common/packets/locking/LockRequestPacket.java @@ -10,64 +10,65 @@ import protopeer.util.quantities.Data; public class LockRequestPacket extends Message implements Serializable { - private static final long serialVersionUID = -1523880233653918696L; - public static final String subject = "__DREAM_LOCK_REQUEST_PACKET_SUBJECT"; - - /** - * Node that requests the lock. - */ - private final NetworkAddress applicant; - - /** - * Nodes to lock. - */ - private final Set lockNodes; - - /** - * Nodes that will sent a lock release. - */ - private final Set unlockNodes; - - private final LockType type; - private final UUID lockID; - - public LockRequestPacket(NetworkAddress applicant, UUID lockID, Set lockNodes, Set unlockNodes, LockType type) { - this.applicant = applicant; - this.lockID = lockID; - this.lockNodes = new HashSet<>(lockNodes); - this.unlockNodes = new HashSet<>(unlockNodes); - this.type = type; - } - - public final NetworkAddress getApplicant() { - return applicant; - } - - public final Set getLockNodes() { - return lockNodes; - } - - public final Set getUnlockNodes() { - return unlockNodes; - } - - public final LockType getType() { - return type; - } - - public final UUID getLockID() { - return lockID; - } - - @Override - public Data getSize() { - // TODO: estimate the real size - return Data.inKByte(1); - } - - @Override - public String toString() { - return "LockRequestPacket [lockID=" + lockID + ", lockNodes=" + lockNodes + "]"; - } + private static final long serialVersionUID = -1523880233653918696L; + public static final String subject = "__DREAM_LOCK_REQUEST_PACKET_SUBJECT"; + + /** + * Node that requests the lock. + */ + private final NetworkAddress applicant; + + /** + * Nodes to lock. + */ + private final Set lockNodes; + + /** + * Nodes that will sent a lock release. + */ + private final Set unlockNodes; + + private final LockType type; + private final UUID lockID; + + public LockRequestPacket(NetworkAddress applicant, UUID lockID, Set lockNodes, Set unlockNodes, + LockType type) { + this.applicant = applicant; + this.lockID = lockID; + this.lockNodes = new HashSet<>(lockNodes); + this.unlockNodes = new HashSet<>(unlockNodes); + this.type = type; + } + + public final NetworkAddress getApplicant() { + return applicant; + } + + public final Set getLockNodes() { + return lockNodes; + } + + public final Set getUnlockNodes() { + return unlockNodes; + } + + public final LockType getType() { + return type; + } + + public final UUID getLockID() { + return lockID; + } + + @Override + public Data getSize() { + // TODO: estimate the real size + return Data.inKByte(1); + } + + @Override + public String toString() { + return "LockRequestPacket [lockID=" + lockID + ", lockNodes=" + lockNodes + "]"; + } } diff --git a/DreamSim/src/dream/common/packets/locking/LockType.java b/DreamSim/src/dream/common/packets/locking/LockType.java index b5b785d..bc07cdb 100644 --- a/DreamSim/src/dream/common/packets/locking/LockType.java +++ b/DreamSim/src/dream/common/packets/locking/LockType.java @@ -1,5 +1,5 @@ package dream.common.packets.locking; public enum LockType { - READ_WRITE, READ_ONLY + READ_WRITE, READ_ONLY } diff --git a/DreamSim/src/dream/common/packets/overlay/AddBrokerMessage.java b/DreamSim/src/dream/common/packets/overlay/AddBrokerMessage.java index 6ca6837..807adf6 100644 --- a/DreamSim/src/dream/common/packets/overlay/AddBrokerMessage.java +++ b/DreamSim/src/dream/common/packets/overlay/AddBrokerMessage.java @@ -10,32 +10,32 @@ */ public class AddBrokerMessage extends Message { - private static final long serialVersionUID = -7791413194580191036L; - - private final NetworkAddress newBroker; - - /** - * Create a new broker. - * - * @param newBroker - * New broker to add. - */ - public AddBrokerMessage(NetworkAddress newBroker) { - this.newBroker = newBroker; - } - - /** - * Get the address of the new broker the new broker to add. - * - * @return The new broker to add. - */ - public NetworkAddress getNewBroker() { - return newBroker; - } - - @Override - public String toString() { - return "ADDBROKER (+" + newBroker + ")"; - } + private static final long serialVersionUID = -7791413194580191036L; + + private final NetworkAddress newBroker; + + /** + * Create a new broker. + * + * @param newBroker + * New broker to add. + */ + public AddBrokerMessage(NetworkAddress newBroker) { + this.newBroker = newBroker; + } + + /** + * Get the address of the new broker the new broker to add. + * + * @return The new broker to add. + */ + public NetworkAddress getNewBroker() { + return newBroker; + } + + @Override + public String toString() { + return "ADDBROKER (+" + newBroker + ")"; + } } diff --git a/DreamSim/src/dream/common/packets/overlay/RemoveBrokerMessage.java b/DreamSim/src/dream/common/packets/overlay/RemoveBrokerMessage.java index a0c3c2f..d229d4d 100644 --- a/DreamSim/src/dream/common/packets/overlay/RemoveBrokerMessage.java +++ b/DreamSim/src/dream/common/packets/overlay/RemoveBrokerMessage.java @@ -10,31 +10,31 @@ */ public class RemoveBrokerMessage extends Message { - private static final long serialVersionUID = 5205869805158147763L; + private static final long serialVersionUID = 5205869805158147763L; - private final NetworkAddress existingBroker; + private final NetworkAddress existingBroker; - /** - * Creates a message for removing an existing broker. - * - * @param existingBroker - * Existing broker to remove - */ - public RemoveBrokerMessage(NetworkAddress existingBroker) { - this.existingBroker = existingBroker; - } + /** + * Creates a message for removing an existing broker. + * + * @param existingBroker + * Existing broker to remove + */ + public RemoveBrokerMessage(NetworkAddress existingBroker) { + this.existingBroker = existingBroker; + } - /** - * Get the address of the existing broker to remove. - * - * @return Existing broker to remove - */ - public NetworkAddress getExistingBroker() { - return existingBroker; - } + /** + * Get the address of the existing broker to remove. + * + * @return Existing broker to remove + */ + public NetworkAddress getExistingBroker() { + return existingBroker; + } - @Override - public String toString() { - return "REMOVEBROKER (-" + existingBroker + ")"; - } + @Override + public String toString() { + return "REMOVEBROKER (-" + existingBroker + ")"; + } } diff --git a/DreamSim/src/dream/common/packets/overlay/ReplaceBrokerMessage.java b/DreamSim/src/dream/common/packets/overlay/ReplaceBrokerMessage.java index 34bfbc8..22a3d09 100644 --- a/DreamSim/src/dream/common/packets/overlay/ReplaceBrokerMessage.java +++ b/DreamSim/src/dream/common/packets/overlay/ReplaceBrokerMessage.java @@ -11,44 +11,44 @@ */ public class ReplaceBrokerMessage extends Message { - private static final long serialVersionUID = 527500412680135179L; - - private final NetworkAddress existingBroker; - private final NetworkAddress newBroker; - - /** - * Create a message for replacing a broker. - * - * @param existingBroker - * Existing broker to replace - * @param newBroker - * New broker to replace the existing one - */ - public ReplaceBrokerMessage(NetworkAddress existingBroker, NetworkAddress newBroker) { - this.existingBroker = existingBroker; - this.newBroker = newBroker; - } - - /** - * Get the address of the existing broker. - * - * @return Address of the existing broker - */ - public NetworkAddress getExistingBroker() { - return existingBroker; - } - - /** - * Get the address of the new broker. - * - * @return Address of the new broker - */ - public NetworkAddress getNewBroker() { - return newBroker; - } - - @Override - public String toString() { - return "REPLACEBROKER (-" + existingBroker + " +" + newBroker + ")"; - } + private static final long serialVersionUID = 527500412680135179L; + + private final NetworkAddress existingBroker; + private final NetworkAddress newBroker; + + /** + * Create a message for replacing a broker. + * + * @param existingBroker + * Existing broker to replace + * @param newBroker + * New broker to replace the existing one + */ + public ReplaceBrokerMessage(NetworkAddress existingBroker, NetworkAddress newBroker) { + this.existingBroker = existingBroker; + this.newBroker = newBroker; + } + + /** + * Get the address of the existing broker. + * + * @return Address of the existing broker + */ + public NetworkAddress getExistingBroker() { + return existingBroker; + } + + /** + * Get the address of the new broker. + * + * @return Address of the new broker + */ + public NetworkAddress getNewBroker() { + return newBroker; + } + + @Override + public String toString() { + return "REPLACEBROKER (-" + existingBroker + " +" + newBroker + ")"; + } } diff --git a/DreamSim/src/dream/common/utils/AtomicDependencyDetector.java b/DreamSim/src/dream/common/utils/AtomicDependencyDetector.java index deeffbe..98b43fa 100644 --- a/DreamSim/src/dream/common/utils/AtomicDependencyDetector.java +++ b/DreamSim/src/dream/common/utils/AtomicDependencyDetector.java @@ -5,26 +5,26 @@ import java.util.Set; public enum AtomicDependencyDetector { - instance; + instance; - protected final DependencyGraph graph = DependencyGraph.instance; + protected final DependencyGraph graph = DependencyGraph.instance; - protected Map> dependencyClosure = new HashMap<>(); + protected Map> dependencyClosure = new HashMap<>(); - public final synchronized void consolidate() { - dependencyClosure = DependencyGraphUtils.computeDependencyClosure(); - } + public final synchronized void consolidate() { + dependencyClosure = DependencyGraphUtils.computeDependencyClosure(); + } - /** - * Returns the nodes that require to be locked during the propagation of an - * update originated at the given source. - * - * @param source - * the source. - * @return the nodes that need to be locked during the propagation. - */ - public Set getNodesToLockFor(String source) { - return dependencyClosure.get(source); - } + /** + * Returns the nodes that require to be locked during the propagation of an + * update originated at the given source. + * + * @param source + * the source. + * @return the nodes that need to be locked during the propagation. + */ + public Set getNodesToLockFor(String source) { + return dependencyClosure.get(source); + } } diff --git a/DreamSim/src/dream/common/utils/CompleteGlitchFreeDependencyDetector.java b/DreamSim/src/dream/common/utils/CompleteGlitchFreeDependencyDetector.java index 922203d..461b6d5 100644 --- a/DreamSim/src/dream/common/utils/CompleteGlitchFreeDependencyDetector.java +++ b/DreamSim/src/dream/common/utils/CompleteGlitchFreeDependencyDetector.java @@ -19,95 +19,95 @@ * depends on both s and s'. */ public enum CompleteGlitchFreeDependencyDetector { - instance; + instance; - private final DependencyGraph graph = DependencyGraph.instance; - private Map> dependencyClosure = new HashMap<>(); + private final DependencyGraph graph = DependencyGraph.instance; + private Map> dependencyClosure = new HashMap<>(); - private final Map> sharedNodes = new HashMap<>(); - private final Map lockRequestNode = new HashMap<>(); + private final Map> sharedNodes = new HashMap<>(); + private final Map lockRequestNode = new HashMap<>(); - public final void consolidate() { - dependencyClosure = DependencyGraphUtils.computeDependencyClosure(); - sharedNodes.clear(); - lockRequestNode.clear(); - computeSharedNodes(); - computeLockRequestNodes(); - } + public final void consolidate() { + dependencyClosure = DependencyGraphUtils.computeDependencyClosure(); + sharedNodes.clear(); + lockRequestNode.clear(); + computeSharedNodes(); + computeLockRequestNodes(); + } - /** - * Return the nodes that require to be locked during the propagation of an - * update originated at the given source. - * - * @param source - * the source. - * @return the nodes that need to be locked during the propagation. - */ - public final synchronized Set getNodesToLockFor(String source) { - return sharedNodes.containsKey(source) // - ? sharedNodes.get(source) // - : new HashSet<>(); - } + /** + * Return the nodes that require to be locked during the propagation of an + * update originated at the given source. + * + * @param source + * the source. + * @return the nodes that need to be locked during the propagation. + */ + public final synchronized Set getNodesToLockFor(String source) { + return sharedNodes.containsKey(source) // + ? sharedNodes.get(source) // + : new HashSet<>(); + } - /** - * Return the node that is responsible to send a lock request for a given - * source. - * - * @param source - * the source. - * @return the node that need to request a lock during the propagation. - */ - public final synchronized String getLockRequestNodeFor(String source) { - return lockRequestNode.get(source); - } + /** + * Return the node that is responsible to send a lock request for a given + * source. + * + * @param source + * the source. + * @return the node that need to request a lock during the propagation. + */ + public final synchronized String getLockRequestNodeFor(String source) { + return lockRequestNode.get(source); + } - private final void computeSharedNodes() { - graph.getSources()// - .forEach(source -> sharedNodes.put(source, computeSharedNodesFor(source))); - } + private final void computeSharedNodes() { + graph.getSources()// + .forEach(source -> sharedNodes.put(source, computeSharedNodesFor(source))); + } - private final void computeLockRequestNodes() { - graph.getSources()// - .forEach(source -> lockRequestNode.put(source, computeLockRequestNodeFor(source))); - } + private final void computeLockRequestNodes() { + graph.getSources()// + .forEach(source -> lockRequestNode.put(source, computeLockRequestNodeFor(source))); + } - private final Set computeSharedNodesFor(String source) { - final Set result = new HashSet<>(); - final Set depNodes = dependencyClosure.get(source); - if (depNodes.size() < 2) { - return result; - } - dependencyClosure.entrySet().stream() // - .filter(entry -> !entry.getKey().equals(source)) // - .forEach(entry -> { - final Set intersect = intersect(depNodes, entry.getValue()); - if (intersect.size() >= 2) { - result.addAll(intersect); - } - }); - return result; - } + private final Set computeSharedNodesFor(String source) { + final Set result = new HashSet<>(); + final Set depNodes = dependencyClosure.get(source); + if (depNodes.size() < 2) { + return result; + } + dependencyClosure.entrySet().stream() // + .filter(entry -> !entry.getKey().equals(source)) // + .forEach(entry -> { + final Set intersect = intersect(depNodes, entry.getValue()); + if (intersect.size() >= 2) { + result.addAll(intersect); + } + }); + return result; + } - private final String computeLockRequestNodeFor(String source) { - // Select the nodes on which all the nodes to lock depend - final Set nodesToLock = sharedNodes.get(source); - final List candidateNodes = dependencyClosure.get(source).stream()// - .filter(n -> dependencyClosure.get(n).containsAll(nodesToLock))// - .collect(Collectors.toList()); + private final String computeLockRequestNodeFor(String source) { + // Select the nodes on which all the nodes to lock depend + final Set nodesToLock = sharedNodes.get(source); + final List candidateNodes = dependencyClosure.get(source).stream()// + .filter(n -> dependencyClosure.get(n).containsAll(nodesToLock))// + .collect(Collectors.toList()); - Collections.sort(candidateNodes, (n1, n2) -> { - assert dependencyClosure.get(n1).contains(n2) || dependencyClosure.get(n2).contains(n1); - return dependencyClosure.get(n1).contains(n2) ? -1 : 1; - }); + Collections.sort(candidateNodes, (n1, n2) -> { + assert dependencyClosure.get(n1).contains(n2) || dependencyClosure.get(n2).contains(n1); + return dependencyClosure.get(n1).contains(n2) ? -1 : 1; + }); - assert !candidateNodes.isEmpty(); - return candidateNodes.get(0); - } + assert !candidateNodes.isEmpty(); + return candidateNodes.get(0); + } - private final Set intersect(Set set1, Set set2) { - return set1.stream().// - filter(elem -> set2.contains(elem)).// - collect(Collectors.toSet()); - } + private final Set intersect(Set set1, Set set2) { + return set1.stream().// + filter(elem -> set2.contains(elem)).// + collect(Collectors.toSet()); + } } diff --git a/DreamSim/src/dream/common/utils/DependencyDetector.java b/DreamSim/src/dream/common/utils/DependencyDetector.java index ddf25af..c95614e 100644 --- a/DreamSim/src/dream/common/utils/DependencyDetector.java +++ b/DreamSim/src/dream/common/utils/DependencyDetector.java @@ -9,10 +9,10 @@ */ public interface DependencyDetector { - /** - * Compute the accessory data structures required to speed-up the query - * process. It needs to be invoked when the dependency graph changes. - */ - public void consolidate(); + /** + * Compute the accessory data structures required to speed-up the query + * process. It needs to be invoked when the dependency graph changes. + */ + public void consolidate(); } diff --git a/DreamSim/src/dream/common/utils/DependencyGraph.java b/DreamSim/src/dream/common/utils/DependencyGraph.java index e0ca219..d9a4417 100644 --- a/DreamSim/src/dream/common/utils/DependencyGraph.java +++ b/DreamSim/src/dream/common/utils/DependencyGraph.java @@ -7,33 +7,33 @@ import java.util.Set; public enum DependencyGraph { - instance; - - // Node -> set of nodes it directly depends on - private final Map> graph = new HashMap<>(); - // Sources - private final Set sources = new HashSet<>(); - - public synchronized final void clear() { - graph.clear(); - sources.clear(); - } - - public synchronized final void addVar(String name) { - sources.add(name); - } - - public synchronized final void addSignal(String name, Collection deps) { - assert!deps.isEmpty(); - graph.put(name, deps); - } - - public synchronized final Map> getGraph() { - return graph; - } - - public synchronized final Set getSources() { - return sources; - } + instance; + + // Node -> set of nodes it directly depends on + private final Map> graph = new HashMap<>(); + // Sources + private final Set sources = new HashSet<>(); + + public synchronized final void clear() { + graph.clear(); + sources.clear(); + } + + public synchronized final void addVar(String name) { + sources.add(name); + } + + public synchronized final void addSignal(String name, Collection deps) { + assert !deps.isEmpty(); + graph.put(name, deps); + } + + public synchronized final Map> getGraph() { + return graph; + } + + public synchronized final Set getSources() { + return sources; + } } diff --git a/DreamSim/src/dream/common/utils/DependencyGraphUtils.java b/DreamSim/src/dream/common/utils/DependencyGraphUtils.java index 9a49b72..4bb7ec1 100644 --- a/DreamSim/src/dream/common/utils/DependencyGraphUtils.java +++ b/DreamSim/src/dream/common/utils/DependencyGraphUtils.java @@ -9,112 +9,114 @@ class DependencyGraphUtils { - /** - * Return, for each node, the set of sources it directly or indirectly depends - * on. - * - * @return the set of sources each node directly or indirectly depends on. - */ - static final Map> computeRelevantSources() { - final Map> result = new HashMap<>(); - final DependencyGraph depGraph = DependencyGraph.instance; - depGraph.getSources().forEach(s -> { - final HashSet sources = new HashSet<>(); - sources.add(s); - result.put(s, sources); - }); - depGraph.getGraph().keySet().// - forEach(node -> result.put(node, computeRelevantSourcesFor(node, depGraph))); - return result; - } + /** + * Return, for each node, the set of sources it directly or indirectly depends + * on. + * + * @return the set of sources each node directly or indirectly depends on. + */ + static final Map> computeRelevantSources() { + final Map> result = new HashMap<>(); + final DependencyGraph depGraph = DependencyGraph.instance; + depGraph.getSources().forEach(s -> { + final HashSet sources = new HashSet<>(); + sources.add(s); + result.put(s, sources); + }); + depGraph.getGraph().keySet().// + forEach(node -> result.put(node, computeRelevantSourcesFor(node, depGraph))); + return result; + } - /** - * Return the set of final nodes in the propagation graph, that is to say, all - * the nodes that do not have any other nodes that depends on them. - * - * @return the set of final nodes in the propagation graph. - */ - static final Set computeFinalNodes() { - final Map> graph = DependencyGraph.instance.getGraph(); - final Set sources = DependencyGraph.instance.getSources(); + /** + * Return the set of final nodes in the propagation graph, that is to say, all + * the nodes that do not have any other nodes that depends on them. + * + * @return the set of final nodes in the propagation graph. + */ + static final Set computeFinalNodes() { + final Map> graph = DependencyGraph.instance.getGraph(); + final Set sources = DependencyGraph.instance.getSources(); - final Set allNodes = new HashSet<>(graph.keySet()); - allNodes.addAll(sources); + final Set allNodes = new HashSet<>(graph.keySet()); + allNodes.addAll(sources); - return allNodes.stream()// - .filter(node -> graph.values().stream() // - .allMatch(depNodes -> !depNodes.contains(node))) // - .collect(Collectors.toSet()); - } + return allNodes.stream()// + .filter(node -> graph.values().stream() // + .allMatch(depNodes -> !depNodes.contains(node))) // + .collect(Collectors.toSet()); + } - /** - * Return, for each node, the set of nodes that directly or indirectly depend - * on it. - * - * @return for each node, the set of nodes that directly or indirectly depend - * on it. - */ - static final Map> computeDependencyClosure() { - final Map> result = new HashMap<>(); - final DependencyGraph depGraph = DependencyGraph.instance; - depGraph.getSources().forEach(n -> result.put(n, computeDependencyClosureFor(n, depGraph))); - depGraph.getGraph().keySet().forEach(n -> result.put(n, computeDependencyClosureFor(n, depGraph))); - return result; - } + /** + * Return, for each node, the set of nodes that directly or indirectly depend + * on it. + * + * @return for each node, the set of nodes that directly or indirectly depend + * on it. + */ + static final Map> computeDependencyClosure() { + final Map> result = new HashMap<>(); + final DependencyGraph depGraph = DependencyGraph.instance; + depGraph.getSources().forEach(n -> result.put(n, computeDependencyClosureFor(n, depGraph))); + depGraph.getGraph().keySet().forEach(n -> result.put(n, computeDependencyClosureFor(n, depGraph))); + return result; + } - private static final Set computeDependencyClosureFor(String node, DependencyGraph depGraph) { - final Set result = new HashSet<>(); - final Set newNodes = new HashSet<>(); - result.add(node); - newNodes.add(node); - computeDependencyClosureFor(newNodes, result, depGraph); - return result; - } + private static final Set computeDependencyClosureFor(String node, DependencyGraph depGraph) { + final Set result = new HashSet<>(); + final Set newNodes = new HashSet<>(); + result.add(node); + newNodes.add(node); + computeDependencyClosureFor(newNodes, result, depGraph); + return result; + } - private static final void computeDependencyClosureFor(Set newNodes, Set accumulator, DependencyGraph depGraph) { - final Set newNodesToEvaluate = new HashSet<>(); - newNodes.forEach(n -> { - final Set depNodes = dependentNodesFor(n, depGraph); - depNodes.removeAll(accumulator); - accumulator.addAll(depNodes); - newNodesToEvaluate.addAll(depNodes); - }); - if (!newNodesToEvaluate.isEmpty()) { - computeDependencyClosureFor(newNodesToEvaluate, accumulator, depGraph); - } - } + private static final void computeDependencyClosureFor(Set newNodes, Set accumulator, + DependencyGraph depGraph) { + final Set newNodesToEvaluate = new HashSet<>(); + newNodes.forEach(n -> { + final Set depNodes = dependentNodesFor(n, depGraph); + depNodes.removeAll(accumulator); + accumulator.addAll(depNodes); + newNodesToEvaluate.addAll(depNodes); + }); + if (!newNodesToEvaluate.isEmpty()) { + computeDependencyClosureFor(newNodesToEvaluate, accumulator, depGraph); + } + } - /** - * Return the set of nodes that directly depend on node. - */ - private static Set dependentNodesFor(String node, DependencyGraph depGraph) { - return depGraph.getGraph().entrySet().stream().// - filter(e -> e.getValue().contains(node)).// - map(e -> e.getKey()).// - collect(Collectors.toSet()); - } + /** + * Return the set of nodes that directly depend on node. + */ + private static Set dependentNodesFor(String node, DependencyGraph depGraph) { + return depGraph.getGraph().entrySet().stream().// + filter(e -> e.getValue().contains(node)).// + map(e -> e.getKey()).// + collect(Collectors.toSet()); + } - private static final Set computeRelevantSourcesFor(String node, DependencyGraph depGraph) { - final Set newNodes = new HashSet<>(); - final Set accumulator = new HashSet<>(); - newNodes.add(node); - computeRelevantSourcesFor(newNodes, accumulator, depGraph); - return accumulator; - } + private static final Set computeRelevantSourcesFor(String node, DependencyGraph depGraph) { + final Set newNodes = new HashSet<>(); + final Set accumulator = new HashSet<>(); + newNodes.add(node); + computeRelevantSourcesFor(newNodes, accumulator, depGraph); + return accumulator; + } - private static final void computeRelevantSourcesFor(Set newNodes, Set accumulator, DependencyGraph depGraph) { - newNodes.stream().// - filter(depGraph.getSources()::contains).// - collect(() -> accumulator, Set::add, Set::addAll); + private static final void computeRelevantSourcesFor(Set newNodes, Set accumulator, + DependencyGraph depGraph) { + newNodes.stream().// + filter(depGraph.getSources()::contains).// + collect(() -> accumulator, Set::add, Set::addAll); - final Set newNodesToEvaluate = newNodes.stream().// - filter(expr -> !depGraph.getSources().contains(expr)).// - map(depGraph.getGraph()::get).// - collect(HashSet::new, HashSet::addAll, HashSet::addAll); + final Set newNodesToEvaluate = newNodes.stream().// + filter(expr -> !depGraph.getSources().contains(expr)).// + map(depGraph.getGraph()::get).// + collect(HashSet::new, HashSet::addAll, HashSet::addAll); - if (!newNodesToEvaluate.isEmpty()) { - computeRelevantSourcesFor(newNodesToEvaluate, accumulator, depGraph); - } - } + if (!newNodesToEvaluate.isEmpty()) { + computeRelevantSourcesFor(newNodesToEvaluate, accumulator, depGraph); + } + } } diff --git a/DreamSim/src/dream/common/utils/FinalNodesDetector.java b/DreamSim/src/dream/common/utils/FinalNodesDetector.java index 9bd9f87..766a931 100755 --- a/DreamSim/src/dream/common/utils/FinalNodesDetector.java +++ b/DreamSim/src/dream/common/utils/FinalNodesDetector.java @@ -11,42 +11,42 @@ * given source and do not have any other node that deoends on them. */ public enum FinalNodesDetector { - instance; - - DependencyGraph depGraph = DependencyGraph.instance; - - // Source -> set of final nodes - private final Map> finalNodes = new HashMap<>(); - - public final synchronized void consolidate() { - finalNodes.clear(); - computeFinalNodes(); - } - - /** - * Return the set of final nodes for the given source. - * - * @param source - * the source. - * @return the set of final nodes for source. - */ - public final synchronized Set getFinalNodesFor(String source) { - return finalNodes.get(source); - } - - private final void computeFinalNodes() { - final Map> closure = DependencyGraphUtils.computeDependencyClosure(); - final Set finalNodesSet = DependencyGraphUtils.computeFinalNodes(); - - depGraph.getSources().forEach(s -> { - finalNodes.put(s, intersect(finalNodesSet, closure.get(s))); - }); - } - - private final Set intersect(Set set1, Set set2) { - final Set result = new HashSet<>(set1); - result.retainAll(set2); - return result; - } + instance; + + DependencyGraph depGraph = DependencyGraph.instance; + + // Source -> set of final nodes + private final Map> finalNodes = new HashMap<>(); + + public final synchronized void consolidate() { + finalNodes.clear(); + computeFinalNodes(); + } + + /** + * Return the set of final nodes for the given source. + * + * @param source + * the source. + * @return the set of final nodes for source. + */ + public final synchronized Set getFinalNodesFor(String source) { + return finalNodes.get(source); + } + + private final void computeFinalNodes() { + final Map> closure = DependencyGraphUtils.computeDependencyClosure(); + final Set finalNodesSet = DependencyGraphUtils.computeFinalNodes(); + + depGraph.getSources().forEach(s -> { + finalNodes.put(s, intersect(finalNodesSet, closure.get(s))); + }); + } + + private final Set intersect(Set set1, Set set2) { + final Set result = new HashSet<>(set1); + result.retainAll(set2); + return result; + } } diff --git a/DreamSim/src/dream/common/utils/IntraSourceDependencyDetector.java b/DreamSim/src/dream/common/utils/IntraSourceDependencyDetector.java index 93abba3..6987493 100755 --- a/DreamSim/src/dream/common/utils/IntraSourceDependencyDetector.java +++ b/DreamSim/src/dream/common/utils/IntraSourceDependencyDetector.java @@ -9,73 +9,73 @@ import dream.common.packets.content.Event; public enum IntraSourceDependencyDetector implements DependencyDetector { - instance; + instance; - private final DependencyGraph depGraph = DependencyGraph.instance; - private Map> relevantSources = new HashMap<>(); + private final DependencyGraph depGraph = DependencyGraph.instance; + private Map> relevantSources = new HashMap<>(); - // Stores the dependencies to compute expressions - // Expression Expr -> Initial expression that caused the recomputation -> - // Wait recommendations - private final Map>> recommendations = new HashMap<>(); + // Stores the dependencies to compute expressions + // Expression Expr -> Initial expression that caused the recomputation -> + // Wait recommendations + private final Map>> recommendations = new HashMap<>(); - public synchronized final Set getWaitRecommendations(Event event, String initialVar) { - final Map> innerMap = recommendations.get(event.getSignature()); - if (innerMap == null) { - return new HashSet<>(); - } - return innerMap.containsKey(initialVar) ? innerMap.get(initialVar) : new HashSet<>(); - } + public synchronized final Set getWaitRecommendations(Event event, String initialVar) { + final Map> innerMap = recommendations.get(event.getSignature()); + if (innerMap == null) { + return new HashSet<>(); + } + return innerMap.containsKey(initialVar) ? innerMap.get(initialVar) : new HashSet<>(); + } - @Override - public synchronized final void consolidate() { - recommendations.clear(); - computeRecommendations(); - } + @Override + public synchronized final void consolidate() { + recommendations.clear(); + computeRecommendations(); + } - private final void computeRecommendations() { - recommendations.clear(); - relevantSources = DependencyGraphUtils.computeRelevantSources(); - depGraph.getGraph().keySet().forEach(expr -> { - relevantSources.get(expr).forEach(initialExpr -> storeRecommendationsFor(expr, initialExpr)); - }); - } + private final void computeRecommendations() { + recommendations.clear(); + relevantSources = DependencyGraphUtils.computeRelevantSources(); + depGraph.getGraph().keySet().forEach(expr -> { + relevantSources.get(expr).forEach(initialExpr -> storeRecommendationsFor(expr, initialExpr)); + }); + } - private final void storeRecommendationsFor(String expr, String initialExpr) { - final Set dependentSiblings = computeDependentSiblingsFor(expr, initialExpr); + private final void storeRecommendationsFor(String expr, String initialExpr) { + final Set dependentSiblings = computeDependentSiblingsFor(expr, initialExpr); - if (dependentSiblings.size() > 1) { - dependentSiblings.forEach(sibling -> { - Map> recommendationsMap = recommendations.get(sibling); - if (recommendationsMap == null) { - recommendationsMap = new HashMap<>(); - recommendations.put(sibling, recommendationsMap); - } - Set recommendationsSet = recommendationsMap.get(initialExpr); - if (recommendationsSet == null) { - recommendationsSet = new HashSet<>(); - recommendationsMap.put(initialExpr, recommendationsSet); - } - final WaitRecommendations wr = new WaitRecommendations(expr); - recommendationsSet.add(wr); - dependentSiblings.stream().// - filter(e -> !e.equals(sibling)).// - forEach(wr::addRecommendation); - }); - } - } + if (dependentSiblings.size() > 1) { + dependentSiblings.forEach(sibling -> { + Map> recommendationsMap = recommendations.get(sibling); + if (recommendationsMap == null) { + recommendationsMap = new HashMap<>(); + recommendations.put(sibling, recommendationsMap); + } + Set recommendationsSet = recommendationsMap.get(initialExpr); + if (recommendationsSet == null) { + recommendationsSet = new HashSet<>(); + recommendationsMap.put(initialExpr, recommendationsSet); + } + final WaitRecommendations wr = new WaitRecommendations(expr); + recommendationsSet.add(wr); + dependentSiblings.stream().// + filter(e -> !e.equals(sibling)).// + forEach(wr::addRecommendation); + }); + } + } - /** - * Compute the set of all expressions with the following properties: - * - * 1) expr directly depends on them - * - * 2) they directly or indirectly depend on the initialExpression - */ - private final Set computeDependentSiblingsFor(String expr, String initialExpression) { - return depGraph.getGraph().get(expr).stream().// - filter(dep -> relevantSources.get(dep).contains(initialExpression)).// - collect(Collectors.toSet()); - } + /** + * Compute the set of all expressions with the following properties: + * + * 1) expr directly depends on them + * + * 2) they directly or indirectly depend on the initialExpression + */ + private final Set computeDependentSiblingsFor(String expr, String initialExpression) { + return depGraph.getGraph().get(expr).stream().// + filter(dep -> relevantSources.get(dep).contains(initialExpression)).// + collect(Collectors.toSet()); + } } diff --git a/DreamSim/src/dream/common/utils/LocalityDetector.java b/DreamSim/src/dream/common/utils/LocalityDetector.java index 669e6f5..9147e15 100644 --- a/DreamSim/src/dream/common/utils/LocalityDetector.java +++ b/DreamSim/src/dream/common/utils/LocalityDetector.java @@ -11,74 +11,74 @@ * not, that is to say, if it requires the acquisition of locks or not. */ public enum LocalityDetector { - instance; - - DependencyGraph depGraph = DependencyGraph.instance; - - // Sources that need the acquisition of a lock for propagation - private final Set sourcesToLock = new HashSet<>(); - - // Nodes that need to be locked in read mode in the case of atomic consistency - private final Set nodesToLockOnRead = new HashSet<>(); - - public final synchronized void consolidate() { - sourcesToLock.clear(); - nodesToLockOnRead.clear(); - computeSourcesToLock(); - computeNodesToLockOnRead(); - } - - public final synchronized boolean sourceRequiresLock(String source) { - return sourcesToLock.contains(source); - } - - public final synchronized boolean nodeRequiresReadLock(String node) { - return nodesToLockOnRead.contains(node); - } - - /** - * A source is local if all its dependent nodes are defined in the same host - * and do not depend on remote sources. In this case, the order of - * propagations can be decided with a local synchronization mechanism between - * sources. - */ - private final void computeSourcesToLock() { - final Map> relevantSources = DependencyGraphUtils.computeRelevantSources(); - final Map> dependencyClosure = DependencyGraphUtils.computeDependencyClosure(); - - final Predicate dependsOnRemoteSource = // - node -> relevantSources.get(node).stream() // - .anyMatch(source -> !source.split("@")[1].equals(node.split("@")[1])); - - final Predicate someNodeDependsOnRemoteSource = // - source -> dependencyClosure.get(source).stream().anyMatch(dependsOnRemoteSource); - - depGraph.getSources().stream() // - .filter(someNodeDependsOnRemoteSource) // - .collect(Collectors.toCollection(() -> sourcesToLock)); - } - - /** - * A node needs to be locked on read if it depends (directly or indirectly) on - * a node defined in a different host or if some of its (directly or - * indirectly) dependent nodes are defined in a different host. - */ - private final void computeNodesToLockOnRead() { - final Map> dependencyClosure = DependencyGraphUtils.computeDependencyClosure(); - - final Predicate aDependentNodeIsRemote = // - node -> dependencyClosure.get(node).stream() // - .anyMatch(depNode -> !depNode.split("@")[1].equals(node.split("@")[1])); - - final Predicate dependsOnRemoteNode = node -> // - dependencyClosure.entrySet().stream() // - .filter(e -> e.getKey().equals(node)) // - .filter(e -> e.getValue().contains(node)) // - .anyMatch(e -> !e.getKey().split("@")[1].equals(node.split("@")[1])); - - depGraph.getGraph().keySet().stream() // - .filter(aDependentNodeIsRemote.or(dependsOnRemoteNode)) // - .collect(Collectors.toCollection(() -> nodesToLockOnRead)); - } + instance; + + DependencyGraph depGraph = DependencyGraph.instance; + + // Sources that need the acquisition of a lock for propagation + private final Set sourcesToLock = new HashSet<>(); + + // Nodes that need to be locked in read mode in the case of atomic consistency + private final Set nodesToLockOnRead = new HashSet<>(); + + public final synchronized void consolidate() { + sourcesToLock.clear(); + nodesToLockOnRead.clear(); + computeSourcesToLock(); + computeNodesToLockOnRead(); + } + + public final synchronized boolean sourceRequiresLock(String source) { + return sourcesToLock.contains(source); + } + + public final synchronized boolean nodeRequiresReadLock(String node) { + return nodesToLockOnRead.contains(node); + } + + /** + * A source is local if all its dependent nodes are defined in the same host + * and do not depend on remote sources. In this case, the order of + * propagations can be decided with a local synchronization mechanism between + * sources. + */ + private final void computeSourcesToLock() { + final Map> relevantSources = DependencyGraphUtils.computeRelevantSources(); + final Map> dependencyClosure = DependencyGraphUtils.computeDependencyClosure(); + + final Predicate dependsOnRemoteSource = // + node -> relevantSources.get(node).stream() // + .anyMatch(source -> !source.split("@")[1].equals(node.split("@")[1])); + + final Predicate someNodeDependsOnRemoteSource = // + source -> dependencyClosure.get(source).stream().anyMatch(dependsOnRemoteSource); + + depGraph.getSources().stream() // + .filter(someNodeDependsOnRemoteSource) // + .collect(Collectors.toCollection(() -> sourcesToLock)); + } + + /** + * A node needs to be locked on read if it depends (directly or indirectly) on + * a node defined in a different host or if some of its (directly or + * indirectly) dependent nodes are defined in a different host. + */ + private final void computeNodesToLockOnRead() { + final Map> dependencyClosure = DependencyGraphUtils.computeDependencyClosure(); + + final Predicate aDependentNodeIsRemote = // + node -> dependencyClosure.get(node).stream() // + .anyMatch(depNode -> !depNode.split("@")[1].equals(node.split("@")[1])); + + final Predicate dependsOnRemoteNode = node -> // + dependencyClosure.entrySet().stream() // + .filter(e -> e.getKey().equals(node)) // + .filter(e -> e.getValue().contains(node)) // + .anyMatch(e -> !e.getKey().split("@")[1].equals(node.split("@")[1])); + + depGraph.getGraph().keySet().stream() // + .filter(aDependentNodeIsRemote.or(dependsOnRemoteNode)) // + .collect(Collectors.toCollection(() -> nodesToLockOnRead)); + } } diff --git a/DreamSim/src/dream/common/utils/WaitRecommendations.java b/DreamSim/src/dream/common/utils/WaitRecommendations.java index 8290ae3..cfdb4c1 100755 --- a/DreamSim/src/dream/common/utils/WaitRecommendations.java +++ b/DreamSim/src/dream/common/utils/WaitRecommendations.java @@ -5,44 +5,45 @@ import java.util.Set; /** - * A WaitRecommendations object is stored inside an event E. It is used by the server to prevent glitches while - * delivering E to the clients. It tells a client C that it must wait for other events before processing E. + * A WaitRecommendations object is stored inside an event E. It is used by the + * server to prevent glitches while delivering E to the clients. It tells a + * client C that it must wait for other events before processing E. */ public class WaitRecommendations implements Serializable { - private static final long serialVersionUID = 5700944080087353096L; + private static final long serialVersionUID = 5700944080087353096L; - private final String expression; - private final Set expressionsToWaitFor = new HashSet(); + private final String expression; + private final Set expressionsToWaitFor = new HashSet(); - public WaitRecommendations(String expression) { - this.expression = expression; - } + public WaitRecommendations(String expression) { + this.expression = expression; + } - public final void addRecommendation(String expressionToWaitFor) { - expressionsToWaitFor.add(expressionToWaitFor); - } + public final void addRecommendation(String expressionToWaitFor) { + expressionsToWaitFor.add(expressionToWaitFor); + } - public final String getExpression() { - return expression; - } + public final String getExpression() { + return expression; + } - public final Set getRecommendations() { - return expressionsToWaitFor; - } + public final Set getRecommendations() { + return expressionsToWaitFor; + } - public final WaitRecommendations dup() { - WaitRecommendations result = new WaitRecommendations(expression); - result.expressionsToWaitFor.addAll(expressionsToWaitFor); - return result; - } + public final WaitRecommendations dup() { + WaitRecommendations result = new WaitRecommendations(expression); + result.expressionsToWaitFor.addAll(expressionsToWaitFor); + return result; + } - public final void removeExpressionToWaitFor(String expressionToWaitFor) { - expressionsToWaitFor.remove(expressionToWaitFor); - } + public final void removeExpressionToWaitFor(String expressionToWaitFor) { + expressionsToWaitFor.remove(expressionToWaitFor); + } - @Override - public String toString() { - return "WaitRecommendations [expression=" + expression + ", expressionsToWaitFor=" + expressionsToWaitFor + "]"; - } + @Override + public String toString() { + return "WaitRecommendations [expression=" + expression + ", expressionsToWaitFor=" + expressionsToWaitFor + "]"; + } } diff --git a/DreamSim/src/dream/generator/GraphGenerator.java b/DreamSim/src/dream/generator/GraphGenerator.java index 362b156..ead9179 100755 --- a/DreamSim/src/dream/generator/GraphGenerator.java +++ b/DreamSim/src/dream/generator/GraphGenerator.java @@ -20,181 +20,183 @@ import dream.experiments.DreamConfiguration; public class GraphGenerator { - private static GraphGenerator instance; - - private final DependencyGraph depGraph = DependencyGraph.instance; - // Existing nodes, organized by source - private final Map> nodesPerSource = new HashMap<>(); - private final List allNodes = new ArrayList<>(); - - private final DreamConfiguration config = DreamConfiguration.get(); - private final Random random = RandomGenerator.get(); - - private final Map listeners = new HashMap<>(); - - public static final GraphGenerator get() { - if (instance == null) { - instance = new GraphGenerator(); - } - return instance; - } - - private GraphGenerator() { - // Nothing to do - } - - public void clean() { - depGraph.clear(); - nodesPerSource.clear(); - allNodes.clear(); - listeners.clear(); - } - - public final void generateGraphs(int id) { - // Only the first client triggers a graph generation - if (id == 0) { - IntStream.range(0, config.graphNumSources).forEach(i -> generateVar()); - nodesPerSource.keySet().forEach(source -> generateGraphFor(source)); - addInterSourceEdges(); - } - } - - public final void addGraphGeneratorListener(GraphGeneratorListener listener, int id) { - final String hostName = Consts.hostPrefix + id; - listeners.put(hostName, listener); - } - - public final void notifyListeners(int id) { - // Only the first client triggers listeners notification - if (id == 0) { - // Consolidates the data structures used during the processing of the - // events - IntraSourceDependencyDetector.instance.consolidate(); - CompleteGlitchFreeDependencyDetector.instance.consolidate(); - AtomicDependencyDetector.instance.consolidate(); - FinalNodesDetector.instance.consolidate(); - LocalityDetector.instance.consolidate(); - - depGraph.getSources().forEach(s -> { - final String hostId = s.split("@")[1]; - listeners.get(hostId).notifyVar(s); - }); - - depGraph.getGraph().entrySet().forEach(e -> { - final String name = e.getKey(); - final String hostId = name.split("@")[1]; - final Set deps = e.getValue().stream().collect(Collectors.toSet()); - listeners.get(hostId).notifySignal(name, deps); - }); - } - } - - private final void generateVar() { - final String host = selectRandomHost(); - final String name = Consts.objPrefix + allNodes.size(); - final String node = name + "@" + host; - final List sourceNodes = new ArrayList<>(); - sourceNodes.add(node); - nodesPerSource.put(node, sourceNodes); - allNodes.add(node); - depGraph.addVar(node); - } - - private final void generateGraphFor(String source) { - final int numLevels = config.graphDepth; - // Start from 1 because level 0 is the source - List previousLevel = new ArrayList<>(); - previousLevel.add(source); - for (int i = 1; i < numLevels; i++) { - previousLevel = generateLevel(source, previousLevel); - } - } - - private final void addInterSourceEdges() { - final List sourceList = new ArrayList<>(nodesPerSource.keySet()); - final int numSharedSources = (int) ((sourceList.size() - 1) * config.graphNodeShareProbability); - for (int i = 0; i < numSharedSources; i++) { - final String source = sourceList.get(i); - final String nextSource = sourceList.get(i + 1); - addInterSourceEdgesBetween(source, nextSource); - } - } - - private final void addInterSourceEdgesBetween(String source1, String source2) { - if (nodesPerSource.get(source1).size() < 3 || nodesPerSource.get(source2).size() < 3) { - throw new IllegalArgumentException("The number of nodes is too small to generate inter-source edges"); - } - final List nodes1 = selectRandomSignalsFromSource(source1, 2); - final List nodes2 = selectRandomSignalsFromSource(source2, 2); - - depGraph.getGraph().get(nodes1.get(0)).add(nodes2.get(0)); - depGraph.getGraph().get(nodes1.get(1)).add(nodes2.get(1)); - } - - private final List generateLevel(String source, List previousLevel) { - final int numNodes = config.graphMinNodesPerLevel + random.nextInt(config.graphMaxNodesPerLevel - config.graphMinNodesPerLevel + 1); - final List currentLevel = new ArrayList<>(); - for (int i = 0; i <= numNodes; i++) { - currentLevel.add(generateSignal(source, previousLevel)); - } - return currentLevel; - } - - private final String generateSignal(String source, List previousLevel) { - final String name = Consts.objPrefix + allNodes.size(); - final List depNodes = selectDepNodes(source, previousLevel); - final String host = random.nextDouble() < config.graphLocality// - ? depNodes.stream().findAny().get().split("@")[1] // - : selectRandomHost(); - - final String node = name + "@" + host; - nodesPerSource.get(source).add(node); - allNodes.add(node); - depGraph.addSignal(node, depNodes); - return node; - } - - private final List selectDepNodes(String source, List previousLevel) { - final List result = new ArrayList<>(); - final int numDeps = Math.min(1 + random.nextInt(config.graphMaxDependenciesPerNode), nodesPerSource.get(source).size()); - - // Always select a node from the previous level of the source graph - result.add(selectRandomNodeFromPreviousLevel(previousLevel)); - // Select the remaining nodes from the same source - result.addAll(selectRandomNodesFromSource(source, numDeps - 1)); - - return result; - } - - private final String selectRandomNodeFromPreviousLevel(List previousLevel) { - return previousLevel.get(random.nextInt(previousLevel.size())); - } - - private final List selectRandomNodesFromSource(String source, int numNodes) { - final List sourceNodes = nodesPerSource.get(source); - Collections.shuffle(sourceNodes); - return IntStream.range(0, numNodes) // - .mapToObj(i -> sourceNodes.get(i)) // - .collect(Collectors.toList()); - } - - private final List selectRandomSignalsFromSource(String source, int numSignals) { - final List sourceNodes = nodesPerSource.get(source); - Collections.shuffle(sourceNodes); - final List result = new ArrayList<>(); - int index = 0; - while (result.size() < numSignals) { - final String node = sourceNodes.get(index++); - if (!node.equals(source)) { - result.add(node); - } - } - return result; - } - - private final String selectRandomHost() { - final int id = random.nextInt(config.numberOfClients); - return Consts.hostPrefix + id; - } + private static GraphGenerator instance; + + private final DependencyGraph depGraph = DependencyGraph.instance; + // Existing nodes, organized by source + private final Map> nodesPerSource = new HashMap<>(); + private final List allNodes = new ArrayList<>(); + + private final DreamConfiguration config = DreamConfiguration.get(); + private final Random random = RandomGenerator.get(); + + private final Map listeners = new HashMap<>(); + + public static final GraphGenerator get() { + if (instance == null) { + instance = new GraphGenerator(); + } + return instance; + } + + private GraphGenerator() { + // Nothing to do + } + + public void clean() { + depGraph.clear(); + nodesPerSource.clear(); + allNodes.clear(); + listeners.clear(); + } + + public final void generateGraphs(int id) { + // Only the first client triggers a graph generation + if (id == 0) { + IntStream.range(0, config.graphNumSources).forEach(i -> generateVar()); + nodesPerSource.keySet().forEach(source -> generateGraphFor(source)); + addInterSourceEdges(); + } + } + + public final void addGraphGeneratorListener(GraphGeneratorListener listener, int id) { + final String hostName = Consts.hostPrefix + id; + listeners.put(hostName, listener); + } + + public final void notifyListeners(int id) { + // Only the first client triggers listeners notification + if (id == 0) { + // Consolidates the data structures used during the processing of the + // events + IntraSourceDependencyDetector.instance.consolidate(); + CompleteGlitchFreeDependencyDetector.instance.consolidate(); + AtomicDependencyDetector.instance.consolidate(); + FinalNodesDetector.instance.consolidate(); + LocalityDetector.instance.consolidate(); + + depGraph.getSources().forEach(s -> { + final String hostId = s.split("@")[1]; + listeners.get(hostId).notifyVar(s); + }); + + depGraph.getGraph().entrySet().forEach(e -> { + final String name = e.getKey(); + final String hostId = name.split("@")[1]; + final Set deps = e.getValue().stream().collect(Collectors.toSet()); + listeners.get(hostId).notifySignal(name, deps); + }); + } + } + + private final void generateVar() { + final String host = selectRandomHost(); + final String name = Consts.objPrefix + allNodes.size(); + final String node = name + "@" + host; + final List sourceNodes = new ArrayList<>(); + sourceNodes.add(node); + nodesPerSource.put(node, sourceNodes); + allNodes.add(node); + depGraph.addVar(node); + } + + private final void generateGraphFor(String source) { + final int numLevels = config.graphDepth; + // Start from 1 because level 0 is the source + List previousLevel = new ArrayList<>(); + previousLevel.add(source); + for (int i = 1; i < numLevels; i++) { + previousLevel = generateLevel(source, previousLevel); + } + } + + private final void addInterSourceEdges() { + final List sourceList = new ArrayList<>(nodesPerSource.keySet()); + final int numSharedSources = (int) ((sourceList.size() - 1) * config.graphNodeShareProbability); + for (int i = 0; i < numSharedSources; i++) { + final String source = sourceList.get(i); + final String nextSource = sourceList.get(i + 1); + addInterSourceEdgesBetween(source, nextSource); + } + } + + private final void addInterSourceEdgesBetween(String source1, String source2) { + if (nodesPerSource.get(source1).size() < 3 || nodesPerSource.get(source2).size() < 3) { + throw new IllegalArgumentException("The number of nodes is too small to generate inter-source edges"); + } + final List nodes1 = selectRandomSignalsFromSource(source1, 2); + final List nodes2 = selectRandomSignalsFromSource(source2, 2); + + depGraph.getGraph().get(nodes1.get(0)).add(nodes2.get(0)); + depGraph.getGraph().get(nodes1.get(1)).add(nodes2.get(1)); + } + + private final List generateLevel(String source, List previousLevel) { + final int numNodes = config.graphMinNodesPerLevel + + random.nextInt(config.graphMaxNodesPerLevel - config.graphMinNodesPerLevel + 1); + final List currentLevel = new ArrayList<>(); + for (int i = 0; i <= numNodes; i++) { + currentLevel.add(generateSignal(source, previousLevel)); + } + return currentLevel; + } + + private final String generateSignal(String source, List previousLevel) { + final String name = Consts.objPrefix + allNodes.size(); + final List depNodes = selectDepNodes(source, previousLevel); + final String host = random.nextDouble() < config.graphLocality// + ? depNodes.stream().findAny().get().split("@")[1] // + : selectRandomHost(); + + final String node = name + "@" + host; + nodesPerSource.get(source).add(node); + allNodes.add(node); + depGraph.addSignal(node, depNodes); + return node; + } + + private final List selectDepNodes(String source, List previousLevel) { + final List result = new ArrayList<>(); + final int numDeps = Math.min(1 + random.nextInt(config.graphMaxDependenciesPerNode), + nodesPerSource.get(source).size()); + + // Always select a node from the previous level of the source graph + result.add(selectRandomNodeFromPreviousLevel(previousLevel)); + // Select the remaining nodes from the same source + result.addAll(selectRandomNodesFromSource(source, numDeps - 1)); + + return result; + } + + private final String selectRandomNodeFromPreviousLevel(List previousLevel) { + return previousLevel.get(random.nextInt(previousLevel.size())); + } + + private final List selectRandomNodesFromSource(String source, int numNodes) { + final List sourceNodes = nodesPerSource.get(source); + Collections.shuffle(sourceNodes); + return IntStream.range(0, numNodes) // + .mapToObj(i -> sourceNodes.get(i)) // + .collect(Collectors.toList()); + } + + private final List selectRandomSignalsFromSource(String source, int numSignals) { + final List sourceNodes = nodesPerSource.get(source); + Collections.shuffle(sourceNodes); + final List result = new ArrayList<>(); + int index = 0; + while (result.size() < numSignals) { + final String node = sourceNodes.get(index++); + if (!node.equals(source)) { + result.add(node); + } + } + return result; + } + + private final String selectRandomHost() { + final int id = random.nextInt(config.numberOfClients); + return Consts.hostPrefix + id; + } } diff --git a/DreamSim/src/dream/generator/GraphGeneratorListener.java b/DreamSim/src/dream/generator/GraphGeneratorListener.java index b09ca5f..83cf7bf 100755 --- a/DreamSim/src/dream/generator/GraphGeneratorListener.java +++ b/DreamSim/src/dream/generator/GraphGeneratorListener.java @@ -9,15 +9,15 @@ */ public interface GraphGeneratorListener { - /** - * Notifies the presence of a var with the given name. - */ - public void notifyVar(String varName); + /** + * Notifies the presence of a var with the given name. + */ + public void notifyVar(String varName); - /** - * Notifies the presence of a signal with the given name and the given - * dependencies. - */ - public void notifySignal(String signalName, Set dependencies); + /** + * Notifies the presence of a signal with the given name and the given + * dependencies. + */ + public void notifySignal(String signalName, Set dependencies); } diff --git a/DreamSim/src/dream/generator/RandomGenerator.java b/DreamSim/src/dream/generator/RandomGenerator.java index 99c7512..ec2150a 100755 --- a/DreamSim/src/dream/generator/RandomGenerator.java +++ b/DreamSim/src/dream/generator/RandomGenerator.java @@ -5,21 +5,21 @@ import dream.experiments.DreamConfiguration; public class RandomGenerator { - private static Random random; + private static Random random; - public static Random get() { - if (random == null) { - random = new Random(DreamConfiguration.get().seed); - } - return random; - } + public static Random get() { + if (random == null) { + random = new Random(DreamConfiguration.get().seed); + } + return random; + } - public static void reset() { - if (random == null) { - random = new Random(DreamConfiguration.get().seed); - } else { - random.setSeed(DreamConfiguration.get().seed); - } - } + public static void reset() { + if (random == null) { + random = new Random(DreamConfiguration.get().seed); + } else { + random.setSeed(DreamConfiguration.get().seed); + } + } } diff --git a/DreamSim/src/dream/locking/DreamLockManager.java b/DreamSim/src/dream/locking/DreamLockManager.java index 7c619ec..e1fc803 100644 --- a/DreamSim/src/dream/locking/DreamLockManager.java +++ b/DreamSim/src/dream/locking/DreamLockManager.java @@ -14,162 +14,162 @@ import dream.common.packets.locking.LockType; class DreamLockManager implements LockManager { - // Pending requests - private final List pendingRequests = new ArrayList<>(); - - // All stored locks - private final Map activeLocks = new HashMap<>(); - - // Stored read and write locks - private final Map readLocks = new HashMap<>(); - private final WriteLockManager writeLocks = new WriteLockManager(); - - @Override - public final boolean processLockRequest(LockRequestPacket request) { - if (canBeGranted(request)) { - lock(request); - return true; - } else { - pendingRequests.add(request); - return false; - } - } - - @Override - public final Set processLockRelease(LockReleasePacket release) { - final UUID lockID = release.getLockID(); - assert activeLocks.containsKey(lockID); - final LockRequestPacket reqPkt = activeLocks.get(lockID); - - final Set result = new HashSet<>(); - if (release(reqPkt.getLockID(), reqPkt.getLockNodes(), reqPkt.getType())) { - final Iterator it = pendingRequests.iterator(); - while (it.hasNext()) { - final LockRequestPacket request = it.next(); - if (canBeGranted(request)) { - lock(request); - result.add(request); - it.remove(); - } - } - } - return result; - } - - private final boolean canBeGranted(LockRequestPacket request) { - final LockType type = request.getType(); - final Set lockNodes = request.getLockNodes(); - final boolean writeConflicts = lockNodes.stream()// - .anyMatch(n -> writeLocks.isLocked(n)); - if (writeConflicts) { - return false; - } - final boolean readConflicts = type == LockType.READ_WRITE && // - lockNodes.stream().anyMatch(n -> readLocks.containsKey(n)); - return !readConflicts; - } - - private final void lock(LockRequestPacket request) { - activeLocks.put(request.getLockID(), request); - final LockType type = request.getType(); - final Set lockNodes = request.getLockNodes(); - switch (type) { - case READ_ONLY: - lockNodes.forEach(n -> { - final Integer count = readLocks.get(n); - if (count == null) { - readLocks.put(n, 1); - } else { - readLocks.put(n, count + 1); - } - }); - break; - case READ_WRITE: - writeLocks.grant(request); - break; - default: - assert false : type; - break; - } - } - - /** - * Return true if the packet released at least one node - */ - private final boolean release(UUID lockId, Set lockNodes, LockType type) { - boolean result = false; - switch (type) { - case READ_ONLY: - for (final String lockNode : lockNodes) { - final int newCount = readLocks.get(lockNode) - 1; - if (newCount == 0) { - readLocks.remove(lockNode); - activeLocks.remove(lockId); - result = true; - } else { - readLocks.put(lockNode, newCount); - } - } - break; - case READ_WRITE: - if (writeLocks.release(lockId)) { - activeLocks.remove(lockId); - result = true; - } - break; - default: - assert false : type; - break; - } - return result; - } - - /** - * Store information about the write locks that have been granted. - */ - private class WriteLockManager { - private final Map grantedLocks = new HashMap<>(); - private final Map> lockedNodesMap = new HashMap<>(); - private final Set lockedNodes = new HashSet<>(); - - /** - * Store the lock coming from the given source and having the given lockId. - */ - void grant(LockRequestPacket request) { - final UUID lockId = request.getLockID(); - final Set nodesToLock = request.getLockNodes(); - assert !grantedLocks.containsKey(lockId); - assert !lockedNodesMap.containsKey(lockId); - - grantedLocks.put(lockId, request.getUnlockNodes().size()); - lockedNodesMap.put(lockId, new HashSet<>(nodesToLock)); - lockedNodes.addAll(nodesToLock); - } - - /** - * Return true if the given node is already locked. - */ - boolean isLocked(String node) { - return lockedNodes.contains(node); - } - - /** - * Return true if the lock has been entirely released. - */ - boolean release(UUID lockId) { - assert grantedLocks.containsKey(lockId); - Integer count = grantedLocks.get(lockId); - if (--count == 0) { - grantedLocks.remove(lockId); - lockedNodesMap.get(lockId).forEach(lockedNodes::remove); - lockedNodesMap.remove(lockId); - return true; - } else { - grantedLocks.put(lockId, count); - return false; - } - } - - } + // Pending requests + private final List pendingRequests = new ArrayList<>(); + + // All stored locks + private final Map activeLocks = new HashMap<>(); + + // Stored read and write locks + private final Map readLocks = new HashMap<>(); + private final WriteLockManager writeLocks = new WriteLockManager(); + + @Override + public final boolean processLockRequest(LockRequestPacket request) { + if (canBeGranted(request)) { + lock(request); + return true; + } else { + pendingRequests.add(request); + return false; + } + } + + @Override + public final Set processLockRelease(LockReleasePacket release) { + final UUID lockID = release.getLockID(); + assert activeLocks.containsKey(lockID); + final LockRequestPacket reqPkt = activeLocks.get(lockID); + + final Set result = new HashSet<>(); + if (release(reqPkt.getLockID(), reqPkt.getLockNodes(), reqPkt.getType())) { + final Iterator it = pendingRequests.iterator(); + while (it.hasNext()) { + final LockRequestPacket request = it.next(); + if (canBeGranted(request)) { + lock(request); + result.add(request); + it.remove(); + } + } + } + return result; + } + + private final boolean canBeGranted(LockRequestPacket request) { + final LockType type = request.getType(); + final Set lockNodes = request.getLockNodes(); + final boolean writeConflicts = lockNodes.stream()// + .anyMatch(n -> writeLocks.isLocked(n)); + if (writeConflicts) { + return false; + } + final boolean readConflicts = type == LockType.READ_WRITE && // + lockNodes.stream().anyMatch(n -> readLocks.containsKey(n)); + return !readConflicts; + } + + private final void lock(LockRequestPacket request) { + activeLocks.put(request.getLockID(), request); + final LockType type = request.getType(); + final Set lockNodes = request.getLockNodes(); + switch (type) { + case READ_ONLY: + lockNodes.forEach(n -> { + final Integer count = readLocks.get(n); + if (count == null) { + readLocks.put(n, 1); + } else { + readLocks.put(n, count + 1); + } + }); + break; + case READ_WRITE: + writeLocks.grant(request); + break; + default: + assert false : type; + break; + } + } + + /** + * Return true if the packet released at least one node + */ + private final boolean release(UUID lockId, Set lockNodes, LockType type) { + boolean result = false; + switch (type) { + case READ_ONLY: + for (final String lockNode : lockNodes) { + final int newCount = readLocks.get(lockNode) - 1; + if (newCount == 0) { + readLocks.remove(lockNode); + activeLocks.remove(lockId); + result = true; + } else { + readLocks.put(lockNode, newCount); + } + } + break; + case READ_WRITE: + if (writeLocks.release(lockId)) { + activeLocks.remove(lockId); + result = true; + } + break; + default: + assert false : type; + break; + } + return result; + } + + /** + * Store information about the write locks that have been granted. + */ + private class WriteLockManager { + private final Map grantedLocks = new HashMap<>(); + private final Map> lockedNodesMap = new HashMap<>(); + private final Set lockedNodes = new HashSet<>(); + + /** + * Store the lock coming from the given source and having the given lockId. + */ + void grant(LockRequestPacket request) { + final UUID lockId = request.getLockID(); + final Set nodesToLock = request.getLockNodes(); + assert !grantedLocks.containsKey(lockId); + assert !lockedNodesMap.containsKey(lockId); + + grantedLocks.put(lockId, request.getUnlockNodes().size()); + lockedNodesMap.put(lockId, new HashSet<>(nodesToLock)); + lockedNodes.addAll(nodesToLock); + } + + /** + * Return true if the given node is already locked. + */ + boolean isLocked(String node) { + return lockedNodes.contains(node); + } + + /** + * Return true if the lock has been entirely released. + */ + boolean release(UUID lockId) { + assert grantedLocks.containsKey(lockId); + Integer count = grantedLocks.get(lockId); + if (--count == 0) { + grantedLocks.remove(lockId); + lockedNodesMap.get(lockId).forEach(lockedNodes::remove); + lockedNodesMap.remove(lockId); + return true; + } else { + grantedLocks.put(lockId, count); + return false; + } + } + + } } diff --git a/DreamSim/src/dream/locking/LockManager.java b/DreamSim/src/dream/locking/LockManager.java index e125afc..b9e3ce2 100644 --- a/DreamSim/src/dream/locking/LockManager.java +++ b/DreamSim/src/dream/locking/LockManager.java @@ -7,23 +7,23 @@ interface LockManager { - /** - * Process the given request. Returns true if the lock can be granted. - * - * @param request - * the request. - * @return true if the lock is granted, false otherwise. - */ - boolean processLockRequest(LockRequestPacket request); + /** + * Process the given request. Returns true if the lock can be granted. + * + * @param request + * the request. + * @return true if the lock is granted, false otherwise. + */ + boolean processLockRequest(LockRequestPacket request); - /** - * Process the given release. Returns the set of pending requests that can now - * obtain a lock. - * - * @param release - * the release. - * @return the set of granted locks. - */ - Set processLockRelease(LockReleasePacket release); + /** + * Process the given release. Returns the set of pending requests that can now + * obtain a lock. + * + * @param release + * the release. + * @return the set of granted locks. + */ + Set processLockRelease(LockReleasePacket release); } \ No newline at end of file diff --git a/DreamSim/src/dream/locking/LockManagerFactory.java b/DreamSim/src/dream/locking/LockManagerFactory.java index 38480bd..5ae4558 100644 --- a/DreamSim/src/dream/locking/LockManagerFactory.java +++ b/DreamSim/src/dream/locking/LockManagerFactory.java @@ -7,12 +7,12 @@ public class LockManagerFactory implements PeerFactory { - @Override - public Peer createPeer(int peerIndex, Experiment experiment) { - final Peer newPeer = new Peer(peerIndex); - newPeer.addPeerlet(new LockManagerForwarder()); - newPeer.addPeerlet(new LockManagerMeasurementPeerlet()); - return newPeer; - } + @Override + public Peer createPeer(int peerIndex, Experiment experiment) { + final Peer newPeer = new Peer(peerIndex); + newPeer.addPeerlet(new LockManagerForwarder()); + newPeer.addPeerlet(new LockManagerMeasurementPeerlet()); + return newPeer; + } } diff --git a/DreamSim/src/dream/locking/LockManagerForwarder.java b/DreamSim/src/dream/locking/LockManagerForwarder.java index 62986ae..15859e3 100644 --- a/DreamSim/src/dream/locking/LockManagerForwarder.java +++ b/DreamSim/src/dream/locking/LockManagerForwarder.java @@ -16,54 +16,54 @@ import protopeer.network.NetworkAddress; public class LockManagerForwarder extends BasePeerlet { - private final Logger logger = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME); - private final LockManager lockManager = // - DreamConfiguration.get().consistencyType == DreamConfiguration.SIDUP // - ? new SidUpLockManager() // - : new DreamLockManager(); + private final Logger logger = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME); + private final LockManager lockManager = // + DreamConfiguration.get().consistencyType == DreamConfiguration.SIDUP // + ? new SidUpLockManager() // + : new DreamLockManager(); - @Override - public void init(Peer peer) { - super.init(peer); - } + @Override + public void init(Peer peer) { + super.init(peer); + } - @Override - public void handleIncomingMessage(Message packet) { - final Outbox outbox = new Outbox(); - if (packet instanceof LockRequestPacket) { - final LockRequestPacket reqPkt = (LockRequestPacket) packet; - logger.fine("Received a request packet: " + reqPkt); - processRequestPacket(packet.getSourceAddress(), reqPkt, outbox); - } else if (packet instanceof LockReleasePacket) { - final LockReleasePacket relPkt = (LockReleasePacket) packet; - logger.finer("Received a release packet: " + relPkt); - processReleasePacket(packet.getSourceAddress(), relPkt, outbox); - } - deliverPacketsInOutbox(outbox); - } + @Override + public void handleIncomingMessage(Message packet) { + final Outbox outbox = new Outbox(); + if (packet instanceof LockRequestPacket) { + final LockRequestPacket reqPkt = (LockRequestPacket) packet; + logger.fine("Received a request packet: " + reqPkt); + processRequestPacket(packet.getSourceAddress(), reqPkt, outbox); + } else if (packet instanceof LockReleasePacket) { + final LockReleasePacket relPkt = (LockReleasePacket) packet; + logger.finer("Received a release packet: " + relPkt); + processReleasePacket(packet.getSourceAddress(), relPkt, outbox); + } + deliverPacketsInOutbox(outbox); + } - private final void processRequestPacket(NetworkAddress sender, LockRequestPacket reqPkt, Outbox outbox) { - final boolean granted = lockManager.processLockRequest(reqPkt); - if (granted) { - final Collection recipients = new ArrayList<>(1); - recipients.add(sender); - outbox.add(LockGrantPacket.subject, new LockGrantPacket(reqPkt), recipients); - } - } + private final void processRequestPacket(NetworkAddress sender, LockRequestPacket reqPkt, Outbox outbox) { + final boolean granted = lockManager.processLockRequest(reqPkt); + if (granted) { + final Collection recipients = new ArrayList<>(1); + recipients.add(sender); + outbox.add(LockGrantPacket.subject, new LockGrantPacket(reqPkt), recipients); + } + } - private final void processReleasePacket(NetworkAddress sender, LockReleasePacket relPkt, Outbox outbox) { - final Set granted = lockManager.processLockRelease(relPkt); - granted.forEach(req -> { - final Collection recipients = new ArrayList<>(1); - recipients.add(req.getApplicant()); - outbox.add(LockGrantPacket.subject, new LockGrantPacket(req), recipients); - }); - } + private final void processReleasePacket(NetworkAddress sender, LockReleasePacket relPkt, Outbox outbox) { + final Set granted = lockManager.processLockRelease(relPkt); + granted.forEach(req -> { + final Collection recipients = new ArrayList<>(1); + recipients.add(req.getApplicant()); + outbox.add(LockGrantPacket.subject, new LockGrantPacket(req), recipients); + }); + } - private final void deliverPacketsInOutbox(Outbox outbox) { - outbox.getPacketsToSend().forEach(p -> { - outbox.getRecipientsFor(p).forEach(r -> getPeer().sendMessage(r, p)); - }); - } + private final void deliverPacketsInOutbox(Outbox outbox) { + outbox.getPacketsToSend().forEach(p -> { + outbox.getRecipientsFor(p).forEach(r -> getPeer().sendMessage(r, p)); + }); + } } diff --git a/DreamSim/src/dream/locking/SidUpLockManager.java b/DreamSim/src/dream/locking/SidUpLockManager.java index 3c35a49..1566611 100644 --- a/DreamSim/src/dream/locking/SidUpLockManager.java +++ b/DreamSim/src/dream/locking/SidUpLockManager.java @@ -9,46 +9,46 @@ import dream.common.packets.locking.LockRequestPacket; class SidUpLockManager implements LockManager { - private final Queue pendingRequests = new LinkedList<>(); - - @Override - public final boolean processLockRequest(LockRequestPacket request) { - pendingRequests.add(new PendingRequest(request)); - return pendingRequests.size() == 1; - } - - @Override - public final Set processLockRelease(LockReleasePacket release) { - assert !pendingRequests.isEmpty(); - final PendingRequest req = pendingRequests.peek(); - assert req.getRequest().getLockID().equals(release.getLockID()); - final Set result = new HashSet<>(); - if (req.decreaseCount()) { - pendingRequests.poll(); - result.add(pendingRequests.peek().getRequest()); - } - return result; - } - - private class PendingRequest { - private final LockRequestPacket request; - private int count; - - PendingRequest(LockRequestPacket request) { - super(); - this.request = request; - count = request.getUnlockNodes().size(); - } - - final LockRequestPacket getRequest() { - return request; - } - - boolean decreaseCount() { - count--; - return count == 0; - } - - } + private final Queue pendingRequests = new LinkedList<>(); + + @Override + public final boolean processLockRequest(LockRequestPacket request) { + pendingRequests.add(new PendingRequest(request)); + return pendingRequests.size() == 1; + } + + @Override + public final Set processLockRelease(LockReleasePacket release) { + assert !pendingRequests.isEmpty(); + final PendingRequest req = pendingRequests.peek(); + assert req.getRequest().getLockID().equals(release.getLockID()); + final Set result = new HashSet<>(); + if (req.decreaseCount()) { + pendingRequests.poll(); + result.add(pendingRequests.peek().getRequest()); + } + return result; + } + + private class PendingRequest { + private final LockRequestPacket request; + private int count; + + PendingRequest(LockRequestPacket request) { + super(); + this.request = request; + count = request.getUnlockNodes().size(); + } + + final LockRequestPacket getRequest() { + return request; + } + + boolean decreaseCount() { + count--; + return count == 0; + } + + } } diff --git a/DreamSim/src/dream/measurement/MeasurementLogger.java b/DreamSim/src/dream/measurement/MeasurementLogger.java index addf43f..3112e38 100755 --- a/DreamSim/src/dream/measurement/MeasurementLogger.java +++ b/DreamSim/src/dream/measurement/MeasurementLogger.java @@ -21,131 +21,131 @@ * @author Alessandro Margara */ public class MeasurementLogger { - private final Map storedMsgs = new HashMap(); - private final Map storedMsgsSize = new HashMap(); - private final Map> storedDelays = new HashMap>(); - private final int epochDuration; - private final int simulationTime; - private static MeasurementLogger logger = null; + private final Map storedMsgs = new HashMap(); + private final Map storedMsgsSize = new HashMap(); + private final Map> storedDelays = new HashMap>(); + private final int epochDuration; + private final int simulationTime; + private static MeasurementLogger logger = null; - public static MeasurementLogger getLogger() { - if (logger == null) { - logger = new MeasurementLogger(); - } - return logger; - } + public static MeasurementLogger getLogger() { + if (logger == null) { + logger = new MeasurementLogger(); + } + return logger; + } - private MeasurementLogger() { - // Private constructor - simulationTime = DreamConfiguration.get().simulationTimeInSeconds; - epochDuration = DreamConfiguration.get().epochDuration; - } + private MeasurementLogger() { + // Private constructor + simulationTime = DreamConfiguration.get().simulationTimeInSeconds; + epochDuration = DreamConfiguration.get().epochDuration; + } - public final void resetCounters() { - storedMsgs.clear(); - storedMsgsSize.clear(); - storedDelays.clear(); - } + public final void resetCounters() { + storedMsgs.clear(); + storedMsgsSize.clear(); + storedDelays.clear(); + } - public void saveMessage(Message msg) { - final String name = msg.getClass().getName(); - addMessage(name, msg); - } + public void saveMessage(Message msg) { + final String name = msg.getClass().getName(); + addMessage(name, msg); + } - public final void saveDelay(double delay) { - final double time = Experiment.getSingleton().getClock().getCurrentTime(); - final Integer epoch = (int) (time / epochDuration); - if (!storedDelays.containsKey(epoch)) { - final List content = new ArrayList(); - storedDelays.put(epoch, content); - } - storedDelays.get(epoch).add(delay); - } + public final void saveDelay(double delay) { + final double time = Experiment.getSingleton().getClock().getCurrentTime(); + final Integer epoch = (int) (time / epochDuration); + if (!storedDelays.containsKey(epoch)) { + final List content = new ArrayList(); + storedDelays.put(epoch, content); + } + storedDelays.get(epoch).add(delay); + } - public final void printResults(String filename) { - printTraffic(filename); - printDelay(filename); - printDelayAvg(filename); - } + public final void printResults(String filename) { + printTraffic(filename); + printDelay(filename); + printDelayAvg(filename); + } - private final void addMessage(String name, Message msg) { - if (!storedMsgs.containsKey(name)) { - storedMsgs.put(name, 1); - storedMsgsSize.put(name, Data.inByte(msg.getSize())); - } else { - final int previousCount = storedMsgs.remove(name); - storedMsgs.put(name, previousCount + 1); - final double previousSize = storedMsgsSize.remove(name); - storedMsgsSize.put(name, previousSize + Data.inByte(msg.getSize())); - } - } + private final void addMessage(String name, Message msg) { + if (!storedMsgs.containsKey(name)) { + storedMsgs.put(name, 1); + storedMsgsSize.put(name, Data.inByte(msg.getSize())); + } else { + final int previousCount = storedMsgs.remove(name); + storedMsgs.put(name, previousCount + 1); + final double previousSize = storedMsgsSize.remove(name); + storedMsgsSize.put(name, previousSize + Data.inByte(msg.getSize())); + } + } - private final void printTraffic(String filename) { - try { - final String filePrefix = DreamConfiguration.get().resultsDir + filename; - final FileOutputStream traffic = new FileOutputStream(filePrefix + "Traffic"); - final FileOutputStream trafficByte = new FileOutputStream(filePrefix + "TrafficByte"); - for (final String s : storedMsgs.keySet()) { - final int val = storedMsgs.get(s).intValue(); - traffic.write((s + "\t" + val + "\t" + (double) val / simulationTime + "\n").getBytes()); - } - for (final String s : storedMsgsSize.keySet()) { - final int val = storedMsgsSize.get(s).intValue(); - trafficByte.write((s + "\t" + val + "\t" + (double) val / simulationTime + "\n").getBytes()); - } - traffic.close(); - trafficByte.close(); - } catch (final FileNotFoundException e) { - e.printStackTrace(); - } catch (final IOException e) { - e.printStackTrace(); - } - } + private final void printTraffic(String filename) { + try { + final String filePrefix = DreamConfiguration.get().resultsDir + filename; + final FileOutputStream traffic = new FileOutputStream(filePrefix + "Traffic"); + final FileOutputStream trafficByte = new FileOutputStream(filePrefix + "TrafficByte"); + for (final String s : storedMsgs.keySet()) { + final int val = storedMsgs.get(s).intValue(); + traffic.write((s + "\t" + val + "\t" + (double) val / simulationTime + "\n").getBytes()); + } + for (final String s : storedMsgsSize.keySet()) { + final int val = storedMsgsSize.get(s).intValue(); + trafficByte.write((s + "\t" + val + "\t" + (double) val / simulationTime + "\n").getBytes()); + } + traffic.close(); + trafficByte.close(); + } catch (final FileNotFoundException e) { + e.printStackTrace(); + } catch (final IOException e) { + e.printStackTrace(); + } + } - private final void printDelay(String filename) { - try { - final String filePrefix = DreamConfiguration.get().resultsDir + filename; - final FileOutputStream delay = new FileOutputStream(filePrefix + "Delay"); - final List keyList = new ArrayList(storedDelays.keySet()); - Collections.sort(keyList); - for (final Integer label : keyList) { - double sum = 0; - final double size = storedDelays.get(label).size(); - for (final Double val : storedDelays.get(label)) { - sum += val; - } - final double avgVal = sum / size; - delay.write((label * epochDuration / 1000 + "\t" + avgVal + "\n").getBytes()); - } - delay.close(); - } catch (final FileNotFoundException e) { - e.printStackTrace(); - } catch (final IOException e) { - e.printStackTrace(); - } - } + private final void printDelay(String filename) { + try { + final String filePrefix = DreamConfiguration.get().resultsDir + filename; + final FileOutputStream delay = new FileOutputStream(filePrefix + "Delay"); + final List keyList = new ArrayList(storedDelays.keySet()); + Collections.sort(keyList); + for (final Integer label : keyList) { + double sum = 0; + final double size = storedDelays.get(label).size(); + for (final Double val : storedDelays.get(label)) { + sum += val; + } + final double avgVal = sum / size; + delay.write((label * epochDuration / 1000 + "\t" + avgVal + "\n").getBytes()); + } + delay.close(); + } catch (final FileNotFoundException e) { + e.printStackTrace(); + } catch (final IOException e) { + e.printStackTrace(); + } + } - private final void printDelayAvg(String filename) { - try { - final String filePrefix = DreamConfiguration.get().resultsDir + filename; - final FileOutputStream delay = new FileOutputStream(filePrefix + "DelayAvg"); - double sum = 0; - double count = 0; - for (final Integer key : storedDelays.keySet()) { - final List values = storedDelays.get(key); - for (final Double val : values) { - sum += val; - count++; - } - } - final double avgVal = sum / count; - delay.write((String.valueOf(avgVal) + "\n").getBytes()); - delay.close(); - } catch (final FileNotFoundException e) { - e.printStackTrace(); - } catch (final IOException e) { - e.printStackTrace(); - } - } + private final void printDelayAvg(String filename) { + try { + final String filePrefix = DreamConfiguration.get().resultsDir + filename; + final FileOutputStream delay = new FileOutputStream(filePrefix + "DelayAvg"); + double sum = 0; + double count = 0; + for (final Integer key : storedDelays.keySet()) { + final List values = storedDelays.get(key); + for (final Double val : values) { + sum += val; + count++; + } + } + final double avgVal = sum / count; + delay.write((String.valueOf(avgVal) + "\n").getBytes()); + delay.close(); + } catch (final FileNotFoundException e) { + e.printStackTrace(); + } catch (final IOException e) { + e.printStackTrace(); + } + } } diff --git a/DreamSim/src/dream/overlay/IClientAssociationGenerator.java b/DreamSim/src/dream/overlay/IClientAssociationGenerator.java index 9f39228..c2451c8 100755 --- a/DreamSim/src/dream/overlay/IClientAssociationGenerator.java +++ b/DreamSim/src/dream/overlay/IClientAssociationGenerator.java @@ -10,14 +10,14 @@ */ public interface IClientAssociationGenerator { - /** - * Get a list of associations between brokers and components. - */ - public Set getAssociation(); + /** + * Get a list of associations between brokers and components. + */ + public Set getAssociation(); - /** - * Reset the association. - */ - public void clean(); + /** + * Reset the association. + */ + public void clean(); } diff --git a/DreamSim/src/dream/overlay/IOverlayGenerator.java b/DreamSim/src/dream/overlay/IOverlayGenerator.java index 84fbbe3..42b2a9b 100755 --- a/DreamSim/src/dream/overlay/IOverlayGenerator.java +++ b/DreamSim/src/dream/overlay/IOverlayGenerator.java @@ -10,16 +10,16 @@ */ public interface IOverlayGenerator { - /** - * Generates a new random topology with the default number of links. The - * default number of links depends on the implementing classes. - * - * @return a set of links that constitutes the generated topology - */ - public Set generateOverlay(); + /** + * Generates a new random topology with the default number of links. The + * default number of links depends on the implementing classes. + * + * @return a set of links that constitutes the generated topology + */ + public Set generateOverlay(); - /** - * Reset the topology generation. - */ - public void clean(); + /** + * Reset the topology generation. + */ + public void clean(); } diff --git a/DreamSim/src/dream/overlay/IOverlayPeerlet.java b/DreamSim/src/dream/overlay/IOverlayPeerlet.java index 8ade0f0..e4cdaea 100755 --- a/DreamSim/src/dream/overlay/IOverlayPeerlet.java +++ b/DreamSim/src/dream/overlay/IOverlayPeerlet.java @@ -5,66 +5,70 @@ import protopeer.network.NetworkAddress; /** - * This is an interface for an Overlay Manager Peerlet. - * The purpose of implementors of this interface is to give access to the - * list of neighbor brokers and components. + * This is an interface for an Overlay Manager Peerlet. The purpose of + * implementors of this interface is to give access to the list of neighbor + * brokers and components. * * @author Daniel Dubois * */ public interface IOverlayPeerlet { - + /** * Add a broker to the the local view of the overlay of this broker. * - * @param broker New broker to add + * @param broker + * New broker to add * @return true if the broker has been added, false if it is already present */ public boolean addBroker(NetworkAddress broker); - + /** * Removes a broker from the local view of the overlay of this broker. * - * @param broker Existing broker to be removed + * @param broker + * Existing broker to be removed * @return true if the broker has been removed, false if it is not present */ public boolean removeBroker(NetworkAddress broker); - + /** * Add a component to the the local view of the overlay of this broker. * - * @param component New component to add + * @param component + * New component to add * @return true if the broker has been added, false if it is already present */ public boolean addComponent(NetworkAddress component); - + /** * Removes a component from the local view of the overlay of this broker. * - * @param component Existing component to be removed + * @param component + * Existing component to be removed * @return true if the broker has been removed, false if it is not present */ public boolean removeComponent(NetworkAddress component); - + /** * Get the set of broker and component neighbors of this broker. * * @return Set of broker and component neighbors */ public Set getNeighbors(); - + /** * Get the set of broker neighbors of this broker. * * @return Set of broker neighbors */ public Set getBrokers(); - + /** * Get the set of component neighbors of this broker. * * @return Set of component neighbors */ public Set getComponents(); - + } diff --git a/DreamSim/src/dream/overlay/Node.java b/DreamSim/src/dream/overlay/Node.java index c40bb25..2567d9d 100755 --- a/DreamSim/src/dream/overlay/Node.java +++ b/DreamSim/src/dream/overlay/Node.java @@ -10,44 +10,45 @@ */ public class Node implements Serializable { - private static final long serialVersionUID = 1096390051568045115L; - private final int id; - - /** - * Creates a generic node with the given id. - * - * @param id Id of the new generic node - */ - public Node(int id) { - this.id = id; - } - - /** - * Get the id of this generic node. - * - * @return id of this generic node - */ - public int getId() { - return id; - } - - @Override - public int hashCode() { - return id; - } - - @Override - public boolean equals(Object o) { - if (o instanceof Node) { - return ((Node) o).getId() == id; - } else { - return false; - } - } - - @Override - public String toString() { - return String.valueOf(id); - } + private static final long serialVersionUID = 1096390051568045115L; + private final int id; + + /** + * Creates a generic node with the given id. + * + * @param id + * Id of the new generic node + */ + public Node(int id) { + this.id = id; + } + + /** + * Get the id of this generic node. + * + * @return id of this generic node + */ + public int getId() { + return id; + } + + @Override + public int hashCode() { + return id; + } + + @Override + public boolean equals(Object o) { + if (o instanceof Node) { + return ((Node) o).getId() == id; + } else { + return false; + } + } + + @Override + public String toString() { + return String.valueOf(id); + } } diff --git a/DreamSim/src/dream/overlay/OverlayPeerlet.java b/DreamSim/src/dream/overlay/OverlayPeerlet.java index ab42914..f2bb4ef 100755 --- a/DreamSim/src/dream/overlay/OverlayPeerlet.java +++ b/DreamSim/src/dream/overlay/OverlayPeerlet.java @@ -15,83 +15,83 @@ import protopeer.network.NetworkAddress; public class OverlayPeerlet extends BasePeerlet implements IOverlayPeerlet { - private static final Logger logger = Logger.getLogger(OverlayPeerlet.class); - private final Set components = new HashSet(); - private final Set brokers = new HashSet(); - private Peer peer; + private static final Logger logger = Logger.getLogger(OverlayPeerlet.class); + private final Set components = new HashSet(); + private final Set brokers = new HashSet(); + private Peer peer; - @Override - public void init(Peer peer) { - this.peer = peer; - final IOverlayGenerator overlayGenerator = TreeOverlayGenerator.get(); - final Set initialTopology = overlayGenerator.generateOverlay(); - for (final Link l : initialTopology) { - if (l.getNode1().getId() == peer.getIndexNumber()) { - addBroker(Experiment.getSingleton().getAddressToBindTo(l.getNode2().getId())); - } else if (l.getNode2().getId() == peer.getIndexNumber()) { - addBroker(Experiment.getSingleton().getAddressToBindTo(l.getNode1().getId())); - } - } + @Override + public void init(Peer peer) { + this.peer = peer; + final IOverlayGenerator overlayGenerator = TreeOverlayGenerator.get(); + final Set initialTopology = overlayGenerator.generateOverlay(); + for (final Link l : initialTopology) { + if (l.getNode1().getId() == peer.getIndexNumber()) { + addBroker(Experiment.getSingleton().getAddressToBindTo(l.getNode2().getId())); + } else if (l.getNode2().getId() == peer.getIndexNumber()) { + addBroker(Experiment.getSingleton().getAddressToBindTo(l.getNode1().getId())); + } + } - final IClientAssociationGenerator associationGenerator = ClientAssociationGenerator.get(); - final Set componentAssociations = associationGenerator.getAssociation(); - for (final Link l : componentAssociations) { - if (l.getNode1().getId() == peer.getIndexNumber()) { - addComponent(Experiment.getSingleton().getAddressToBindTo(l.getNode2().getId())); - } - } - } + final IClientAssociationGenerator associationGenerator = ClientAssociationGenerator.get(); + final Set componentAssociations = associationGenerator.getAssociation(); + for (final Link l : componentAssociations) { + if (l.getNode1().getId() == peer.getIndexNumber()) { + addComponent(Experiment.getSingleton().getAddressToBindTo(l.getNode2().getId())); + } + } + } - @Override - public boolean addBroker(NetworkAddress broker) { - logger.info(peer.getIndexNumber() + ": adding broker " + broker); - return brokers.add(broker); - } + @Override + public boolean addBroker(NetworkAddress broker) { + logger.info(peer.getIndexNumber() + ": adding broker " + broker); + return brokers.add(broker); + } - @Override - public boolean removeBroker(NetworkAddress broker) { - logger.info(peer.getIndexNumber() + ": removing broker " + broker); - return brokers.remove(broker); - } + @Override + public boolean removeBroker(NetworkAddress broker) { + logger.info(peer.getIndexNumber() + ": removing broker " + broker); + return brokers.remove(broker); + } - @Override - public boolean addComponent(NetworkAddress component) { - logger.info(peer.getIndexNumber() + ": adding component " + component); - return components.add(component); - } + @Override + public boolean addComponent(NetworkAddress component) { + logger.info(peer.getIndexNumber() + ": adding component " + component); + return components.add(component); + } - @Override - public boolean removeComponent(NetworkAddress component) { - logger.info(peer.getIndexNumber() + ": removing component " + component); - return components.remove(component); - } + @Override + public boolean removeComponent(NetworkAddress component) { + logger.info(peer.getIndexNumber() + ": removing component " + component); + return components.remove(component); + } - @Override - public Set getNeighbors() { - final HashSet neighbors = new HashSet(brokers); - neighbors.addAll(components); - return neighbors; - } + @Override + public Set getNeighbors() { + final HashSet neighbors = new HashSet(brokers); + neighbors.addAll(components); + return neighbors; + } - @Override - public Set getBrokers() { - return new HashSet(brokers); - } + @Override + public Set getBrokers() { + return new HashSet(brokers); + } - @Override - public Set getComponents() { - return new HashSet(components); - } + @Override + public Set getComponents() { + return new HashSet(components); + } - @Override - public void handleIncomingMessage(Message message) { - if (message instanceof AddBrokerMessage) { - addBroker(((AddBrokerMessage) message).getNewBroker()); - } else if (message instanceof RemoveBrokerMessage) { - removeBroker(((RemoveBrokerMessage) message).getExistingBroker()); - } else if (message instanceof ReplaceBrokerMessage) { - addBroker(((ReplaceBrokerMessage) message).getNewBroker()); - removeBroker(((ReplaceBrokerMessage) message).getExistingBroker()); - } - } + @Override + public void handleIncomingMessage(Message message) { + if (message instanceof AddBrokerMessage) { + addBroker(((AddBrokerMessage) message).getNewBroker()); + } else if (message instanceof RemoveBrokerMessage) { + removeBroker(((RemoveBrokerMessage) message).getExistingBroker()); + } else if (message instanceof ReplaceBrokerMessage) { + addBroker(((ReplaceBrokerMessage) message).getNewBroker()); + removeBroker(((ReplaceBrokerMessage) message).getExistingBroker()); + } + } } diff --git a/DreamSim/src/dream/server/AdvertisementTable.java b/DreamSim/src/dream/server/AdvertisementTable.java index e766db6..8a6d8f0 100755 --- a/DreamSim/src/dream/server/AdvertisementTable.java +++ b/DreamSim/src/dream/server/AdvertisementTable.java @@ -13,38 +13,38 @@ import protopeer.network.NetworkAddress; final class AdvertisementTable { - private final Map> advs = new HashMap>(); - - final void addAdvertisement(NetworkAddress node, Advertisement adv) { - Collection advsList = advs.get(node); - if (advsList == null) { - advsList = new ArrayList(); - advs.put(node, advsList); - } - advsList.add(adv); - } - - final void removeAdvertisement(NetworkAddress node, Advertisement adv) { - final Collection advsList = advs.get(node); - if (advsList == null) { - return; - } - advsList.remove(adv); - if (advsList.isEmpty()) { - advs.remove(node); - } - } - - final Set getMatchingNodes(Subscription sub) { - final Predicate isAdvSat = adv -> adv.isSatisfiedBy(sub); - final Predicate hasAdvSat = node -> advs.get(node).stream().anyMatch(isAdvSat); - return advs.keySet().stream().// - filter(hasAdvSat).// - collect(Collectors.toSet()); - } - - final void removeAllAdvertisementsFor(NetworkAddress node) { - advs.remove(node); - } + private final Map> advs = new HashMap>(); + + final void addAdvertisement(NetworkAddress node, Advertisement adv) { + Collection advsList = advs.get(node); + if (advsList == null) { + advsList = new ArrayList(); + advs.put(node, advsList); + } + advsList.add(adv); + } + + final void removeAdvertisement(NetworkAddress node, Advertisement adv) { + final Collection advsList = advs.get(node); + if (advsList == null) { + return; + } + advsList.remove(adv); + if (advsList.isEmpty()) { + advs.remove(node); + } + } + + final Set getMatchingNodes(Subscription sub) { + final Predicate isAdvSat = adv -> adv.isSatisfiedBy(sub); + final Predicate hasAdvSat = node -> advs.get(node).stream().anyMatch(isAdvSat); + return advs.keySet().stream().// + filter(hasAdvSat).// + collect(Collectors.toSet()); + } + + final void removeAllAdvertisementsFor(NetworkAddress node) { + advs.remove(node); + } } diff --git a/DreamSim/src/dream/server/ServerEventForwarder.java b/DreamSim/src/dream/server/ServerEventForwarder.java index 4e1c013..8cd329b 100755 --- a/DreamSim/src/dream/server/ServerEventForwarder.java +++ b/DreamSim/src/dream/server/ServerEventForwarder.java @@ -17,98 +17,98 @@ import protopeer.network.NetworkAddress; public class ServerEventForwarder extends BasePeerlet { - protected final Logger logger = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME); - - protected final SubscriptionTable clientsSubTable = new SubscriptionTable(); - protected final SubscriptionTable brokersSubTable = new SubscriptionTable(); - protected final AdvertisementTable advTable = new AdvertisementTable(); - - protected IOverlayPeerlet overlay; - - @Override - public void init(Peer peer) { - super.init(peer); - overlay = (IOverlayPeerlet) peer.getPeerletOfType(IOverlayPeerlet.class); - } - - @Override - public final void handleIncomingMessage(Message packet) { - final Outbox outbox = new Outbox(); - if (packet instanceof SubscriptionPacket) { - final SubscriptionPacket subPkt = (SubscriptionPacket) packet; - logger.fine("Received a subscription packet: " + subPkt); - processSubscription(packet.getSourceAddress(), subPkt, outbox); - } else if (packet instanceof EventPacket) { - final EventPacket evPkt = (EventPacket) packet; - logger.finer("Received an event packet: " + evPkt); - processEvent(packet.getSourceAddress(), evPkt, outbox); - } else if (packet instanceof AdvertisementPacket) { - final AdvertisementPacket advPkt = (AdvertisementPacket) packet; - logger.fine("Received an advertisement packet: " + advPkt); - processAdvertisement(packet.getSourceAddress(), advPkt, outbox); - } - deliverPacketsInOutbox(outbox); - } - - private final void processSubscription(NetworkAddress sender, SubscriptionPacket packet, Outbox box) { - updateSubscriptionTables(sender, packet); - final Set matchingNodes = advTable.getMatchingNodes(packet.getSubscription()); - if (!matchingNodes.isEmpty()) { - sendTo(SubscriptionPacket.subject, packet, box, matchingNodes); - } - } - - private final void updateSubscriptionTables(NetworkAddress sender, SubscriptionPacket subPkt) { - final SubscriptionTable table = isClient(sender) ? clientsSubTable : brokersSubTable; - switch (subPkt.getSubType()) { - case SUB: - table.addSubscription(sender, subPkt.getSubscription()); - break; - case UNSUB: - table.removeSubscription(sender, subPkt.getSubscription()); - break; - default: - assert false : subPkt.getSubType(); - } - } - - private void processEvent(NetworkAddress sender, EventPacket packet, Outbox outbox) { - final Map matchingClients = clientsSubTable.getMatchingNodes(packet.getEvent()); - final Map matchingBrokers = brokersSubTable.getMatchingNodes(packet.getEvent()); - sendTo(EventPacket.subject, packet, outbox, matchingClients.keySet()); - sendTo(EventPacket.subject, packet, outbox, matchingBrokers.keySet()); - } - - private final void processAdvertisement(NetworkAddress sender, AdvertisementPacket packet, Outbox outbox) { - switch (packet.getAdvType()) { - case ADV: - advTable.addAdvertisement(sender, packet.getAdvertisement()); - break; - case UNADV: - advTable.removeAdvertisement(sender, packet.getAdvertisement()); - break; - } - outbox.add(AdvertisementPacket.subject, packet, getAllNodesExcept(sender)); - } - - private final void sendTo(String subject, Message packet, Outbox box, Collection recipients) { - box.add(subject, packet, recipients); - } - - private final Collection getAllNodesExcept(NetworkAddress nodeToSkip) { - return overlay.getNeighbors().stream()// - .filter(node -> !node.equals(nodeToSkip))// - .collect(Collectors.toSet()); - } - - private final void deliverPacketsInOutbox(Outbox outbox) { - outbox.getPacketsToSend().forEach(p -> { - outbox.getRecipientsFor(p).forEach(r -> getPeer().sendMessage(r, p)); - }); - } - - private final boolean isClient(NetworkAddress address) { - return overlay.getComponents().contains(address); - } + protected final Logger logger = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME); + + protected final SubscriptionTable clientsSubTable = new SubscriptionTable(); + protected final SubscriptionTable brokersSubTable = new SubscriptionTable(); + protected final AdvertisementTable advTable = new AdvertisementTable(); + + protected IOverlayPeerlet overlay; + + @Override + public void init(Peer peer) { + super.init(peer); + overlay = (IOverlayPeerlet) peer.getPeerletOfType(IOverlayPeerlet.class); + } + + @Override + public final void handleIncomingMessage(Message packet) { + final Outbox outbox = new Outbox(); + if (packet instanceof SubscriptionPacket) { + final SubscriptionPacket subPkt = (SubscriptionPacket) packet; + logger.fine("Received a subscription packet: " + subPkt); + processSubscription(packet.getSourceAddress(), subPkt, outbox); + } else if (packet instanceof EventPacket) { + final EventPacket evPkt = (EventPacket) packet; + logger.finer("Received an event packet: " + evPkt); + processEvent(packet.getSourceAddress(), evPkt, outbox); + } else if (packet instanceof AdvertisementPacket) { + final AdvertisementPacket advPkt = (AdvertisementPacket) packet; + logger.fine("Received an advertisement packet: " + advPkt); + processAdvertisement(packet.getSourceAddress(), advPkt, outbox); + } + deliverPacketsInOutbox(outbox); + } + + private final void processSubscription(NetworkAddress sender, SubscriptionPacket packet, Outbox box) { + updateSubscriptionTables(sender, packet); + final Set matchingNodes = advTable.getMatchingNodes(packet.getSubscription()); + if (!matchingNodes.isEmpty()) { + sendTo(SubscriptionPacket.subject, packet, box, matchingNodes); + } + } + + private final void updateSubscriptionTables(NetworkAddress sender, SubscriptionPacket subPkt) { + final SubscriptionTable table = isClient(sender) ? clientsSubTable : brokersSubTable; + switch (subPkt.getSubType()) { + case SUB: + table.addSubscription(sender, subPkt.getSubscription()); + break; + case UNSUB: + table.removeSubscription(sender, subPkt.getSubscription()); + break; + default: + assert false : subPkt.getSubType(); + } + } + + private void processEvent(NetworkAddress sender, EventPacket packet, Outbox outbox) { + final Map matchingClients = clientsSubTable.getMatchingNodes(packet.getEvent()); + final Map matchingBrokers = brokersSubTable.getMatchingNodes(packet.getEvent()); + sendTo(EventPacket.subject, packet, outbox, matchingClients.keySet()); + sendTo(EventPacket.subject, packet, outbox, matchingBrokers.keySet()); + } + + private final void processAdvertisement(NetworkAddress sender, AdvertisementPacket packet, Outbox outbox) { + switch (packet.getAdvType()) { + case ADV: + advTable.addAdvertisement(sender, packet.getAdvertisement()); + break; + case UNADV: + advTable.removeAdvertisement(sender, packet.getAdvertisement()); + break; + } + outbox.add(AdvertisementPacket.subject, packet, getAllNodesExcept(sender)); + } + + private final void sendTo(String subject, Message packet, Outbox box, Collection recipients) { + box.add(subject, packet, recipients); + } + + private final Collection getAllNodesExcept(NetworkAddress nodeToSkip) { + return overlay.getNeighbors().stream()// + .filter(node -> !node.equals(nodeToSkip))// + .collect(Collectors.toSet()); + } + + private final void deliverPacketsInOutbox(Outbox outbox) { + outbox.getPacketsToSend().forEach(p -> { + outbox.getRecipientsFor(p).forEach(r -> getPeer().sendMessage(r, p)); + }); + } + + private final boolean isClient(NetworkAddress address) { + return overlay.getComponents().contains(address); + } } diff --git a/DreamSim/src/dream/server/ServerFactory.java b/DreamSim/src/dream/server/ServerFactory.java index 20c294b..82342d7 100644 --- a/DreamSim/src/dream/server/ServerFactory.java +++ b/DreamSim/src/dream/server/ServerFactory.java @@ -8,13 +8,13 @@ public class ServerFactory implements PeerFactory { - @Override - public Peer createPeer(int peerIndex, Experiment experiment) { - final Peer newPeer = new Peer(peerIndex); - newPeer.addPeerlet(new OverlayPeerlet()); - newPeer.addPeerlet(new ServerMeasurementPeerlet()); - newPeer.addPeerlet(new ServerEventForwarder()); - return newPeer; - } + @Override + public Peer createPeer(int peerIndex, Experiment experiment) { + final Peer newPeer = new Peer(peerIndex); + newPeer.addPeerlet(new OverlayPeerlet()); + newPeer.addPeerlet(new ServerMeasurementPeerlet()); + newPeer.addPeerlet(new ServerEventForwarder()); + return newPeer; + } } diff --git a/DreamSim/src/dream/server/SubscriptionTable.java b/DreamSim/src/dream/server/SubscriptionTable.java index 9842498..ad7bb4a 100755 --- a/DreamSim/src/dream/server/SubscriptionTable.java +++ b/DreamSim/src/dream/server/SubscriptionTable.java @@ -10,51 +10,51 @@ import protopeer.network.NetworkAddress; final class SubscriptionTable { - private final Map> subs = new HashMap<>(); - - final void addSubscription(NetworkAddress node, Subscription sub) { - Collection subsList = subs.get(node); - if (subsList == null) { - subsList = new ArrayList(); - subs.put(node, subsList); - } - subsList.add(sub); - } - - final void removeSubscription(NetworkAddress node, Subscription sub) { - final Collection subsList = subs.get(node); - if (subsList == null) { - return; - } - subsList.remove(sub); - if (subsList.isEmpty()) { - subs.remove(node); - } - } - - final Map getMatchingNodes(Event ev) { - final Map result = new HashMap(); - for (final NetworkAddress node : subs.keySet()) { - int count = 0; - for (final Subscription sub : subs.get(node)) { - if (sub.isSatisfiedBy(ev)) { - count++; - } - } - if (count != 0) { - result.put(node, count); - } - } - return result; - } - - final void removeAllSubscriptionsFor(NetworkAddress node) { - subs.remove(node); - } - - @Override - public String toString() { - return "SubscriptionTable [subs=" + subs + "]"; - } + private final Map> subs = new HashMap<>(); + + final void addSubscription(NetworkAddress node, Subscription sub) { + Collection subsList = subs.get(node); + if (subsList == null) { + subsList = new ArrayList(); + subs.put(node, subsList); + } + subsList.add(sub); + } + + final void removeSubscription(NetworkAddress node, Subscription sub) { + final Collection subsList = subs.get(node); + if (subsList == null) { + return; + } + subsList.remove(sub); + if (subsList.isEmpty()) { + subs.remove(node); + } + } + + final Map getMatchingNodes(Event ev) { + final Map result = new HashMap(); + for (final NetworkAddress node : subs.keySet()) { + int count = 0; + for (final Subscription sub : subs.get(node)) { + if (sub.isSatisfiedBy(ev)) { + count++; + } + } + if (count != 0) { + result.put(node, count); + } + } + return result; + } + + final void removeAllSubscriptionsFor(NetworkAddress node) { + subs.remove(node); + } + + @Override + public String toString() { + return "SubscriptionTable [subs=" + subs + "]"; + } } From 03d8e3956475a7207624a075e1a955fd391fe135 Mon Sep 17 00:00:00 2001 From: Alessandro Margara Date: Wed, 9 Mar 2016 14:10:38 +0100 Subject: [PATCH 026/161] Plot scripts adapted to the names used in the paper --- DreamSim/scripts/plotScriptSidUp.plot | 2 +- DreamSim/scripts/plotScriptSidUpIntervals.plot | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DreamSim/scripts/plotScriptSidUp.plot b/DreamSim/scripts/plotScriptSidUp.plot index 2b7b94b..8e190e2 100644 --- a/DreamSim/scripts/plotScriptSidUp.plot +++ b/DreamSim/scripts/plotScriptSidUp.plot @@ -135,7 +135,7 @@ plot "../resultsAvg/numGraphDependencies_causal_TrafficByte" u ($1):($8/1000) t # GRAPH SHARE PROBABILITY # ########################### -set xlabel "Probability for a signal to depend on multiple sources" offset 0,0.2 +set xlabel "Degree of nodes sharing" offset 0,0.2 set output "../graphs/graphShareDelay.ps" set ylabel "Average Delay (ms)" offset 0.5,0 diff --git a/DreamSim/scripts/plotScriptSidUpIntervals.plot b/DreamSim/scripts/plotScriptSidUpIntervals.plot index 1899433..ff1149e 100644 --- a/DreamSim/scripts/plotScriptSidUpIntervals.plot +++ b/DreamSim/scripts/plotScriptSidUpIntervals.plot @@ -185,7 +185,7 @@ plot "../resultsAvg/numGraphDependencies_causal_TrafficByte" u ($1):($8/1000):($ # GRAPH SHARE PROBABILITY # ########################### -set xlabel "Probability for a signal to depend on multiple sources" offset 0,0.2 +set xlabel "Degree of nodes sharing" offset 0,0.2 set output "../graphs/graphShareDelay.ps" set ylabel "Average Delay (ms)" offset 0.5,0 From 48437f5f88019887434307b1c9e11383f235b474 Mon Sep 17 00:00:00 2001 From: Tobias Becker Date: Fri, 18 Mar 2016 11:16:44 +0100 Subject: [PATCH 027/161] rewrote to decentralized architecture --- .../java/dream/examples/chat/Chat.java | 74 ++++++++++++------- .../java/dream/examples/chat/ChatGUI.java | 5 -- .../java/dream/examples/chat/ChatServer.java | 70 +----------------- 3 files changed, 50 insertions(+), 99 deletions(-) diff --git a/Dream2/src/examples/java/dream/examples/chat/Chat.java b/Dream2/src/examples/java/dream/examples/chat/Chat.java index 4742d3c..ccf2fa1 100644 --- a/Dream2/src/examples/java/dream/examples/chat/Chat.java +++ b/Dream2/src/examples/java/dream/examples/chat/Chat.java @@ -2,65 +2,80 @@ import java.awt.EventQueue; import java.util.ArrayList; -import java.util.Set; +import java.util.List; +import java.util.stream.Collectors; -import dream.client.DreamClient; +import dream.client.ChangeEventHandler; import dream.client.RemoteVar; import dream.client.Signal; import dream.client.Var; import dream.common.Consts; import javafx.util.Pair; -public class Chat { +public class Chat implements ChangeEventHandler { - private RemoteVar remoteMessages; private Var myMessages; + private Var incoming; private String userName; private ChatGUI gui; + private List listening; + Signal> onlineList; public Chat(String username) throws Exception { this.userName = username; Consts.hostName = userName; + + listening = new ArrayList<>(); // Establish new session with server RemoteVar> registeredClients = new RemoteVar>(ChatServer.NAME, ChatServer.SERVER_REGISTERED_CLIENTS); - Signal> onlineList = new Signal>("setup", () -> { + onlineList = new Signal>("setup", () -> { if (registeredClients.get() == null) return new ArrayList(); else return registeredClients.get(); } , registeredClients); onlineList.change().addHandler((o, n) -> { - if (n.contains(username) && gui == null) { + if (n.contains("chat_message@" + username) && gui == null) { System.out.println("Setup: Server Registration done!"); - setup(n); - } else - gui.setOnline(n); + setup(); + } + List names = n.stream().map(x -> x.split("@")[1]).collect(Collectors.toList()); + gui.setOnline(names); + checkConnections(n); }); myMessages = new Var("chat_message", ""); - + incoming = new Var("incoming_messages", ""); System.out.println("Setup: Waiting for Registration to Server ..."); } - private void setup(ArrayList online) { - if (gui != null) - return; - Set vars = DreamClient.instance.listVariables(); - String serverVar = vars.stream().map(x -> new Pair(x.split("@")[1], x.split("@")[0])).// Pair(Host,Var) - filter(x -> x.getKey().equals(ChatServer.NAME) && x.getValue().contains(userName)).// - reduce(null, (a, b) -> b).getValue(); - System.out.println("Setup: Listening to Main Chat provided by Server"); - remoteMessages = new RemoteVar(ChatServer.NAME, serverVar); - - Signal incoming = new Signal("incoming", () -> { - if (remoteMessages.get() != null) - return remoteMessages.get(); - else - return ""; - } , remoteMessages); + private void checkConnections(ArrayList n) { + n.stream().map(x -> new Pair(x.split("@")[1], x.split("@")[0])).// Pair(Host,Var) + filter(x -> !listening.contains(x.getKey()) && x.getValue().startsWith("chat_")).// + forEach(x -> { + RemoteVar temp = new RemoteVar<>(x.getKey(), x.getValue()); + listening.add(x.getKey()); + new Signal("incoming" + x.getKey(), () -> { + if (temp.get() != null) + return temp.get(); + else + return ""; + } , temp).change().addHandler(this); + System.out.println("Adding listener to " + x.getKey()); + }); + /* + * if (!n.stream().map(x -> listening.contains(x)).reduce((a, b) -> a && + * b).orElse(false)) { System.out.println("checking again in 5"); try { + * Thread.sleep(5000); } catch (InterruptedException e) { + * e.printStackTrace(); } + * + * checkConnections(onlineList.get()); } + */ + } + private void setup() { Signal display = new Signal("display", () -> { if (incoming.get().startsWith("/")) { String[] temp = incoming.get().split(" ", 2); @@ -83,14 +98,17 @@ private void setup(ArrayList online) { gui = new ChatGUI(userName); gui.setListener(this); - System.out.println("Setup: Initializing Online-List"); - gui.setOnline(online); display.change().addHandler((oldValue, newValue) -> { if (newValue != null) gui.displayMessage(newValue); }); } + @Override + public void handle(String oldVal, String newVal) { + incoming.set(newVal); + } + protected void sendMessage(String text) { myMessages.set(text); } diff --git a/Dream2/src/examples/java/dream/examples/chat/ChatGUI.java b/Dream2/src/examples/java/dream/examples/chat/ChatGUI.java index 52a0ea3..ee93073 100644 --- a/Dream2/src/examples/java/dream/examples/chat/ChatGUI.java +++ b/Dream2/src/examples/java/dream/examples/chat/ChatGUI.java @@ -63,8 +63,6 @@ public void displayMessage(String text) { private List lastOnline; public void setOnline(List online) { - System.out.println("listModel: " + listModel); - System.out.println("onlineList: " + online); if (lastOnline != null) { for (String s : lastOnline) { if (!online.contains(s)) @@ -81,7 +79,6 @@ public void setOnline(List online) { if (!online.contains(listModel.get(i))) offlineList.add(listModel.get(i)); } - System.out.println("offlineList: " + offlineList); SwingUtilities.invokeLater(new Runnable() { @Override @@ -92,8 +89,6 @@ public void run() { } for (String e : offlineList) listModel.addElement(e); - System.out.println("listModel: " + listModel); - System.out.println("Interval: [0, " + (online.size() - 1) + "]"); statusList.setSelectionInterval(0, online.size() - 1); } }); diff --git a/Dream2/src/examples/java/dream/examples/chat/ChatServer.java b/Dream2/src/examples/java/dream/examples/chat/ChatServer.java index 08d31a0..b9674bf 100644 --- a/Dream2/src/examples/java/dream/examples/chat/ChatServer.java +++ b/Dream2/src/examples/java/dream/examples/chat/ChatServer.java @@ -3,13 +3,9 @@ import java.math.BigInteger; import java.security.SecureRandom; import java.util.ArrayList; -import java.util.HashMap; -import java.util.Map; import java.util.Set; import dream.client.DreamClient; -import dream.client.RemoteVar; -import dream.client.Signal; import dream.client.Var; import dream.common.Consts; import dream.locking.LockManagerLauncher; @@ -22,7 +18,6 @@ public class ChatServer { private boolean serverStarted = false; private boolean lockManagerStarted = false; - private Map> clientVars; private Var> clients; private static SecureRandom r = new SecureRandom(); @@ -38,7 +33,6 @@ private void initServer() { Consts.hostName = NAME; clients = new Var>(SERVER_REGISTERED_CLIENTS, new ArrayList()); - clientVars = new HashMap>(); detectNewSession(); } @@ -48,7 +42,9 @@ private void initServer() { private void detectNewSession() { Set vars = DreamClient.instance.listVariables(); vars.stream().map(x -> new Pair(x.split("@")[1], x.split("@")[0])).// Pair(Host,Var) - filter(x -> !clientVars.keySet().contains(x.getKey()) && x.getValue().startsWith("chat_")).// + filter(x -> !clients.get().contains(x.getValue() + "@" + x.getKey()) + && x.getValue().startsWith("chat_")) + .// forEach(x -> createNewSessionFor(x.getKey(), x.getValue())); try { Thread.sleep(5000); @@ -70,66 +66,8 @@ private void detectNewSession() { * the name of the variable, must be of type String */ private void createNewSessionFor(String clientName, String clientVar) { - // x = "chat_xxx" - RemoteVar remote = new RemoteVar(clientName, clientVar); - Signal listen = new Signal(SERVER_PREFIX + "listen", () -> { - if (remote.get() == null) - return ""; - else - return remote.get(); - } , remote); - - // handler for "client writes something" - listen.change().addHandler((oldValue, newValue) -> clientWrote(clientName, newValue)); - - // create new var for sending messages to this client - String reply = getRandom(); - Var replyChannel = new Var(reply + clientName, clientName); - // add client as registered - clients.modify((old) -> old.add(clientName)); - clientVars.put(clientName, replyChannel); - } - - /** - * called when the server receives a new message from a client - * - * @param name - * @param text - */ - private void clientWrote(String name, String text) { - if (text.startsWith("/")) { - String[] temp = text.split(" ", 2); - String command = temp[0].substring(1, temp[0].length()); - String rest = temp.length > 1 ? temp[1] : ""; - // QUIT - for now only used to update the registeredClients list - // (aka who's online) - if (command.equalsIgnoreCase("QUIT") || command.equalsIgnoreCase("Q")) { - clients.modify((old) -> old.remove(name)); - } else if (command.equalsIgnoreCase("PRIVMSG") || command.equalsIgnoreCase("W") - || command.equalsIgnoreCase("WHISPER")) { - String[] temp1 = rest.split(" ", 2); - String target = temp1[0]; - String message = temp1.length > 1 ? temp1[1] : ""; - if (message.isEmpty()) { - // no actual message - // TODO: Reply to sender or ignore - } else if (!clientVars.containsKey(target) || !clients.get().contains(target)) { - // target is not known to the server or target is offline - // TODO: Reply to sender or send if target comes online or - // ignore - } else { - clientVars.get(target).set("/w " + name + " " + message); - } - } - System.out.println("Server: " + name + " USED " + command); - } else { - System.out.println("Server: " + name + " -> " + text); - clientVars.forEach((client, var) -> { - if (client != name) - var.set(name + ": " + text); - }); - } + clients.modify((old) -> old.add(clientVar + "@" + clientName)); } /** From a4e1e36854c6da4a61db2951e2ecbd2f0070c829 Mon Sep 17 00:00:00 2001 From: Tobias Becker Date: Fri, 25 Mar 2016 17:42:00 +0100 Subject: [PATCH 028/161] added names to messages --- Dream2/src/examples/java/dream/examples/chat/Chat.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Dream2/src/examples/java/dream/examples/chat/Chat.java b/Dream2/src/examples/java/dream/examples/chat/Chat.java index ccf2fa1..82d5ce8 100644 --- a/Dream2/src/examples/java/dream/examples/chat/Chat.java +++ b/Dream2/src/examples/java/dream/examples/chat/Chat.java @@ -35,7 +35,7 @@ public Chat(String username) throws Exception { return new ArrayList(); else return registeredClients.get(); - } , registeredClients); + }, registeredClients); onlineList.change().addHandler((o, n) -> { if (n.contains("chat_message@" + username) && gui == null) { System.out.println("Setup: Server Registration done!"); @@ -59,10 +59,10 @@ private void checkConnections(ArrayList n) { listening.add(x.getKey()); new Signal("incoming" + x.getKey(), () -> { if (temp.get() != null) - return temp.get(); + return x.getKey() + ": " + temp.get(); else return ""; - } , temp).change().addHandler(this); + }, temp).change().addHandler(this); System.out.println("Adding listener to " + x.getKey()); }); /* @@ -92,7 +92,7 @@ private void setup() { return null; } else return incoming.get(); - } , incoming); + }, incoming); System.out.println("Setup: Starting GUI"); gui = new ChatGUI(userName); From 26d2867467efa5f18e7ecad852119719c41edce5 Mon Sep 17 00:00:00 2001 From: Tobias Becker Date: Fri, 25 Mar 2016 19:34:49 +0100 Subject: [PATCH 029/161] adds a example dependency graph --- .../dream/examples/chat/DependencyGraph.dot | 14 ++++++++++++++ .../dream/examples/chat/DependencyGraph.png | Bin 0 -> 39530 bytes 2 files changed, 14 insertions(+) create mode 100644 Dream2/src/examples/java/dream/examples/chat/DependencyGraph.dot create mode 100644 Dream2/src/examples/java/dream/examples/chat/DependencyGraph.png diff --git a/Dream2/src/examples/java/dream/examples/chat/DependencyGraph.dot b/Dream2/src/examples/java/dream/examples/chat/DependencyGraph.dot new file mode 100644 index 0000000..6e68b34 --- /dev/null +++ b/Dream2/src/examples/java/dream/examples/chat/DependencyGraph.dot @@ -0,0 +1,14 @@ +digraph G { +"incoming_messages@Bob" -> "display@Bob" +"chat_message@Bob" -> "incomingBob@Bob" +"chat_message@Alice" -> "incomingAlice@Bob" +"incomingAlice@Bob" -> "incoming_messages@Bob" +"incomingBob@Bob" -> "incoming_messages@Bob" +"incoming_messages@Alice" -> "display@Alice" +"chat_message@Bob" -> "incomingBob@Alice" +"chat_message@Alice" -> "incomingAlice@Alice" +"incomingAlice@Alice" -> "incoming_messages@Alice" +"incomingBob@Alice" -> "incoming_messages@Alice" +"server_RegisteredClients@ChatServer" -> "setup@Bob" +"server_RegisteredClients@ChatServer" -> "setup@Alice" +} \ No newline at end of file diff --git a/Dream2/src/examples/java/dream/examples/chat/DependencyGraph.png b/Dream2/src/examples/java/dream/examples/chat/DependencyGraph.png new file mode 100644 index 0000000000000000000000000000000000000000..1ba7adad3f956d7430e69913060389f4854c58b9 GIT binary patch literal 39530 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 E0}%mvH~;_u literal 0 HcmV?d00001 From 547dcbc5ab69f10112a7b29054d0fa430a516991 Mon Sep 17 00:00:00 2001 From: Alessandro Margara Date: Wed, 6 Apr 2016 12:16:09 +0200 Subject: [PATCH 030/161] Fixed a bug in the remote example --- .../java/dream/examples/remote/RemoteSignalExample.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dream2/src/examples/java/dream/examples/remote/RemoteSignalExample.java b/Dream2/src/examples/java/dream/examples/remote/RemoteSignalExample.java index 7647491..9d3eeee 100755 --- a/Dream2/src/examples/java/dream/examples/remote/RemoteSignalExample.java +++ b/Dream2/src/examples/java/dream/examples/remote/RemoteSignalExample.java @@ -1,7 +1,7 @@ package dream.examples.remote; +import java.util.ArrayList; import java.util.HashSet; -import java.util.LinkedList; import java.util.Set; import dream.client.DreamClient; @@ -33,7 +33,7 @@ public static void main(String args[]) { final RemoteVar remoteInt = new RemoteVar("Remote", "remoteInt"); final RemoteVar remoteString1 = new RemoteVar("Remote", "remoteString1"); final RemoteVar remoteString2 = new RemoteVar("Remote", "remoteString2"); - final RemoteVar> remoteList = new RemoteVar<>("Remote", "remoteList"); + final RemoteVar> remoteList = new RemoteVar<>("Remote", "remoteList"); final Signal signal1 = new Signal("signal1", () -> remoteInt.get() + remoteString1.get().length(), remoteInt, remoteString1); From 66db204d70b250d3e6efc00f3a466da404023ca7 Mon Sep 17 00:00:00 2001 From: Tobias Becker Date: Sun, 17 Apr 2016 17:23:18 +0200 Subject: [PATCH 031/161] chat assuming only fifo consistency --- .../java/dream/examples/chat_fifo/Chat.java | 205 ++++++++++++++ .../dream/examples/chat_fifo/ChatGUI.java | 255 ++++++++++++++++++ .../dream/examples/chat_fifo/ChatServer.java | 119 ++++++++ .../dream/examples/chat_fifo/VectorClock.java | 252 +++++++++++++++++ 4 files changed, 831 insertions(+) create mode 100644 Dream2/src/examples/java/dream/examples/chat_fifo/Chat.java create mode 100644 Dream2/src/examples/java/dream/examples/chat_fifo/ChatGUI.java create mode 100644 Dream2/src/examples/java/dream/examples/chat_fifo/ChatServer.java create mode 100644 Dream2/src/examples/java/dream/examples/chat_fifo/VectorClock.java diff --git a/Dream2/src/examples/java/dream/examples/chat_fifo/Chat.java b/Dream2/src/examples/java/dream/examples/chat_fifo/Chat.java new file mode 100644 index 0000000..f82c79b --- /dev/null +++ b/Dream2/src/examples/java/dream/examples/chat_fifo/Chat.java @@ -0,0 +1,205 @@ +package dream.examples.chat_fifo; + +import java.awt.EventQueue; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.stream.Collectors; + +import dream.client.RemoteVar; +import dream.client.Signal; +import dream.client.Var; +import dream.common.Consts; + +public class Chat { + + private Var myMessages; + private Var incoming; + private String userName; + private ChatGUI gui; + private List listening; + Signal> onlineList; + private VectorClock clock = new VectorClock(); + private ArrayList> allMessages = new ArrayList>(); + + public Chat(String username) throws Exception { + this.userName = username; + + Consts.hostName = userName; + Logger.getGlobal().setLevel(Level.ALL); + listening = new ArrayList<>(); + // Establish new session with server + RemoteVar> registeredClients = new RemoteVar>(ChatServer.NAME, + ChatServer.SERVER_REGISTERED_CLIENTS); + onlineList = new Signal>("setup", () -> { + if (registeredClients.get() == null) + return new ArrayList(); + else + return registeredClients.get(); + }, registeredClients); + onlineList.change().addHandler((o, n) -> { + if (n.contains("chat_message@" + username) && gui == null) { + System.out.println("Setup: Server Registration done!"); + setup(); + } + List names = n.stream().map(x -> x.split("@")[1]).collect(Collectors.toList()); + setOnline(names); + checkConnections(n); + }); + + myMessages = new Var("chat_message", ""); + incoming = new Var("incoming_messages", ""); + System.out.println("Setup: Waiting for Registration to Server ..."); + } + + private List lastOnline; + + private void setOnline(List online) { + if (lastOnline != null) { + for (String s : lastOnline) { + if (!online.contains(s)) { + String msg = s + " has left the Chat."; + clock.incrementClock(userName); + allMessages.add(new Pair(msg, clock)); + gui.displayMessage(msg); + } + } + for (String s : online) { + if (!lastOnline.contains(s)) { + String msg = s + " has joined."; + clock.incrementClock(userName); + allMessages.add(new Pair(msg, clock)); + gui.displayMessage(msg); + } + } + } + lastOnline = online; + gui.setOnline(online); + } + + private void checkConnections(ArrayList n) { + n.stream().map(x -> new Pair(x.split("@")[1], x.split("@")[0])).// Pair(Host,Var) + filter(x -> !listening.contains(x.getHost()) && x.getVar().startsWith("chat_")).// + forEach(x -> { + RemoteVar temp = new RemoteVar<>(x.getHost(), x.getVar()); + listening.add(x.getHost()); + new Signal("incoming" + x.getHost(), () -> { + if (temp.get() != null) + return x.getHost() + ": " + temp.get(); + else + return ""; + }, temp).change().addHandler((oldVal, newVal) -> incoming.set(newVal)); + System.out.println("Adding listener to " + x.getHost()); + }); + /* + * if (!n.stream().map(x -> listening.contains(x)).reduce((a, b) -> a && + * b).orElse(false)) { System.out.println("checking again in 5"); try { + * Thread.sleep(5000); } catch (InterruptedException e) { + * e.printStackTrace(); } + * + * checkConnections(onlineList.get()); } + */ + } + + private void setup() { + Signal display = new Signal("display", () -> { + String msg = incoming.get().split("\\|", 2)[0]; + if (msg.startsWith("/")) { + String[] temp = msg.split(" ", 2); + String command = temp[0].substring(1, temp[0].length()); + String rest = temp.length > 1 ? temp[1] : ""; + // QUIT - for now only used to update the registeredClients list + // (aka who's online) + if (command.equalsIgnoreCase("W")) { + String[] temp1 = rest.split(" ", 2); + String sender = temp1[0]; + String message = temp1.length > 1 ? temp1[1] : ""; + return sender + " whispered: " + message; + } else + return null; + } else + return msg; + }, incoming); + + System.out.println("Setup: Starting GUI"); + gui = new ChatGUI(userName); + gui.setListener(this); + + display.change().addHandler((oldValue, newValue) -> { + if (newValue != null) { + String msg = incoming.get().split("\\|", 2)[0]; + String incClock = incoming.get().split("\\|", 2)[1]; + Logger.getGlobal().fine("Received Message: " + incoming.get()); + Logger.getGlobal().finer("Current clock: " + clock.toString()); + VectorClock incomingClock = VectorClock.fromString(incClock); + allMessages.add(new Pair(msg, incomingClock)); + Collections.sort(allMessages, (p1, p2) -> new VectorClock().compare(p1.getSecond(), p2.getSecond())); + clock = VectorClock.max(clock, incomingClock); + gui.displayMessage(allMessages); + } + }); + } + + protected void sendMessage(String text) { + clock.incrementClock(userName); + String clockString = clock.toString(); + if (!text.startsWith("/")) { + allMessages.add(new Pair("You: " + text, clock)); + gui.displayMessage("You: " + text); + } + myMessages.set(text + "|" + clockString); + Logger.getGlobal().fine("Send Message: " + text + "|" + clockString); + Logger.getGlobal().finer("Current clock: " + clock.toString()); + } + + public static void main(String[] args) { + try { + if (args.length < 1) { + System.out.println("username missing"); + return; + } + EventQueue.invokeLater(new Runnable() { + + @Override + public void run() { + try { + new Chat(args[0]); + } catch (Exception e) { + e.printStackTrace(); + } + + } + }); + } catch (Exception e) { + e.printStackTrace(); + } + } +} + +class Pair { + private final S first; + private final T second; + + Pair(S host, T var) { + this.first = host; + this.second = var; + } + + public T getSecond() { + return second; + } + + public S getFirst() { + return first; + } + + public S getHost() { + return first; + } + + public T getVar() { + return second; + } +} \ No newline at end of file diff --git a/Dream2/src/examples/java/dream/examples/chat_fifo/ChatGUI.java b/Dream2/src/examples/java/dream/examples/chat_fifo/ChatGUI.java new file mode 100644 index 0000000..699f17a --- /dev/null +++ b/Dream2/src/examples/java/dream/examples/chat_fifo/ChatGUI.java @@ -0,0 +1,255 @@ +package dream.examples.chat_fifo; + +import java.awt.Color; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.KeyEvent; +import java.awt.event.KeyListener; +import java.awt.event.WindowEvent; +import java.awt.event.WindowListener; +import java.io.File; +import java.net.MalformedURLException; +import java.util.ArrayList; +import java.util.List; + +import javax.swing.DefaultListCellRenderer; +import javax.swing.DefaultListModel; +import javax.swing.ImageIcon; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JList; +import javax.swing.JTextArea; +import javax.swing.JTextField; +import javax.swing.ListSelectionModel; +import javax.swing.SpringLayout; +import javax.swing.SwingUtilities; + +public class ChatGUI extends JFrame implements WindowListener { + + private static final long serialVersionUID = 4659984914364067514L; + private JTextArea msgs; + private JTextField sendText; + private JList statusList; + private DefaultListModel listModel; + + private Chat listener; + + public ChatGUI(String userName) { + this.addWindowListener(this); + initUI(userName); + + } + + public void setListener(Chat c) { + listener = c; + } + + public String getTypedText() { + return sendText.getText(); + } + + public void resetTypedText() { + sendText.setText(""); + } + + public void displayMessage(String text) { + if (msgs.getText().isEmpty()) + msgs.append(text); + else + msgs.append(System.lineSeparator() + text); + } + + public void displayMessage(ArrayList> allMessages) { + msgs.setText(""); + for (Pair pair : allMessages) { + displayMessage(pair.getFirst()); + } + } + + public void setOnline(List online) { + List offlineList = new ArrayList(); + for (int i = 0; i < listModel.size(); i++) { + if (!online.contains(listModel.get(i))) + offlineList.add(listModel.get(i)); + } + SwingUtilities.invokeLater(new Runnable() { + + @Override + public void run() { + listModel.clear(); + for (String e : online) { + listModel.addElement(e); + } + for (String e : offlineList) + listModel.addElement(e); + statusList.setSelectionInterval(0, online.size() - 1); + } + }); + + } + + public void removeOnline(String name) { + listModel.removeElement(name); + } + + private void sendText() { + listener.sendMessage(getTypedText()); + this.resetTypedText(); + } + + private void initUI(String userName) { + sendText = new JTextField(20); + sendText.addKeyListener(new KeyListener() { + + @Override + public void keyTyped(KeyEvent e) { + } + + @Override + public void keyReleased(KeyEvent e) { + } + + @Override + public void keyPressed(KeyEvent e) { + if (e.getKeyCode() == KeyEvent.VK_ENTER) + sendText(); + } + }); + JButton sendButton = new JButton("Send"); + sendButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent event) { + sendText(); + } + }); + msgs = new JTextArea(5, 27); + msgs.setEditable(false); + msgs.setMaximumSize(null); + + listModel = new DefaultListModel(); + statusList = new JList(listModel); + statusList.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); + statusList.setEnabled(false); + statusList.setLayoutOrientation(JList.HORIZONTAL_WRAP); + statusList.setVisibleRowCount(-1); + // statusList.setSelectionBackground(Color.GREEN); + // statusList.setSelectionForeground(Color.BLACK); + // statusList.setForeground(Color.BLACK); + // statusList.setBackground(Color.WHITE); + // statusList.setCellRenderer(new DefaultListCellRenderer()); + + statusList.setCellRenderer(new DefaultListCellRenderer() { + + private static final long serialVersionUID = 9019815674349211344L; + private JLabel label = new JLabel(); + private Color textSelectionColor = Color.BLACK; + private Color backgroundSelectionColor = Color.CYAN; + private Color textNonSelectionColor = Color.BLACK; + private Color backgroundNonSelectionColor = Color.WHITE; + + @Override + public java.awt.Component getListCellRendererComponent(javax.swing.JList list, Object value, int index, + boolean isSelected, boolean cellHasFocus) { + + String name = (String) value; + label.setText(name); + + if (isSelected) { + label.setBackground(backgroundSelectionColor); + label.setForeground(textSelectionColor); + label.setIcon(createImageIcon("status-online.png", "Online")); + } else { + label.setBackground(backgroundNonSelectionColor); + label.setForeground(textNonSelectionColor); + label.setIcon(createImageIcon("status-offline.png", "Offline")); + } + + return label; + }; + + }); + + SpringLayout layout = new SpringLayout(); + + // put messages on (5,5) + layout.putConstraint(SpringLayout.WEST, msgs, 5, SpringLayout.WEST, getContentPane()); + layout.putConstraint(SpringLayout.NORTH, msgs, 5, SpringLayout.NORTH, getContentPane()); + + // put textfield below messages + layout.putConstraint(SpringLayout.NORTH, sendText, 5, SpringLayout.SOUTH, msgs); + layout.putConstraint(SpringLayout.WEST, sendText, 5, SpringLayout.WEST, getContentPane()); + + // put button next to the textfield + layout.putConstraint(SpringLayout.NORTH, sendButton, 5, SpringLayout.SOUTH, msgs); + layout.putConstraint(SpringLayout.WEST, sendButton, 5, SpringLayout.EAST, sendText); + + // make the frame big enough to fit all in + layout.putConstraint(SpringLayout.EAST, getContentPane(), 10, SpringLayout.EAST, statusList); + layout.putConstraint(SpringLayout.SOUTH, getContentPane(), 10, SpringLayout.SOUTH, sendText); + + layout.putConstraint(SpringLayout.NORTH, statusList, 5, SpringLayout.NORTH, getContentPane()); + layout.putConstraint(SpringLayout.WEST, statusList, 15, SpringLayout.EAST, sendButton); + + getContentPane().setLayout(layout); + + getContentPane().add(msgs); + getContentPane().add(sendText); + getContentPane().add(sendButton); + getContentPane().add(statusList); + + setTitle("Chat - " + userName); + // setSize(300, 200); + setLocationRelativeTo(null); + setDefaultCloseOperation(EXIT_ON_CLOSE); + + pack(); + setVisible(true); + } + + /** Returns an ImageIcon, or null if the path was invalid. */ + protected ImageIcon createImageIcon(String path, String description) { + File img = new File("./src/resources/dream/examples/chat/" + path); + try { + java.net.URL imgURL = img.toURI().toURL(); + if (imgURL != null) { + return new ImageIcon(imgURL, description); + } else { + System.err.println("Couldn't find file: " + path); + return null; + } + } catch (MalformedURLException e) { + System.err.println("Couldn't find file: " + path); + return null; + } + } + + @Override + public void windowOpened(WindowEvent e) { + } + + @Override + public void windowIconified(WindowEvent e) { + } + + @Override + public void windowDeiconified(WindowEvent e) { + } + + @Override + public void windowDeactivated(WindowEvent e) { + } + + @Override + public void windowClosing(WindowEvent e) { + listener.sendMessage("/quit"); + } + + @Override + public void windowClosed(WindowEvent e) { + } + + @Override + public void windowActivated(WindowEvent e) { + } +} diff --git a/Dream2/src/examples/java/dream/examples/chat_fifo/ChatServer.java b/Dream2/src/examples/java/dream/examples/chat_fifo/ChatServer.java new file mode 100644 index 0000000..3b76ff9 --- /dev/null +++ b/Dream2/src/examples/java/dream/examples/chat_fifo/ChatServer.java @@ -0,0 +1,119 @@ +package dream.examples.chat_fifo; + +import java.math.BigInteger; +import java.security.SecureRandom; +import java.util.ArrayList; +import java.util.Set; + +import dream.client.DreamClient; +import dream.client.Var; +import dream.common.Consts; +import dream.locking.LockManagerLauncher; +import dream.server.ServerLauncher; +import javafx.util.Pair; + +public class ChatServer { + public static final String NAME = "ChatServer"; + + private boolean serverStarted = false; + private boolean lockManagerStarted = false; + + private Var> clients; + + private static SecureRandom r = new SecureRandom(); + + public static final String SERVER_PREFIX = "server_"; + public static final String SERVER_REGISTERED_CLIENTS = SERVER_PREFIX + "RegisteredClients"; + + public static void main(String[] args) { + new ChatServer().start(); + } + + private void initServer() { + Consts.hostName = NAME; + + clients = new Var>(SERVER_REGISTERED_CLIENTS, new ArrayList()); + detectNewSession(); + } + + /** + * Look for new clients every 5 seconds + */ + private void detectNewSession() { + Set vars = DreamClient.instance.listVariables(); + vars.stream().map(x -> new Pair(x.split("@")[1], x.split("@")[0])).// Pair(Host,Var) + filter(x -> !clients.get().contains(x.getValue() + "@" + x.getKey()) + && x.getValue().startsWith("chat_")) + .// + forEach(x -> createNewSessionFor(x.getKey(), x.getValue())); + try { + Thread.sleep(5000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + detectNewSession(); + + } + + /** + * Registers a new chat client with its name and the name of the variable it + * is sending messages on + * + * @param clientName + * the name of the client + * @param clientVar + * the name of the variable, must be of type String + */ + private void createNewSessionFor(String clientName, String clientVar) { + // add client as registered + clients.modify((old) -> old.add(clientVar + "@" + clientName)); + } + + /** + * @return random String hashed with SHA-256 + */ + public static String getRandom() { + return new BigInteger(130, r).toString(32); + } + + public void start() { + startServerIfNeeded(); + startLockManagerIfNeeded(); + + // Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).setLevel(Level.ALL); + initServer(); + + while (true) { + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + + private final void startServerIfNeeded() { + if (!serverStarted) { + ServerLauncher.start(); + serverStarted = true; + } + try { + Thread.sleep(500); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + private final void startLockManagerIfNeeded() { + if (!lockManagerStarted) { + LockManagerLauncher.start(); + lockManagerStarted = true; + } + try { + Thread.sleep(500); + } catch (final InterruptedException e) { + e.printStackTrace(); + } + } +} diff --git a/Dream2/src/examples/java/dream/examples/chat_fifo/VectorClock.java b/Dream2/src/examples/java/dream/examples/chat_fifo/VectorClock.java new file mode 100644 index 0000000..c5189cb --- /dev/null +++ b/Dream2/src/examples/java/dream/examples/chat_fifo/VectorClock.java @@ -0,0 +1,252 @@ +package dream.examples.chat_fifo; + +import java.io.Serializable; +import java.util.Arrays; +import java.util.Comparator; +import java.util.HashMap; + +/** + * Implements a VectorClock that records the time stamps of all send and receive + * events. It contains functions to compare and merge two VectorClocks. + * + * @author Frits de Nijs + * @author Peter Dijkshoorn + */ +public class VectorClock extends HashMap implements Serializable, Comparator { + // Unique Serial. + private static final long serialVersionUID = 6668164199894268488L; + + public VectorClock() { + + } + + public VectorClock(String initString) { + fromString(this, initString); + } + + /** + * Increases the component of pUnit by 1. + * + * @param pUnit + * - The ID of the vector element being increased. + */ + public void incrementClock(String pUnit) { + // If we have it in the vector, increment. + if (this.containsKey(pUnit)) { + this.put(pUnit, this.get(pUnit).intValue() + 1); + } + // Else, store with value 1 (starts at 0, +1). + else { + this.put(pUnit, 1); + } + } + + /** + * GUI operation, returns the IDs in some neat order. + * + * @return The IDs of the elements in the Clock. + */ + public String[] getOrderedIDs() { + String[] lResult = new String[this.size()]; + + lResult = this.keySet().toArray(lResult); + + Arrays.sort(lResult); + + return lResult; + } + + /** + * GUI operation, returns the values in some neat order. + * + * @return The Values of the elements in the Clock. + */ + public Integer[] getOrderedValues() { + Integer[] lResult = new Integer[this.size()]; + String[] lKeySet = this.getOrderedIDs(); + + int i = 0; + for (String lKey : lKeySet) { + lResult[i] = this.get(lKey); + i++; + } + + return lResult; + } + + @Override + public Integer get(Object key) { + Integer lResult = super.get(key); + + if (lResult == null) + lResult = 0; + + return lResult; + } + + @Override + public VectorClock clone() { + return (VectorClock) super.clone(); + } + + @Override + public String toString() { + return toString(this); + } + + /** + * VectorClock merging operation. Creates a new VectorClock with the maximum + * for each element in either clock. Used in Buffer and Process to + * manipulate clocks. + * + * @param pOne + * - First Clock being merged. + * @param pTwo + * - Second Clock being merged. + * + * @return A new VectorClock with the maximum for each element in either + * clock. + */ + public static VectorClock max(VectorClock pOne, VectorClock pTwo) { + // Create new Clock. + VectorClock lResult = new VectorClock(); + + // Go over all elements in clock One, put them in the new clock. + for (String lEntry : pOne.keySet()) { + lResult.put(lEntry, pOne.get(lEntry)); + } + + // Go over all elements in clock Two, + for (String lEntry : pTwo.keySet()) { + // Insert the Clock Two value if it is not present in One, or if it + // is higher. + if (!lResult.containsKey(lEntry) || lResult.get(lEntry) < pTwo.get(lEntry)) { + lResult.put(lEntry, pTwo.get(lEntry)); + } + } + + // Return the merged clock. + return lResult; + } + + public int compare(VectorClock v1, VectorClock v2) { + switch (order(v1, v2)) { + case GREATER: + return 1; + case SMALLER: + return -1; + default: + return 0; + } + } + + /** + * VectorClock compare operation. Returns one of four possible values + * indicating how clock one relates to clock two: + * + * VectorComparison.GREATER If One > Two. VectorComparison.EQUAL If One = + * Two. VectorComparison.SMALLER If One < Two. VectorComparison.SIMULTANEOUS + * If One <> Two. + * + * @param pOne + * - First Clock being compared. + * @param pTwo + * - Second Clock being compared. + * + * @return VectorComparison value indicating how One relates to Two. + */ + public static VectorComparison order(VectorClock pOne, VectorClock pTwo) { + // Initially we assume it is all possible things. + boolean lEqual = true; + boolean lGreater = true; + boolean lSmaller = true; + + // Go over all elements in Clock one. + for (String lEntry : pOne.keySet()) { + // Compare if also present in clock two. + if (pTwo.containsKey(lEntry)) { + // If there is a difference, it can never be equal. + // Greater / smaller depends on the difference. + if (pOne.get(lEntry) < pTwo.get(lEntry)) { + lEqual = false; + lGreater = false; + } + if (pOne.get(lEntry) > pTwo.get(lEntry)) { + lEqual = false; + lSmaller = false; + } + } + // Else assume zero (default value is 0). + else if (pOne.get(lEntry) != 0) { + lEqual = false; + lSmaller = false; + } + } + + // Go over all elements in Clock two. + for (String lEntry : pTwo.keySet()) { + // Only elements we have not found in One still need to be checked. + if (!pOne.containsKey(lEntry) && (pTwo.get(lEntry) != 0)) { + lEqual = false; + lGreater = false; + } + } + + // Return based on determined information. + if (lEqual) { + return VectorComparison.EQUAL; + } else if (lGreater && !lSmaller) { + return VectorComparison.GREATER; + } else if (lSmaller && !lGreater) { + return VectorComparison.SMALLER; + } else { + return VectorComparison.SIMULTANEOUS; + } + } + + public static String toString(VectorClock clock) { + StringBuilder sb = new StringBuilder(); + String[] lIDs = clock.getOrderedIDs(); + Integer[] lRequests = clock.getOrderedValues(); + sb.append("["); + for (int i = 0; i < lRequests.length; i++) { + sb.append(lIDs[i]).append("=").append(lRequests[i].toString()); + if (i + 1 < lRequests.length) { + sb.append(","); + } + } + sb.append("]"); + return sb.toString(); + } + + public static VectorClock fromString(String clockString) { + return fromString(new VectorClock(), clockString); + } + + private static VectorClock fromString(VectorClock vc, String clockString) { + if (clockString.startsWith("[") && clockString.endsWith("]")) { + clockString = clockString.substring(1, clockString.length() - 1); + String[] pairs = clockString.split(","); + for (int i = 0; i < pairs.length; i++) { + if (pairs[i].length() > 0) { + String[] temp = pairs[i].split("="); + String key = temp[0]; + int value = Integer.valueOf(temp[1]); + vc.put(key, value); + } + } + return vc; + } else + throw new IllegalArgumentException("String is not a VectorClock: " + clockString); + } +} + +/** + * Enumerates the four different outcomes of comparing two VectorClocks. + * + * @author Frits de Nijs + * @author Peter Dijkshoorn + */ +enum VectorComparison { + GREATER, EQUAL, SMALLER, SIMULTANEOUS; +} \ No newline at end of file From cc8c1d48026d012e7019a14fff4f6c4ef99eb065 Mon Sep 17 00:00:00 2001 From: Tobias Becker Date: Mon, 18 Apr 2016 13:54:21 +0200 Subject: [PATCH 032/161] updated normal chat for easier diff --- .../java/dream/examples/chat/Chat.java | 85 ++++++++++++++----- .../java/dream/examples/chat/ChatGUI.java | 15 ---- 2 files changed, 66 insertions(+), 34 deletions(-) diff --git a/Dream2/src/examples/java/dream/examples/chat/Chat.java b/Dream2/src/examples/java/dream/examples/chat/Chat.java index 82d5ce8..e4fd615 100644 --- a/Dream2/src/examples/java/dream/examples/chat/Chat.java +++ b/Dream2/src/examples/java/dream/examples/chat/Chat.java @@ -3,16 +3,16 @@ import java.awt.EventQueue; import java.util.ArrayList; import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; import java.util.stream.Collectors; -import dream.client.ChangeEventHandler; import dream.client.RemoteVar; import dream.client.Signal; import dream.client.Var; import dream.common.Consts; -import javafx.util.Pair; -public class Chat implements ChangeEventHandler { +public class Chat { private Var myMessages; private Var incoming; @@ -25,7 +25,7 @@ public Chat(String username) throws Exception { this.userName = username; Consts.hostName = userName; - + Logger.getGlobal().setLevel(Level.ALL); listening = new ArrayList<>(); // Establish new session with server RemoteVar> registeredClients = new RemoteVar>(ChatServer.NAME, @@ -42,7 +42,7 @@ public Chat(String username) throws Exception { setup(); } List names = n.stream().map(x -> x.split("@")[1]).collect(Collectors.toList()); - gui.setOnline(names); + setOnline(names); checkConnections(n); }); @@ -51,19 +51,40 @@ public Chat(String username) throws Exception { System.out.println("Setup: Waiting for Registration to Server ..."); } + private List lastOnline; + + private void setOnline(List online) { + if (lastOnline != null) { + for (String s : lastOnline) { + if (!online.contains(s)) { + String msg = s + " has left the Chat."; + gui.displayMessage(msg); + } + } + for (String s : online) { + if (!lastOnline.contains(s)) { + String msg = s + " has joined."; + gui.displayMessage(msg); + } + } + } + lastOnline = online; + gui.setOnline(online); + } + private void checkConnections(ArrayList n) { n.stream().map(x -> new Pair(x.split("@")[1], x.split("@")[0])).// Pair(Host,Var) - filter(x -> !listening.contains(x.getKey()) && x.getValue().startsWith("chat_")).// + filter(x -> !listening.contains(x.getHost()) && x.getVar().startsWith("chat_")).// forEach(x -> { - RemoteVar temp = new RemoteVar<>(x.getKey(), x.getValue()); - listening.add(x.getKey()); - new Signal("incoming" + x.getKey(), () -> { + RemoteVar temp = new RemoteVar<>(x.getHost(), x.getVar()); + listening.add(x.getHost()); + new Signal("incoming" + x.getHost(), () -> { if (temp.get() != null) - return x.getKey() + ": " + temp.get(); + return x.getHost() + ": " + temp.get(); else return ""; - }, temp).change().addHandler(this); - System.out.println("Adding listener to " + x.getKey()); + }, temp).change().addHandler((oldVal, newVal) -> incoming.set(newVal)); + System.out.println("Adding listener to " + x.getHost()); }); /* * if (!n.stream().map(x -> listening.contains(x)).reduce((a, b) -> a && @@ -99,18 +120,19 @@ private void setup() { gui.setListener(this); display.change().addHandler((oldValue, newValue) -> { - if (newValue != null) + if (newValue != null) { + Logger.getGlobal().fine("Received Message: " + incoming.get()); gui.displayMessage(newValue); + } }); } - @Override - public void handle(String oldVal, String newVal) { - incoming.set(newVal); - } - protected void sendMessage(String text) { + if (!text.startsWith("/")) { + gui.displayMessage("You: " + text); + } myMessages.set(text); + Logger.getGlobal().fine("Send Message: " + text); } public static void main(String[] args) { @@ -124,7 +146,6 @@ public static void main(String[] args) { @Override public void run() { try { - new Chat(args[0]); } catch (Exception e) { e.printStackTrace(); @@ -136,4 +157,30 @@ public void run() { e.printStackTrace(); } } +} + +class Pair { + private final S first; + private final T second; + + Pair(S host, T var) { + this.first = host; + this.second = var; + } + + public T getSecond() { + return second; + } + + public S getFirst() { + return first; + } + + public S getHost() { + return first; + } + + public T getVar() { + return second; + } } \ No newline at end of file diff --git a/Dream2/src/examples/java/dream/examples/chat/ChatGUI.java b/Dream2/src/examples/java/dream/examples/chat/ChatGUI.java index ee93073..ecaa1b7 100644 --- a/Dream2/src/examples/java/dream/examples/chat/ChatGUI.java +++ b/Dream2/src/examples/java/dream/examples/chat/ChatGUI.java @@ -60,20 +60,7 @@ public void displayMessage(String text) { msgs.append(System.lineSeparator() + text); } - private List lastOnline; - public void setOnline(List online) { - if (lastOnline != null) { - for (String s : lastOnline) { - if (!online.contains(s)) - displayMessage(s + " has left the Chat."); - } - for (String s : online) { - if (!lastOnline.contains(s)) - displayMessage(s + " has joined."); - } - } - lastOnline = online; List offlineList = new ArrayList(); for (int i = 0; i < listModel.size(); i++) { if (!online.contains(listModel.get(i))) @@ -101,8 +88,6 @@ public void removeOnline(String name) { private void sendText() { listener.sendMessage(getTypedText()); - if (!getTypedText().startsWith("/")) - this.displayMessage("You: " + getTypedText()); this.resetTypedText(); } From c31ad2fc01f9d2130e84d16adab22bb722251d7a Mon Sep 17 00:00:00 2001 From: Tobias Becker Date: Mon, 18 Apr 2016 14:10:46 +0200 Subject: [PATCH 033/161] added diff count from normal chat --- .../java/dream/examples/chat_fifo/DiffToNormal.txt | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 Dream2/src/examples/java/dream/examples/chat_fifo/DiffToNormal.txt diff --git a/Dream2/src/examples/java/dream/examples/chat_fifo/DiffToNormal.txt b/Dream2/src/examples/java/dream/examples/chat_fifo/DiffToNormal.txt new file mode 100644 index 0000000..4fb0d5f --- /dev/null +++ b/Dream2/src/examples/java/dream/examples/chat_fifo/DiffToNormal.txt @@ -0,0 +1,9 @@ +Chat: +19 lines added (2 for Logging) +5 lines changed + +ChatGUI: +6 lines added + +VectorClock: +148 lines added (252 lines with comments and empty lines) \ No newline at end of file From 5e90d08b1b8d03086268a270e5f37ab3e891d1c7 Mon Sep 17 00:00:00 2001 From: Tobias Becker Date: Fri, 22 Apr 2016 14:49:09 +0200 Subject: [PATCH 034/161] some refactoring --- .../java/dream/examples/chat/Chat.java | 97 +++++++------------ .../java/dream/examples/chat/ChatServer.java | 52 +++------- 2 files changed, 51 insertions(+), 98 deletions(-) diff --git a/Dream2/src/examples/java/dream/examples/chat/Chat.java b/Dream2/src/examples/java/dream/examples/chat/Chat.java index e4fd615..9400311 100644 --- a/Dream2/src/examples/java/dream/examples/chat/Chat.java +++ b/Dream2/src/examples/java/dream/examples/chat/Chat.java @@ -14,19 +14,22 @@ public class Chat { - private Var myMessages; - private Var incoming; - private String userName; + private final Var myMessages; + private final Var incoming; + private final String userName; private ChatGUI gui; - private List listening; - Signal> onlineList; + private final List listening = new ArrayList<>(); + private final Signal> onlineList; - public Chat(String username) throws Exception { - this.userName = username; + private List lastOnline; + private final static Logger logger = Logger.getGlobal(); + public Chat(String username) { + this.userName = username; Consts.hostName = userName; - Logger.getGlobal().setLevel(Level.ALL); - listening = new ArrayList<>(); + + logger.setLevel(Level.ALL); + // Establish new session with server RemoteVar> registeredClients = new RemoteVar>(ChatServer.NAME, ChatServer.SERVER_REGISTERED_CLIENTS); @@ -38,7 +41,7 @@ public Chat(String username) throws Exception { }, registeredClients); onlineList.change().addHandler((o, n) -> { if (n.contains("chat_message@" + username) && gui == null) { - System.out.println("Setup: Server Registration done!"); + logger.fine("Setup: Server Registration done!"); setup(); } List names = n.stream().map(x -> x.split("@")[1]).collect(Collectors.toList()); @@ -48,11 +51,9 @@ public Chat(String username) throws Exception { myMessages = new Var("chat_message", ""); incoming = new Var("incoming_messages", ""); - System.out.println("Setup: Waiting for Registration to Server ..."); + logger.fine("Setup: Waiting for Registration to Server ..."); } - private List lastOnline; - private void setOnline(List online) { if (lastOnline != null) { for (String s : lastOnline) { @@ -74,26 +75,18 @@ private void setOnline(List online) { private void checkConnections(ArrayList n) { n.stream().map(x -> new Pair(x.split("@")[1], x.split("@")[0])).// Pair(Host,Var) - filter(x -> !listening.contains(x.getHost()) && x.getVar().startsWith("chat_")).// + filter(x -> !listening.contains(x.getFirst()) && x.getSecond().startsWith("chat_")).// forEach(x -> { - RemoteVar temp = new RemoteVar<>(x.getHost(), x.getVar()); - listening.add(x.getHost()); - new Signal("incoming" + x.getHost(), () -> { + RemoteVar temp = new RemoteVar<>(x.getFirst(), x.getSecond()); + listening.add(x.getFirst()); + new Signal("incoming" + x.getFirst(), () -> { if (temp.get() != null) - return x.getHost() + ": " + temp.get(); + return x.getFirst() + ": " + temp.get(); else return ""; }, temp).change().addHandler((oldVal, newVal) -> incoming.set(newVal)); - System.out.println("Adding listener to " + x.getHost()); + logger.finer("Adding listener to " + x.getFirst()); }); - /* - * if (!n.stream().map(x -> listening.contains(x)).reduce((a, b) -> a && - * b).orElse(false)) { System.out.println("checking again in 5"); try { - * Thread.sleep(5000); } catch (InterruptedException e) { - * e.printStackTrace(); } - * - * checkConnections(onlineList.get()); } - */ } private void setup() { @@ -115,13 +108,13 @@ private void setup() { return incoming.get(); }, incoming); - System.out.println("Setup: Starting GUI"); + logger.fine("Setup: Starting GUI"); gui = new ChatGUI(userName); gui.setListener(this); display.change().addHandler((oldValue, newValue) -> { if (newValue != null) { - Logger.getGlobal().fine("Received Message: " + incoming.get()); + logger.fine("Received Message: " + incoming.get()); gui.displayMessage(newValue); } }); @@ -132,30 +125,20 @@ protected void sendMessage(String text) { gui.displayMessage("You: " + text); } myMessages.set(text); - Logger.getGlobal().fine("Send Message: " + text); + logger.fine("Send Message: " + text); } public static void main(String[] args) { - try { - if (args.length < 1) { - System.out.println("username missing"); - return; - } - EventQueue.invokeLater(new Runnable() { - - @Override - public void run() { - try { - new Chat(args[0]); - } catch (Exception e) { - e.printStackTrace(); - } - - } - }); - } catch (Exception e) { - e.printStackTrace(); + if (args.length < 1) { + logger.severe("username missing"); + return; } + EventQueue.invokeLater(new Runnable() { + @Override + public void run() { + new Chat(args[0]); + } + }); } } @@ -163,24 +146,16 @@ class Pair { private final S first; private final T second; - Pair(S host, T var) { - this.first = host; - this.second = var; - } - - public T getSecond() { - return second; + Pair(S a, T b) { + this.first = a; + this.second = b; } public S getFirst() { return first; } - public S getHost() { - return first; - } - - public T getVar() { + public T getSecond() { return second; } } \ No newline at end of file diff --git a/Dream2/src/examples/java/dream/examples/chat/ChatServer.java b/Dream2/src/examples/java/dream/examples/chat/ChatServer.java index b9674bf..bc7897a 100644 --- a/Dream2/src/examples/java/dream/examples/chat/ChatServer.java +++ b/Dream2/src/examples/java/dream/examples/chat/ChatServer.java @@ -1,16 +1,15 @@ package dream.examples.chat; -import java.math.BigInteger; -import java.security.SecureRandom; import java.util.ArrayList; import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; import dream.client.DreamClient; import dream.client.Var; import dream.common.Consts; import dream.locking.LockManagerLauncher; import dream.server.ServerLauncher; -import javafx.util.Pair; public class ChatServer { public static final String NAME = "ChatServer"; @@ -18,18 +17,21 @@ public class ChatServer { private boolean serverStarted = false; private boolean lockManagerStarted = false; - private Var> clients; + private final Var> clients; - private static SecureRandom r = new SecureRandom(); + private final Logger logger = Logger.getGlobal(); public static final String SERVER_PREFIX = "server_"; public static final String SERVER_REGISTERED_CLIENTS = SERVER_PREFIX + "RegisteredClients"; public static void main(String[] args) { - new ChatServer().start(); + new ChatServer(); } - private void initServer() { + public ChatServer() { + startServerIfNeeded(); + startLockManagerIfNeeded(); + Consts.hostName = NAME; clients = new Var>(SERVER_REGISTERED_CLIENTS, new ArrayList()); @@ -42,18 +44,17 @@ private void initServer() { private void detectNewSession() { Set vars = DreamClient.instance.listVariables(); vars.stream().map(x -> new Pair(x.split("@")[1], x.split("@")[0])).// Pair(Host,Var) - filter(x -> !clients.get().contains(x.getValue() + "@" + x.getKey()) - && x.getValue().startsWith("chat_")) + filter(x -> !clients.get().contains(x.getSecond() + "@" + x.getFirst()) + && x.getSecond().startsWith("chat_")) .// - forEach(x -> createNewSessionFor(x.getKey(), x.getValue())); + forEach(x -> createNewSessionFor(x.getFirst(), x.getSecond())); try { Thread.sleep(5000); } catch (InterruptedException e) { - e.printStackTrace(); + logger.log(Level.SEVERE, "Failed to sleep for 5 seconds", e); } detectNewSession(); - } /** @@ -70,29 +71,6 @@ private void createNewSessionFor(String clientName, String clientVar) { clients.modify((old) -> old.add(clientVar + "@" + clientName)); } - /** - * @return random String hashed with SHA-256 - */ - public static String getRandom() { - return new BigInteger(130, r).toString(32); - } - - public void start() { - startServerIfNeeded(); - startLockManagerIfNeeded(); - - // Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).setLevel(Level.ALL); - initServer(); - - while (true) { - try { - Thread.sleep(1000); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - } - private final void startServerIfNeeded() { if (!serverStarted) { ServerLauncher.start(); @@ -101,7 +79,7 @@ private final void startServerIfNeeded() { try { Thread.sleep(500); } catch (InterruptedException e) { - e.printStackTrace(); + logger.log(Level.SEVERE, "Failed to wait for Server starting", e); } } @@ -113,7 +91,7 @@ private final void startLockManagerIfNeeded() { try { Thread.sleep(500); } catch (final InterruptedException e) { - e.printStackTrace(); + logger.log(Level.SEVERE, "Failed to wait for LockManager starting", e); } } } From c63d474db4ec9e5195c784e53db916336df9058c Mon Sep 17 00:00:00 2001 From: Tobias Becker Date: Wed, 27 Apr 2016 14:08:20 +0200 Subject: [PATCH 035/161] changed topology and added chat rooms --- .../java/dream/examples/chat/Chat.java | 178 ++++++++++++------ .../java/dream/examples/chat/ChatGUI.java | 51 +++-- .../java/dream/examples/chat/ChatServer.java | 107 ++++++++++- 3 files changed, 262 insertions(+), 74 deletions(-) diff --git a/Dream2/src/examples/java/dream/examples/chat/Chat.java b/Dream2/src/examples/java/dream/examples/chat/Chat.java index 9400311..f5d00a7 100644 --- a/Dream2/src/examples/java/dream/examples/chat/Chat.java +++ b/Dream2/src/examples/java/dream/examples/chat/Chat.java @@ -2,11 +2,14 @@ import java.awt.EventQueue; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; import java.util.stream.Collectors; +import dream.client.DreamClient; import dream.client.RemoteVar; import dream.client.Signal; import dream.client.Var; @@ -14,20 +17,25 @@ public class Chat { - private final Var myMessages; - private final Var incoming; private final String userName; private ChatGUI gui; - private final List listening = new ArrayList<>(); - private final Signal> onlineList; + private final Signal> onlineList; private List lastOnline; - private final static Logger logger = Logger.getGlobal(); + + private Var toServer; + + private Map> rooms = new HashMap<>(); + private Map roomNames = new HashMap<>(); + + private final Logger logger; public Chat(String username) { this.userName = username; Consts.hostName = userName; + logger = Logger.getLogger("Chat_" + userName); + logger.addHandler(Logger.getGlobal().getHandlers()[0]); logger.setLevel(Level.ALL); // Establish new session with server @@ -40,17 +48,14 @@ public Chat(String username) { return registeredClients.get(); }, registeredClients); onlineList.change().addHandler((o, n) -> { - if (n.contains("chat_message@" + username) && gui == null) { - logger.fine("Setup: Server Registration done!"); + if (n.contains("toServerVar@" + username) && gui == null) setup(); - } + List names = n.stream().map(x -> x.split("@")[1]).collect(Collectors.toList()); setOnline(names); - checkConnections(n); }); - myMessages = new Var("chat_message", ""); - incoming = new Var("incoming_messages", ""); + toServer = new Var("toServerVar", ""); logger.fine("Setup: Waiting for Registration to Server ..."); } @@ -59,13 +64,13 @@ private void setOnline(List online) { for (String s : lastOnline) { if (!online.contains(s)) { String msg = s + " has left the Chat."; - gui.displayMessage(msg); + // gui.displayMessage(0, msg); } } for (String s : online) { if (!lastOnline.contains(s)) { String msg = s + " has joined."; - gui.displayMessage(msg); + // gui.displayMessage(0, msg); } } } @@ -73,64 +78,127 @@ private void setOnline(List online) { gui.setOnline(online); } - private void checkConnections(ArrayList n) { - n.stream().map(x -> new Pair(x.split("@")[1], x.split("@")[0])).// Pair(Host,Var) - filter(x -> !listening.contains(x.getFirst()) && x.getSecond().startsWith("chat_")).// - forEach(x -> { - RemoteVar temp = new RemoteVar<>(x.getFirst(), x.getSecond()); - listening.add(x.getFirst()); - new Signal("incoming" + x.getFirst(), () -> { - if (temp.get() != null) - return x.getFirst() + ": " + temp.get(); - else - return ""; - }, temp).change().addHandler((oldVal, newVal) -> incoming.set(newVal)); - logger.finer("Adding listener to " + x.getFirst()); - }); - } - private void setup() { - Signal display = new Signal("display", () -> { - if (incoming.get().startsWith("/")) { - String[] temp = incoming.get().split(" ", 2); - String command = temp[0].substring(1, temp[0].length()); - String rest = temp.length > 1 ? temp[1] : ""; - // QUIT - for now only used to update the registeredClients list - // (aka who's online) - if (command.equalsIgnoreCase("W")) { - String[] temp1 = rest.split(" ", 2); - String sender = temp1[0]; - String message = temp1.length > 1 ? temp1[1] : ""; - return sender + " whispered: " + message; - } else - return null; - } else - return incoming.get(); - }, incoming); + logger.fine("Setup: Var successfully registered to Server"); + // Var for messages from server + String serverVar = ChatServer.getRandom(); + toServer.set(serverVar); + // while (!DreamClient.instance.listVariables().contains(serverVar + "@" + // + ChatServer.NAME)) { + logger.fine(DreamClient.instance.listVariables().toString()); + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + // } + RemoteVar remote = new RemoteVar(ChatServer.NAME, serverVar); + Signal fromServer = new Signal<>("fromServer", () -> { + if (remote.get() != null) + return remote.get(); + else + return ""; + }, remote); + fromServer.change().addHandler((oldValue, newValue) -> receivedServerMessage(newValue)); logger.fine("Setup: Starting GUI"); gui = new ChatGUI(userName); gui.setListener(this); - display.change().addHandler((oldValue, newValue) -> { - if (newValue != null) { - logger.fine("Received Message: " + incoming.get()); - gui.displayMessage(newValue); + // main room: + // newRoom("Main", "*"); + } + + private void receivedChatMessage(int roomNumber, String sender, String text) { + gui.displayMessage(roomNumber, sender + ": " + text); + } + + private void receivedServerMessage(String message) { + logger.fine("Received message from server: " + message); + String[] temp = message.split(" ", 2); + String command = temp[0]; + String rest = temp.length > 1 ? temp[1] : ""; + if (command.equalsIgnoreCase("room")) { + // room ... + String[] t = rest.split(" ", 2); + String roomName = t[0]; + String otherClients = t[1]; + logger.finer("Server requested a Var for room " + roomName + " with " + otherClients); + + int no = gui.newChat(roomName); + String roomVar = "room" + no; + Var room = new Var(roomVar, ""); + rooms.put(no, room); + roomNames.put(roomName, no); + toServer.set("roomVar " + roomName + " " + roomVar); + } else if (command.equalsIgnoreCase("roomVar")) { + // roomVar = = + String[] t = rest.split(" ", 2); + String roomName = t[0]; + String[] pairs = t[1].split(" "); + for (String p : pairs) { + String[] t2 = p.split("=", 2); + String clientName = t2[0]; + String clientVar = t2[1]; + int roomNumber = roomNames.get(roomName); + createConnection(roomNumber, roomName, clientName, clientVar); } - }); + } + } + + private void createConnection(int roomNumber, String roomName, String clientName, String clientVar) { + if (clientName.equals(userName)) + return; + RemoteVar r = new RemoteVar<>(clientName, clientVar); + Signal s = new Signal<>(roomName + "_" + clientName, () -> { + if (r.get() != null) + return r.get(); + else + return ""; + }, r); + s.change().addHandler((oldValue, newValue) -> receivedChatMessage(roomNumber, clientName, newValue)); } protected void sendMessage(String text) { if (!text.startsWith("/")) { - gui.displayMessage("You: " + text); + // normal message + int room = gui.getSelectedChat(); + gui.displayMessage(room, "You: " + text); + rooms.get(room).set(text); + logger.fine("Send Message to Room" + room + ": " + text); + } else { + // message to server + processCommand(text.substring(1, text.length())); + } + } + + private void processCommand(String text) { + String[] temp = text.split(" ", 2); + String command = temp[0]; + String rest = temp.length > 1 ? temp[1] : ""; + if (command.equalsIgnoreCase("room")) { + String[] temp1 = rest.split(" ", 2); + String name = temp1[0]; + newRoom(name, temp1[1]); } - myMessages.set(text); - logger.fine("Send Message: " + text); + logger.fine("Processed Command: " + text); + } + + private void newRoom(String name, String recipients) { + int no = gui.newChat(name); + String roomVar = "room" + no; + Var room = new Var(roomVar, ""); + rooms.put(no, room); + roomNames.put(name, no); + logger.fine("Room: Creating new Room(" + no + ") to " + recipients); + // room command: room ... + logger.finer("Room: Sending to Server: " + "room " + name + " " + roomVar + " " + recipients); + toServer.set("room " + name + " " + roomVar + " " + recipients); } public static void main(String[] args) { if (args.length < 1) { - logger.severe("username missing"); + Logger.getGlobal().severe("username missing"); return; } EventQueue.invokeLater(new Runnable() { diff --git a/Dream2/src/examples/java/dream/examples/chat/ChatGUI.java b/Dream2/src/examples/java/dream/examples/chat/ChatGUI.java index ecaa1b7..71009f6 100644 --- a/Dream2/src/examples/java/dream/examples/chat/ChatGUI.java +++ b/Dream2/src/examples/java/dream/examples/chat/ChatGUI.java @@ -1,6 +1,8 @@ package dream.examples.chat; import java.awt.Color; +import java.awt.Component; +import java.awt.Dimension; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; @@ -19,6 +21,7 @@ import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JList; +import javax.swing.JTabbedPane; import javax.swing.JTextArea; import javax.swing.JTextField; import javax.swing.ListSelectionModel; @@ -28,7 +31,8 @@ public class ChatGUI extends JFrame implements WindowListener { private static final long serialVersionUID = 4659984914364067514L; - private JTextArea msgs; + private JTabbedPane jtp; + private List msgs; private JTextField sendText; private JList statusList; private DefaultListModel listModel; @@ -38,7 +42,6 @@ public class ChatGUI extends JFrame implements WindowListener { public ChatGUI(String userName) { this.addWindowListener(this); initUI(userName); - } public void setListener(Chat c) { @@ -53,11 +56,28 @@ public void resetTypedText() { sendText.setText(""); } - public void displayMessage(String text) { - if (msgs.getText().isEmpty()) - msgs.append(text); + public int getSelectedChat() { + return jtp.getSelectedIndex(); + } + + public int newChat(String name) { + msgs.add(new JTextArea(5, 27)); + int r = msgs.size() - 1; + msgs.get(r).setEditable(false); + jtp.add(name, msgs.get(r)); + pack(); + return r; + } + + public void closeChat(int index) { + Component t = jtp.getComponentAt(index); + } + + public void displayMessage(int room, String text) { + if (msgs.get(room).getText().isEmpty()) + msgs.get(room).append(text); else - msgs.append(System.lineSeparator() + text); + msgs.get(room).append(System.lineSeparator() + text); } public void setOnline(List online) { @@ -116,9 +136,10 @@ public void actionPerformed(ActionEvent event) { sendText(); } }); - msgs = new JTextArea(5, 27); - msgs.setEditable(false); - msgs.setMaximumSize(null); + jtp = new JTabbedPane(JTabbedPane.TOP); + jtp.setPreferredSize(new Dimension(400, 100)); + msgs = new ArrayList<>(); + // newChat("Main"); listModel = new DefaultListModel(); statusList = new JList(listModel); @@ -166,15 +187,15 @@ public java.awt.Component getListCellRendererComponent(javax.swing.JList list SpringLayout layout = new SpringLayout(); // put messages on (5,5) - layout.putConstraint(SpringLayout.WEST, msgs, 5, SpringLayout.WEST, getContentPane()); - layout.putConstraint(SpringLayout.NORTH, msgs, 5, SpringLayout.NORTH, getContentPane()); + layout.putConstraint(SpringLayout.WEST, jtp, 5, SpringLayout.WEST, getContentPane()); + layout.putConstraint(SpringLayout.NORTH, jtp, 5, SpringLayout.NORTH, getContentPane()); // put textfield below messages - layout.putConstraint(SpringLayout.NORTH, sendText, 5, SpringLayout.SOUTH, msgs); + layout.putConstraint(SpringLayout.NORTH, sendText, 5, SpringLayout.SOUTH, jtp); layout.putConstraint(SpringLayout.WEST, sendText, 5, SpringLayout.WEST, getContentPane()); // put button next to the textfield - layout.putConstraint(SpringLayout.NORTH, sendButton, 5, SpringLayout.SOUTH, msgs); + layout.putConstraint(SpringLayout.NORTH, sendButton, 5, SpringLayout.SOUTH, jtp); layout.putConstraint(SpringLayout.WEST, sendButton, 5, SpringLayout.EAST, sendText); // make the frame big enough to fit all in @@ -182,11 +203,11 @@ public java.awt.Component getListCellRendererComponent(javax.swing.JList list layout.putConstraint(SpringLayout.SOUTH, getContentPane(), 10, SpringLayout.SOUTH, sendText); layout.putConstraint(SpringLayout.NORTH, statusList, 5, SpringLayout.NORTH, getContentPane()); - layout.putConstraint(SpringLayout.WEST, statusList, 15, SpringLayout.EAST, sendButton); + layout.putConstraint(SpringLayout.WEST, statusList, 15, SpringLayout.EAST, jtp); getContentPane().setLayout(layout); - getContentPane().add(msgs); + getContentPane().add(jtp); getContentPane().add(sendText); getContentPane().add(sendButton); getContentPane().add(statusList); diff --git a/Dream2/src/examples/java/dream/examples/chat/ChatServer.java b/Dream2/src/examples/java/dream/examples/chat/ChatServer.java index bc7897a..5bf0798 100644 --- a/Dream2/src/examples/java/dream/examples/chat/ChatServer.java +++ b/Dream2/src/examples/java/dream/examples/chat/ChatServer.java @@ -1,11 +1,18 @@ package dream.examples.chat; +import java.math.BigInteger; +import java.security.SecureRandom; import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; import dream.client.DreamClient; +import dream.client.RemoteVar; +import dream.client.Signal; import dream.client.Var; import dream.common.Consts; import dream.locking.LockManagerLauncher; @@ -17,9 +24,12 @@ public class ChatServer { private boolean serverStarted = false; private boolean lockManagerStarted = false; - private final Var> clients; + private final static SecureRandom r = new SecureRandom(); - private final Logger logger = Logger.getGlobal(); + private final Var> clients; + private final HashMap> clientVars; + private final HashMap> rooms; + private final Logger logger = Logger.getLogger("ChatServer"); public static final String SERVER_PREFIX = "server_"; public static final String SERVER_REGISTERED_CLIENTS = SERVER_PREFIX + "RegisteredClients"; @@ -32,8 +42,11 @@ public ChatServer() { startServerIfNeeded(); startLockManagerIfNeeded(); + logger.setLevel(Level.ALL); + logger.addHandler(Logger.getGlobal().getHandlers()[0]); Consts.hostName = NAME; - + clientVars = new HashMap<>(); + rooms = new HashMap<>(); clients = new Var>(SERVER_REGISTERED_CLIENTS, new ArrayList()); detectNewSession(); } @@ -45,7 +58,7 @@ private void detectNewSession() { Set vars = DreamClient.instance.listVariables(); vars.stream().map(x -> new Pair(x.split("@")[1], x.split("@")[0])).// Pair(Host,Var) filter(x -> !clients.get().contains(x.getSecond() + "@" + x.getFirst()) - && x.getSecond().startsWith("chat_")) + && x.getSecond().equalsIgnoreCase("toServerVar")) .// forEach(x -> createNewSessionFor(x.getFirst(), x.getSecond())); try { @@ -67,8 +80,94 @@ private void detectNewSession() { * the name of the variable, must be of type String */ private void createNewSessionFor(String clientName, String clientVar) { + logger.fine("Creating new Session for " + clientName + " with Var " + clientVar); + + // add listener to messages from this client + RemoteVar var = new RemoteVar(clientName, clientVar); + Signal sig = new Signal<>(SERVER_PREFIX + "receive_" + clientName, () -> { + if (var.get() != null) + return var.get(); + else + return ""; + }, var); + sig.change().addHandler((oldValue, newValue) -> receivedMessage(clientName, newValue)); + // add client as registered clients.modify((old) -> old.add(clientVar + "@" + clientName)); + + // now wait for first message from client + logger.fine("Setup: Waiting for first message from " + clientName); + } + + private void receivedMessage(String clientName, String message) { + logger.fine("Received client message from " + clientName + ": " + message); + if (!clientVars.containsKey(clientName)) { + // first message from client + // setup Var for messages to this client + clientVars.put(clientName, new Var(message, "")); + logger.fine("Setup: new Var for messages to " + clientName); + } else { + String[] temp = message.split(" ", 2); + String command = temp[0]; + String rest = temp.length > 1 ? temp[1] : ""; + + if (command.equalsIgnoreCase("room")) { + // room ... + logger.fine("Room: Received command to create new room (" + message + ")"); + String[] t = rest.split(" ", 3); + String roomName = t[0]; + String otherClients = t[2]; + // Map: clientName -> clientRoomVar + HashMap roomVars = new HashMap<>(); + roomVars.put(clientName, t[1]); + // send message to every recipient to send a Var for that room + if (otherClients.equals("*")) { + for (String c : clients.get()) { + // set Var for c as "not sent" + roomVars.put(c, null); + // and ask c for his Var + logger.finer("Room: Sending Var-Request for " + roomName + " to " + c); + clientVars.get(c).set("room " + roomName + " " + otherClients.replace(c, clientName)); + } + } else { + for (String c : otherClients.split(" ")) { + // set Var for c as "not sent" + roomVars.put(c, null); + // and ask c for his Var + logger.finer("Room: Sending Var-Request for " + roomName + " to " + c); + clientVars.get(c).set("room " + roomName + " " + otherClients.replace(c, clientName)); + } + } + rooms.put(roomName, roomVars); + } else if (command.equalsIgnoreCase("roomVar")) { + // roomVar + String[] t = rest.split(" ", 2); + String roomName = t[0]; + String varName = t[1]; + logger.fine("Room: Received a Var (" + varName + ") from " + clientName + " for room " + roomName); + Map roomVars = rooms.get(roomName); + roomVars.put(clientName, varName); + + // check if every room member has sent a Var + if (!roomVars.values().contains(null)) { + String varString = ""; + for (Entry e : roomVars.entrySet()) { + varString += e.getKey() + "=" + e.getValue() + " "; + } + for (String client : roomVars.keySet()) { + clientVars.get(client).set("roomVar " + roomName + " " + varString); + } + logger.fine("Room: Finished setting up room " + roomName); + } + } + } + } + + /** + * @return random String hashed with SHA-256 + */ + public static String getRandom() { + return new BigInteger(130, r).toString(32); } private final void startServerIfNeeded() { From 5ea7fa7ab9cfda597ce3288be866a3f2b78b38f2 Mon Sep 17 00:00:00 2001 From: Tobias Becker Date: Sat, 30 Apr 2016 09:16:38 +0200 Subject: [PATCH 036/161] possiblity to set chat window position --- .../java/dream/examples/chat/Chat.java | 30 ++++++++++++------ .../java/dream/examples/chat/ChatGUI.java | 31 +++++++------------ .../java/dream/examples/chat/ChatServer.java | 4 +-- 3 files changed, 34 insertions(+), 31 deletions(-) diff --git a/Dream2/src/examples/java/dream/examples/chat/Chat.java b/Dream2/src/examples/java/dream/examples/chat/Chat.java index f5d00a7..d25467d 100644 --- a/Dream2/src/examples/java/dream/examples/chat/Chat.java +++ b/Dream2/src/examples/java/dream/examples/chat/Chat.java @@ -29,11 +29,14 @@ public class Chat { private Map roomNames = new HashMap<>(); private final Logger logger; + private int posX; + private int posY; - public Chat(String username) { + public Chat(String username, int window_x, int window_y) { this.userName = username; Consts.hostName = userName; - + this.posX = window_x; + this.posY = window_y; logger = Logger.getLogger("Chat_" + userName); logger.addHandler(Logger.getGlobal().getHandlers()[0]); logger.setLevel(Level.ALL); @@ -102,7 +105,7 @@ private void setup() { fromServer.change().addHandler((oldValue, newValue) -> receivedServerMessage(newValue)); logger.fine("Setup: Starting GUI"); - gui = new ChatGUI(userName); + gui = new ChatGUI(userName, posX, posY); gui.setListener(this); // main room: @@ -201,12 +204,21 @@ public static void main(String[] args) { Logger.getGlobal().severe("username missing"); return; } - EventQueue.invokeLater(new Runnable() { - @Override - public void run() { - new Chat(args[0]); - } - }); + int x, y; + for (String s : args) { + System.out.print(s + ","); + } + System.out.println(); + if (args.length < 3) + y = -1; + else + y = Integer.parseInt(args[2]); + + if (args.length < 2) + x = -1; + else + x = Integer.parseInt(args[1]); + EventQueue.invokeLater(() -> new Chat(args[0], x, y)); } } diff --git a/Dream2/src/examples/java/dream/examples/chat/ChatGUI.java b/Dream2/src/examples/java/dream/examples/chat/ChatGUI.java index 71009f6..88d6f98 100644 --- a/Dream2/src/examples/java/dream/examples/chat/ChatGUI.java +++ b/Dream2/src/examples/java/dream/examples/chat/ChatGUI.java @@ -3,8 +3,6 @@ import java.awt.Color; import java.awt.Component; import java.awt.Dimension; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.awt.event.WindowEvent; @@ -39,9 +37,11 @@ public class ChatGUI extends JFrame implements WindowListener { private Chat listener; - public ChatGUI(String userName) { + public ChatGUI(String userName, int posX, int posY) { this.addWindowListener(this); initUI(userName); + if (posX >= 0 && posY >= 0) + this.setLocation(posX, posY); } public void setListener(Chat c) { @@ -86,18 +86,14 @@ public void setOnline(List online) { if (!online.contains(listModel.get(i))) offlineList.add(listModel.get(i)); } - SwingUtilities.invokeLater(new Runnable() { - - @Override - public void run() { - listModel.clear(); - for (String e : online) { - listModel.addElement(e); - } - for (String e : offlineList) - listModel.addElement(e); - statusList.setSelectionInterval(0, online.size() - 1); + SwingUtilities.invokeLater(() -> { + listModel.clear(); + for (String e : online) { + listModel.addElement(e); } + for (String e : offlineList) + listModel.addElement(e); + statusList.setSelectionInterval(0, online.size() - 1); }); } @@ -130,12 +126,7 @@ public void keyPressed(KeyEvent e) { } }); JButton sendButton = new JButton("Send"); - sendButton.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent event) { - sendText(); - } - }); + sendButton.addActionListener((e) -> sendText()); jtp = new JTabbedPane(JTabbedPane.TOP); jtp.setPreferredSize(new Dimension(400, 100)); msgs = new ArrayList<>(); diff --git a/Dream2/src/examples/java/dream/examples/chat/ChatServer.java b/Dream2/src/examples/java/dream/examples/chat/ChatServer.java index 5bf0798..230ce70 100644 --- a/Dream2/src/examples/java/dream/examples/chat/ChatServer.java +++ b/Dream2/src/examples/java/dream/examples/chat/ChatServer.java @@ -62,9 +62,9 @@ private void detectNewSession() { .// forEach(x -> createNewSessionFor(x.getFirst(), x.getSecond())); try { - Thread.sleep(5000); + Thread.sleep(500); } catch (InterruptedException e) { - logger.log(Level.SEVERE, "Failed to sleep for 5 seconds", e); + logger.log(Level.SEVERE, "Failed to sleep for 0.5 seconds", e); } detectNewSession(); From 7f062773163cca6bfd2a6de84c607487f0550b95 Mon Sep 17 00:00:00 2001 From: Tobias Becker Date: Sat, 30 Apr 2016 09:16:55 +0200 Subject: [PATCH 037/161] added simple helper class for testing --- .../dream/examples/chat/util/Starter.java | 119 ++++++++++++++++++ 1 file changed, 119 insertions(+) create mode 100644 Dream2/src/examples/java/dream/examples/chat/util/Starter.java diff --git a/Dream2/src/examples/java/dream/examples/chat/util/Starter.java b/Dream2/src/examples/java/dream/examples/chat/util/Starter.java new file mode 100644 index 0000000..702f72b --- /dev/null +++ b/Dream2/src/examples/java/dream/examples/chat/util/Starter.java @@ -0,0 +1,119 @@ +package dream.examples.chat.util; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.concurrent.TimeUnit; + +import dream.examples.chat.Chat; +import dream.examples.chat.ChatServer; + +/** + * Convenience class to start ChatServer and x Chats (x = CHAT_COUNT), each in + * its own VM. + * + * To exit all processes just close one chat window. + */ +public class Starter { + + public static final int CHAT_COUNT = 4; + + private static ArrayList processes = new ArrayList(); + + public static void main(String[] args) { + new Starter().start(); + } + + private Thread serverThread; + + private static final String[] names = { "Alice", "Bob", "Chris", "David", "Eve", "Fred", "Georg", "Hans", "Igor" }; + int xStep = 450; + int yStep = 175; + + private void start() { + Runtime.getRuntime().addShutdownHook(new Thread(() -> onExit())); + serverThread = new Thread(() -> ChatServer.main(null)); + serverThread.start(); + sleep(1500); + int x = 0; + int y = 0; + for (int i = 0; i < CHAT_COUNT; i++) { + startSecondJVM(Chat.class, getName(i), Integer.toString(x), Integer.toString(y)); + x += xStep; + if (x >= 3 * xStep) { + x = 0; + y += yStep; + } + } + // sleep infinite time + sleep(-1); + } + + private String getName(int i) { + if (i < names.length) + return names[i]; + else + return names[i % names.length] + "" + i; + } + + @Override + protected void finalize() throws Throwable { + onExit(); + super.finalize(); + } + + private void sleep(int time) { + do { + try { + Thread.sleep(time == -1 ? 1000 : time); + checkExit(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } while (time == -1); + + } + + private void checkExit() { + for (Process p : processes) { + if (!p.isAlive()) { + System.out.println("One window closed ... exiting!"); + System.exit(0); + } + } + } + + private void onExit() { + System.out.println("exit"); + for (Process p : processes) { + p.destroyForcibly(); + System.out.println("Destroying " + p.toString()); + } + } + + public void startSecondJVM(Class c, String... args) { + System.out.println("Starting " + c.getName() + " ..."); + String separator = System.getProperty("file.separator"); + String classpath = System.getProperty("java.class.path"); + String path = System.getProperty("java.home") + separator + "bin" + separator + "java"; + String[] arguments = new String[args.length + 4]; + arguments[0] = path; + arguments[1] = "-cp"; + arguments[2] = classpath; + arguments[3] = c.getName(); + for (int i = 0; i < args.length; i++) { + arguments[i + 4] = args[i]; + } + ProcessBuilder processBuilder = new ProcessBuilder(arguments).inheritIO(); + Process process; + try { + process = processBuilder.start(); + processes.add(process); + process.waitFor(1, TimeUnit.SECONDS); + } catch (IOException e) { + e.printStackTrace(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + System.out.println(c.getName() + " started!"); + } +} From 01521ad09e268ff20535c8c3e7915201ddc48762 Mon Sep 17 00:00:00 2001 From: Tobias Becker Date: Mon, 2 May 2016 14:28:42 +0200 Subject: [PATCH 038/161] refactoring for new fifo --- .../dream/examples/chat/{ => core}/Chat.java | 53 ++-- .../examples/chat/{ => core}/ChatGUI.java | 6 +- .../examples/chat/{ => core}/ChatServer.java | 26 +- .../{chat_fifo => chat/fifo}/VectorClock.java | 2 +- .../dream/examples/chat/package-info.java | 6 +- .../dream/examples/chat/util/Starter.java | 4 +- .../java/dream/examples/chat_fifo/Chat.java | 205 -------------- .../dream/examples/chat_fifo/ChatGUI.java | 255 ------------------ .../dream/examples/chat_fifo/ChatServer.java | 119 -------- .../dream/examples/chat_fifo/DiffToNormal.txt | 9 - 10 files changed, 55 insertions(+), 630 deletions(-) rename Dream2/src/examples/java/dream/examples/chat/{ => core}/Chat.java (84%) rename Dream2/src/examples/java/dream/examples/chat/{ => core}/ChatGUI.java (98%) rename Dream2/src/examples/java/dream/examples/chat/{ => core}/ChatServer.java (89%) rename Dream2/src/examples/java/dream/examples/{chat_fifo => chat/fifo}/VectorClock.java (99%) delete mode 100644 Dream2/src/examples/java/dream/examples/chat_fifo/Chat.java delete mode 100644 Dream2/src/examples/java/dream/examples/chat_fifo/ChatGUI.java delete mode 100644 Dream2/src/examples/java/dream/examples/chat_fifo/ChatServer.java delete mode 100644 Dream2/src/examples/java/dream/examples/chat_fifo/DiffToNormal.txt diff --git a/Dream2/src/examples/java/dream/examples/chat/Chat.java b/Dream2/src/examples/java/dream/examples/chat/core/Chat.java similarity index 84% rename from Dream2/src/examples/java/dream/examples/chat/Chat.java rename to Dream2/src/examples/java/dream/examples/chat/core/Chat.java index d25467d..3b10c52 100644 --- a/Dream2/src/examples/java/dream/examples/chat/Chat.java +++ b/Dream2/src/examples/java/dream/examples/chat/core/Chat.java @@ -1,4 +1,4 @@ -package dream.examples.chat; +package dream.examples.chat.core; import java.awt.EventQueue; import java.util.ArrayList; @@ -85,7 +85,7 @@ private void setup() { logger.fine("Setup: Var successfully registered to Server"); // Var for messages from server String serverVar = ChatServer.getRandom(); - toServer.set(serverVar); + sendServerMessage(serverVar); // while (!DreamClient.instance.listVariables().contains(serverVar + "@" // + ChatServer.NAME)) { logger.fine(DreamClient.instance.listVariables().toString()); @@ -112,11 +112,11 @@ private void setup() { // newRoom("Main", "*"); } - private void receivedChatMessage(int roomNumber, String sender, String text) { - gui.displayMessage(roomNumber, sender + ": " + text); + protected void receivedChatMessage(int roomNumber, String sender, String message) { + gui.displayMessage(roomNumber, sender + ": " + message); } - private void receivedServerMessage(String message) { + protected void receivedServerMessage(String message) { logger.fine("Received message from server: " + message); String[] temp = message.split(" ", 2); String command = temp[0]; @@ -128,12 +128,8 @@ private void receivedServerMessage(String message) { String otherClients = t[1]; logger.finer("Server requested a Var for room " + roomName + " with " + otherClients); - int no = gui.newChat(roomName); - String roomVar = "room" + no; - Var room = new Var(roomVar, ""); - rooms.put(no, room); - roomNames.put(roomName, no); - toServer.set("roomVar " + roomName + " " + roomVar); + String roomVar = newRoom(roomName); + sendServerMessage("roomVar " + roomName + " " + roomVar); } else if (command.equalsIgnoreCase("roomVar")) { // roomVar = = String[] t = rest.split(" ", 2); @@ -149,6 +145,24 @@ private void receivedServerMessage(String message) { } } + protected void sendChatMessage(int roomNumber, String message) { + rooms.get(roomNumber).set(message); + } + + protected void sendServerMessage(String message) { + toServer.set(message); + } + + protected String newRoom(String roomName) { + int roomNumber = gui.newChat(roomName); + String roomVar = "room" + roomNumber; + Var room = new Var(roomVar, ""); + rooms.put(roomNumber, room); + roomNames.put(roomName, roomNumber); + logger.fine("Room: Creating new Room(" + roomNumber + ")"); + return roomVar; + } + private void createConnection(int roomNumber, String roomName, String clientName, String clientVar) { if (clientName.equals(userName)) return; @@ -162,12 +176,12 @@ private void createConnection(int roomNumber, String roomName, String clientName s.change().addHandler((oldValue, newValue) -> receivedChatMessage(roomNumber, clientName, newValue)); } - protected void sendMessage(String text) { + protected void typedMessage(String text) { if (!text.startsWith("/")) { // normal message int room = gui.getSelectedChat(); gui.displayMessage(room, "You: " + text); - rooms.get(room).set(text); + sendChatMessage(room, text); logger.fine("Send Message to Room" + room + ": " + text); } else { // message to server @@ -182,21 +196,16 @@ private void processCommand(String text) { if (command.equalsIgnoreCase("room")) { String[] temp1 = rest.split(" ", 2); String name = temp1[0]; - newRoom(name, temp1[1]); + initiateNewRoom(name, temp1[1]); } logger.fine("Processed Command: " + text); } - private void newRoom(String name, String recipients) { - int no = gui.newChat(name); - String roomVar = "room" + no; - Var room = new Var(roomVar, ""); - rooms.put(no, room); - roomNames.put(name, no); - logger.fine("Room: Creating new Room(" + no + ") to " + recipients); + private void initiateNewRoom(String name, String recipients) { + String roomVar = newRoom(name); // room command: room ... logger.finer("Room: Sending to Server: " + "room " + name + " " + roomVar + " " + recipients); - toServer.set("room " + name + " " + roomVar + " " + recipients); + sendServerMessage("room " + name + " " + roomVar + " " + recipients); } public static void main(String[] args) { diff --git a/Dream2/src/examples/java/dream/examples/chat/ChatGUI.java b/Dream2/src/examples/java/dream/examples/chat/core/ChatGUI.java similarity index 98% rename from Dream2/src/examples/java/dream/examples/chat/ChatGUI.java rename to Dream2/src/examples/java/dream/examples/chat/core/ChatGUI.java index 88d6f98..daa5f93 100644 --- a/Dream2/src/examples/java/dream/examples/chat/ChatGUI.java +++ b/Dream2/src/examples/java/dream/examples/chat/core/ChatGUI.java @@ -1,4 +1,4 @@ -package dream.examples.chat; +package dream.examples.chat.core; import java.awt.Color; import java.awt.Component; @@ -103,7 +103,7 @@ public void removeOnline(String name) { } private void sendText() { - listener.sendMessage(getTypedText()); + listener.typedMessage(getTypedText()); this.resetTypedText(); } @@ -247,7 +247,7 @@ public void windowDeactivated(WindowEvent e) { @Override public void windowClosing(WindowEvent e) { - listener.sendMessage("/quit"); + listener.typedMessage("/quit"); } @Override diff --git a/Dream2/src/examples/java/dream/examples/chat/ChatServer.java b/Dream2/src/examples/java/dream/examples/chat/core/ChatServer.java similarity index 89% rename from Dream2/src/examples/java/dream/examples/chat/ChatServer.java rename to Dream2/src/examples/java/dream/examples/chat/core/ChatServer.java index 230ce70..c71a71d 100644 --- a/Dream2/src/examples/java/dream/examples/chat/ChatServer.java +++ b/Dream2/src/examples/java/dream/examples/chat/core/ChatServer.java @@ -1,4 +1,4 @@ -package dream.examples.chat; +package dream.examples.chat.core; import java.math.BigInteger; import java.security.SecureRandom; @@ -99,7 +99,11 @@ private void createNewSessionFor(String clientName, String clientVar) { logger.fine("Setup: Waiting for first message from " + clientName); } - private void receivedMessage(String clientName, String message) { + protected void sendMessage(String clientName, String message) { + clientVars.get(clientName).set(message); + } + + protected void receivedMessage(String clientName, String message) { logger.fine("Received client message from " + clientName + ": " + message); if (!clientVars.containsKey(clientName)) { // first message from client @@ -122,20 +126,20 @@ private void receivedMessage(String clientName, String message) { roomVars.put(clientName, t[1]); // send message to every recipient to send a Var for that room if (otherClients.equals("*")) { - for (String c : clients.get()) { + for (String client : clients.get()) { // set Var for c as "not sent" - roomVars.put(c, null); + roomVars.put(client, null); // and ask c for his Var - logger.finer("Room: Sending Var-Request for " + roomName + " to " + c); - clientVars.get(c).set("room " + roomName + " " + otherClients.replace(c, clientName)); + logger.finer("Room: Sending Var-Request for " + roomName + " to " + client); + sendMessage(client, "room " + roomName + " " + otherClients.replace(client, clientName)); } } else { - for (String c : otherClients.split(" ")) { + for (String client : otherClients.split(" ")) { // set Var for c as "not sent" - roomVars.put(c, null); + roomVars.put(client, null); // and ask c for his Var - logger.finer("Room: Sending Var-Request for " + roomName + " to " + c); - clientVars.get(c).set("room " + roomName + " " + otherClients.replace(c, clientName)); + logger.finer("Room: Sending Var-Request for " + roomName + " to " + client); + sendMessage(client, "room " + roomName + " " + otherClients.replace(client, clientName)); } } rooms.put(roomName, roomVars); @@ -155,7 +159,7 @@ private void receivedMessage(String clientName, String message) { varString += e.getKey() + "=" + e.getValue() + " "; } for (String client : roomVars.keySet()) { - clientVars.get(client).set("roomVar " + roomName + " " + varString); + sendMessage(client, "roomVar " + roomName + " " + varString); } logger.fine("Room: Finished setting up room " + roomName); } diff --git a/Dream2/src/examples/java/dream/examples/chat_fifo/VectorClock.java b/Dream2/src/examples/java/dream/examples/chat/fifo/VectorClock.java similarity index 99% rename from Dream2/src/examples/java/dream/examples/chat_fifo/VectorClock.java rename to Dream2/src/examples/java/dream/examples/chat/fifo/VectorClock.java index c5189cb..4e5b3e5 100644 --- a/Dream2/src/examples/java/dream/examples/chat_fifo/VectorClock.java +++ b/Dream2/src/examples/java/dream/examples/chat/fifo/VectorClock.java @@ -1,4 +1,4 @@ -package dream.examples.chat_fifo; +package dream.examples.chat.fifo; import java.io.Serializable; import java.util.Arrays; diff --git a/Dream2/src/examples/java/dream/examples/chat/package-info.java b/Dream2/src/examples/java/dream/examples/chat/package-info.java index 1e63a68..cba4f26 100644 --- a/Dream2/src/examples/java/dream/examples/chat/package-info.java +++ b/Dream2/src/examples/java/dream/examples/chat/package-info.java @@ -2,9 +2,9 @@ * A small example for a Chat Application * * Usage: Start {@link javareact.chat.ChatServer ChatServer} first. Then start - * as many instances of {@link javareact.chat.Chat Chat} as you need and supply - * a user name as the first parameter. + * as many instances of {@link dream.examples.chat.core.Chat Chat} as you need + * and supply a user name as the first parameter. * * @author Tobias Becker */ -package dream.examples.chat; \ No newline at end of file +package dream.examples.chat; diff --git a/Dream2/src/examples/java/dream/examples/chat/util/Starter.java b/Dream2/src/examples/java/dream/examples/chat/util/Starter.java index 702f72b..b6864d5 100644 --- a/Dream2/src/examples/java/dream/examples/chat/util/Starter.java +++ b/Dream2/src/examples/java/dream/examples/chat/util/Starter.java @@ -4,8 +4,8 @@ import java.util.ArrayList; import java.util.concurrent.TimeUnit; -import dream.examples.chat.Chat; -import dream.examples.chat.ChatServer; +import dream.examples.chat.core.Chat; +import dream.examples.chat.core.ChatServer; /** * Convenience class to start ChatServer and x Chats (x = CHAT_COUNT), each in diff --git a/Dream2/src/examples/java/dream/examples/chat_fifo/Chat.java b/Dream2/src/examples/java/dream/examples/chat_fifo/Chat.java deleted file mode 100644 index f82c79b..0000000 --- a/Dream2/src/examples/java/dream/examples/chat_fifo/Chat.java +++ /dev/null @@ -1,205 +0,0 @@ -package dream.examples.chat_fifo; - -import java.awt.EventQueue; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.logging.Level; -import java.util.logging.Logger; -import java.util.stream.Collectors; - -import dream.client.RemoteVar; -import dream.client.Signal; -import dream.client.Var; -import dream.common.Consts; - -public class Chat { - - private Var myMessages; - private Var incoming; - private String userName; - private ChatGUI gui; - private List listening; - Signal> onlineList; - private VectorClock clock = new VectorClock(); - private ArrayList> allMessages = new ArrayList>(); - - public Chat(String username) throws Exception { - this.userName = username; - - Consts.hostName = userName; - Logger.getGlobal().setLevel(Level.ALL); - listening = new ArrayList<>(); - // Establish new session with server - RemoteVar> registeredClients = new RemoteVar>(ChatServer.NAME, - ChatServer.SERVER_REGISTERED_CLIENTS); - onlineList = new Signal>("setup", () -> { - if (registeredClients.get() == null) - return new ArrayList(); - else - return registeredClients.get(); - }, registeredClients); - onlineList.change().addHandler((o, n) -> { - if (n.contains("chat_message@" + username) && gui == null) { - System.out.println("Setup: Server Registration done!"); - setup(); - } - List names = n.stream().map(x -> x.split("@")[1]).collect(Collectors.toList()); - setOnline(names); - checkConnections(n); - }); - - myMessages = new Var("chat_message", ""); - incoming = new Var("incoming_messages", ""); - System.out.println("Setup: Waiting for Registration to Server ..."); - } - - private List lastOnline; - - private void setOnline(List online) { - if (lastOnline != null) { - for (String s : lastOnline) { - if (!online.contains(s)) { - String msg = s + " has left the Chat."; - clock.incrementClock(userName); - allMessages.add(new Pair(msg, clock)); - gui.displayMessage(msg); - } - } - for (String s : online) { - if (!lastOnline.contains(s)) { - String msg = s + " has joined."; - clock.incrementClock(userName); - allMessages.add(new Pair(msg, clock)); - gui.displayMessage(msg); - } - } - } - lastOnline = online; - gui.setOnline(online); - } - - private void checkConnections(ArrayList n) { - n.stream().map(x -> new Pair(x.split("@")[1], x.split("@")[0])).// Pair(Host,Var) - filter(x -> !listening.contains(x.getHost()) && x.getVar().startsWith("chat_")).// - forEach(x -> { - RemoteVar temp = new RemoteVar<>(x.getHost(), x.getVar()); - listening.add(x.getHost()); - new Signal("incoming" + x.getHost(), () -> { - if (temp.get() != null) - return x.getHost() + ": " + temp.get(); - else - return ""; - }, temp).change().addHandler((oldVal, newVal) -> incoming.set(newVal)); - System.out.println("Adding listener to " + x.getHost()); - }); - /* - * if (!n.stream().map(x -> listening.contains(x)).reduce((a, b) -> a && - * b).orElse(false)) { System.out.println("checking again in 5"); try { - * Thread.sleep(5000); } catch (InterruptedException e) { - * e.printStackTrace(); } - * - * checkConnections(onlineList.get()); } - */ - } - - private void setup() { - Signal display = new Signal("display", () -> { - String msg = incoming.get().split("\\|", 2)[0]; - if (msg.startsWith("/")) { - String[] temp = msg.split(" ", 2); - String command = temp[0].substring(1, temp[0].length()); - String rest = temp.length > 1 ? temp[1] : ""; - // QUIT - for now only used to update the registeredClients list - // (aka who's online) - if (command.equalsIgnoreCase("W")) { - String[] temp1 = rest.split(" ", 2); - String sender = temp1[0]; - String message = temp1.length > 1 ? temp1[1] : ""; - return sender + " whispered: " + message; - } else - return null; - } else - return msg; - }, incoming); - - System.out.println("Setup: Starting GUI"); - gui = new ChatGUI(userName); - gui.setListener(this); - - display.change().addHandler((oldValue, newValue) -> { - if (newValue != null) { - String msg = incoming.get().split("\\|", 2)[0]; - String incClock = incoming.get().split("\\|", 2)[1]; - Logger.getGlobal().fine("Received Message: " + incoming.get()); - Logger.getGlobal().finer("Current clock: " + clock.toString()); - VectorClock incomingClock = VectorClock.fromString(incClock); - allMessages.add(new Pair(msg, incomingClock)); - Collections.sort(allMessages, (p1, p2) -> new VectorClock().compare(p1.getSecond(), p2.getSecond())); - clock = VectorClock.max(clock, incomingClock); - gui.displayMessage(allMessages); - } - }); - } - - protected void sendMessage(String text) { - clock.incrementClock(userName); - String clockString = clock.toString(); - if (!text.startsWith("/")) { - allMessages.add(new Pair("You: " + text, clock)); - gui.displayMessage("You: " + text); - } - myMessages.set(text + "|" + clockString); - Logger.getGlobal().fine("Send Message: " + text + "|" + clockString); - Logger.getGlobal().finer("Current clock: " + clock.toString()); - } - - public static void main(String[] args) { - try { - if (args.length < 1) { - System.out.println("username missing"); - return; - } - EventQueue.invokeLater(new Runnable() { - - @Override - public void run() { - try { - new Chat(args[0]); - } catch (Exception e) { - e.printStackTrace(); - } - - } - }); - } catch (Exception e) { - e.printStackTrace(); - } - } -} - -class Pair { - private final S first; - private final T second; - - Pair(S host, T var) { - this.first = host; - this.second = var; - } - - public T getSecond() { - return second; - } - - public S getFirst() { - return first; - } - - public S getHost() { - return first; - } - - public T getVar() { - return second; - } -} \ No newline at end of file diff --git a/Dream2/src/examples/java/dream/examples/chat_fifo/ChatGUI.java b/Dream2/src/examples/java/dream/examples/chat_fifo/ChatGUI.java deleted file mode 100644 index 699f17a..0000000 --- a/Dream2/src/examples/java/dream/examples/chat_fifo/ChatGUI.java +++ /dev/null @@ -1,255 +0,0 @@ -package dream.examples.chat_fifo; - -import java.awt.Color; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.KeyEvent; -import java.awt.event.KeyListener; -import java.awt.event.WindowEvent; -import java.awt.event.WindowListener; -import java.io.File; -import java.net.MalformedURLException; -import java.util.ArrayList; -import java.util.List; - -import javax.swing.DefaultListCellRenderer; -import javax.swing.DefaultListModel; -import javax.swing.ImageIcon; -import javax.swing.JButton; -import javax.swing.JFrame; -import javax.swing.JLabel; -import javax.swing.JList; -import javax.swing.JTextArea; -import javax.swing.JTextField; -import javax.swing.ListSelectionModel; -import javax.swing.SpringLayout; -import javax.swing.SwingUtilities; - -public class ChatGUI extends JFrame implements WindowListener { - - private static final long serialVersionUID = 4659984914364067514L; - private JTextArea msgs; - private JTextField sendText; - private JList statusList; - private DefaultListModel listModel; - - private Chat listener; - - public ChatGUI(String userName) { - this.addWindowListener(this); - initUI(userName); - - } - - public void setListener(Chat c) { - listener = c; - } - - public String getTypedText() { - return sendText.getText(); - } - - public void resetTypedText() { - sendText.setText(""); - } - - public void displayMessage(String text) { - if (msgs.getText().isEmpty()) - msgs.append(text); - else - msgs.append(System.lineSeparator() + text); - } - - public void displayMessage(ArrayList> allMessages) { - msgs.setText(""); - for (Pair pair : allMessages) { - displayMessage(pair.getFirst()); - } - } - - public void setOnline(List online) { - List offlineList = new ArrayList(); - for (int i = 0; i < listModel.size(); i++) { - if (!online.contains(listModel.get(i))) - offlineList.add(listModel.get(i)); - } - SwingUtilities.invokeLater(new Runnable() { - - @Override - public void run() { - listModel.clear(); - for (String e : online) { - listModel.addElement(e); - } - for (String e : offlineList) - listModel.addElement(e); - statusList.setSelectionInterval(0, online.size() - 1); - } - }); - - } - - public void removeOnline(String name) { - listModel.removeElement(name); - } - - private void sendText() { - listener.sendMessage(getTypedText()); - this.resetTypedText(); - } - - private void initUI(String userName) { - sendText = new JTextField(20); - sendText.addKeyListener(new KeyListener() { - - @Override - public void keyTyped(KeyEvent e) { - } - - @Override - public void keyReleased(KeyEvent e) { - } - - @Override - public void keyPressed(KeyEvent e) { - if (e.getKeyCode() == KeyEvent.VK_ENTER) - sendText(); - } - }); - JButton sendButton = new JButton("Send"); - sendButton.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent event) { - sendText(); - } - }); - msgs = new JTextArea(5, 27); - msgs.setEditable(false); - msgs.setMaximumSize(null); - - listModel = new DefaultListModel(); - statusList = new JList(listModel); - statusList.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); - statusList.setEnabled(false); - statusList.setLayoutOrientation(JList.HORIZONTAL_WRAP); - statusList.setVisibleRowCount(-1); - // statusList.setSelectionBackground(Color.GREEN); - // statusList.setSelectionForeground(Color.BLACK); - // statusList.setForeground(Color.BLACK); - // statusList.setBackground(Color.WHITE); - // statusList.setCellRenderer(new DefaultListCellRenderer()); - - statusList.setCellRenderer(new DefaultListCellRenderer() { - - private static final long serialVersionUID = 9019815674349211344L; - private JLabel label = new JLabel(); - private Color textSelectionColor = Color.BLACK; - private Color backgroundSelectionColor = Color.CYAN; - private Color textNonSelectionColor = Color.BLACK; - private Color backgroundNonSelectionColor = Color.WHITE; - - @Override - public java.awt.Component getListCellRendererComponent(javax.swing.JList list, Object value, int index, - boolean isSelected, boolean cellHasFocus) { - - String name = (String) value; - label.setText(name); - - if (isSelected) { - label.setBackground(backgroundSelectionColor); - label.setForeground(textSelectionColor); - label.setIcon(createImageIcon("status-online.png", "Online")); - } else { - label.setBackground(backgroundNonSelectionColor); - label.setForeground(textNonSelectionColor); - label.setIcon(createImageIcon("status-offline.png", "Offline")); - } - - return label; - }; - - }); - - SpringLayout layout = new SpringLayout(); - - // put messages on (5,5) - layout.putConstraint(SpringLayout.WEST, msgs, 5, SpringLayout.WEST, getContentPane()); - layout.putConstraint(SpringLayout.NORTH, msgs, 5, SpringLayout.NORTH, getContentPane()); - - // put textfield below messages - layout.putConstraint(SpringLayout.NORTH, sendText, 5, SpringLayout.SOUTH, msgs); - layout.putConstraint(SpringLayout.WEST, sendText, 5, SpringLayout.WEST, getContentPane()); - - // put button next to the textfield - layout.putConstraint(SpringLayout.NORTH, sendButton, 5, SpringLayout.SOUTH, msgs); - layout.putConstraint(SpringLayout.WEST, sendButton, 5, SpringLayout.EAST, sendText); - - // make the frame big enough to fit all in - layout.putConstraint(SpringLayout.EAST, getContentPane(), 10, SpringLayout.EAST, statusList); - layout.putConstraint(SpringLayout.SOUTH, getContentPane(), 10, SpringLayout.SOUTH, sendText); - - layout.putConstraint(SpringLayout.NORTH, statusList, 5, SpringLayout.NORTH, getContentPane()); - layout.putConstraint(SpringLayout.WEST, statusList, 15, SpringLayout.EAST, sendButton); - - getContentPane().setLayout(layout); - - getContentPane().add(msgs); - getContentPane().add(sendText); - getContentPane().add(sendButton); - getContentPane().add(statusList); - - setTitle("Chat - " + userName); - // setSize(300, 200); - setLocationRelativeTo(null); - setDefaultCloseOperation(EXIT_ON_CLOSE); - - pack(); - setVisible(true); - } - - /** Returns an ImageIcon, or null if the path was invalid. */ - protected ImageIcon createImageIcon(String path, String description) { - File img = new File("./src/resources/dream/examples/chat/" + path); - try { - java.net.URL imgURL = img.toURI().toURL(); - if (imgURL != null) { - return new ImageIcon(imgURL, description); - } else { - System.err.println("Couldn't find file: " + path); - return null; - } - } catch (MalformedURLException e) { - System.err.println("Couldn't find file: " + path); - return null; - } - } - - @Override - public void windowOpened(WindowEvent e) { - } - - @Override - public void windowIconified(WindowEvent e) { - } - - @Override - public void windowDeiconified(WindowEvent e) { - } - - @Override - public void windowDeactivated(WindowEvent e) { - } - - @Override - public void windowClosing(WindowEvent e) { - listener.sendMessage("/quit"); - } - - @Override - public void windowClosed(WindowEvent e) { - } - - @Override - public void windowActivated(WindowEvent e) { - } -} diff --git a/Dream2/src/examples/java/dream/examples/chat_fifo/ChatServer.java b/Dream2/src/examples/java/dream/examples/chat_fifo/ChatServer.java deleted file mode 100644 index 3b76ff9..0000000 --- a/Dream2/src/examples/java/dream/examples/chat_fifo/ChatServer.java +++ /dev/null @@ -1,119 +0,0 @@ -package dream.examples.chat_fifo; - -import java.math.BigInteger; -import java.security.SecureRandom; -import java.util.ArrayList; -import java.util.Set; - -import dream.client.DreamClient; -import dream.client.Var; -import dream.common.Consts; -import dream.locking.LockManagerLauncher; -import dream.server.ServerLauncher; -import javafx.util.Pair; - -public class ChatServer { - public static final String NAME = "ChatServer"; - - private boolean serverStarted = false; - private boolean lockManagerStarted = false; - - private Var> clients; - - private static SecureRandom r = new SecureRandom(); - - public static final String SERVER_PREFIX = "server_"; - public static final String SERVER_REGISTERED_CLIENTS = SERVER_PREFIX + "RegisteredClients"; - - public static void main(String[] args) { - new ChatServer().start(); - } - - private void initServer() { - Consts.hostName = NAME; - - clients = new Var>(SERVER_REGISTERED_CLIENTS, new ArrayList()); - detectNewSession(); - } - - /** - * Look for new clients every 5 seconds - */ - private void detectNewSession() { - Set vars = DreamClient.instance.listVariables(); - vars.stream().map(x -> new Pair(x.split("@")[1], x.split("@")[0])).// Pair(Host,Var) - filter(x -> !clients.get().contains(x.getValue() + "@" + x.getKey()) - && x.getValue().startsWith("chat_")) - .// - forEach(x -> createNewSessionFor(x.getKey(), x.getValue())); - try { - Thread.sleep(5000); - } catch (InterruptedException e) { - e.printStackTrace(); - } - - detectNewSession(); - - } - - /** - * Registers a new chat client with its name and the name of the variable it - * is sending messages on - * - * @param clientName - * the name of the client - * @param clientVar - * the name of the variable, must be of type String - */ - private void createNewSessionFor(String clientName, String clientVar) { - // add client as registered - clients.modify((old) -> old.add(clientVar + "@" + clientName)); - } - - /** - * @return random String hashed with SHA-256 - */ - public static String getRandom() { - return new BigInteger(130, r).toString(32); - } - - public void start() { - startServerIfNeeded(); - startLockManagerIfNeeded(); - - // Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).setLevel(Level.ALL); - initServer(); - - while (true) { - try { - Thread.sleep(1000); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - } - - private final void startServerIfNeeded() { - if (!serverStarted) { - ServerLauncher.start(); - serverStarted = true; - } - try { - Thread.sleep(500); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - - private final void startLockManagerIfNeeded() { - if (!lockManagerStarted) { - LockManagerLauncher.start(); - lockManagerStarted = true; - } - try { - Thread.sleep(500); - } catch (final InterruptedException e) { - e.printStackTrace(); - } - } -} diff --git a/Dream2/src/examples/java/dream/examples/chat_fifo/DiffToNormal.txt b/Dream2/src/examples/java/dream/examples/chat_fifo/DiffToNormal.txt deleted file mode 100644 index 4fb0d5f..0000000 --- a/Dream2/src/examples/java/dream/examples/chat_fifo/DiffToNormal.txt +++ /dev/null @@ -1,9 +0,0 @@ -Chat: -19 lines added (2 for Logging) -5 lines changed - -ChatGUI: -6 lines added - -VectorClock: -148 lines added (252 lines with comments and empty lines) \ No newline at end of file From 631fc46f29793b857c453a73fef935f907e85b04 Mon Sep 17 00:00:00 2001 From: Tobias Becker Date: Mon, 2 May 2016 15:38:47 +0200 Subject: [PATCH 039/161] added some gui improvement --- .../src/examples/java/dream/examples/chat/core/ChatGUI.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Dream2/src/examples/java/dream/examples/chat/core/ChatGUI.java b/Dream2/src/examples/java/dream/examples/chat/core/ChatGUI.java index daa5f93..94e5d63 100644 --- a/Dream2/src/examples/java/dream/examples/chat/core/ChatGUI.java +++ b/Dream2/src/examples/java/dream/examples/chat/core/ChatGUI.java @@ -19,6 +19,7 @@ import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JList; +import javax.swing.JScrollPane; import javax.swing.JTabbedPane; import javax.swing.JTextArea; import javax.swing.JTextField; @@ -64,13 +65,16 @@ public int newChat(String name) { msgs.add(new JTextArea(5, 27)); int r = msgs.size() - 1; msgs.get(r).setEditable(false); - jtp.add(name, msgs.get(r)); + JScrollPane sp = new JScrollPane(msgs.get(r)); + sp.setAutoscrolls(true); + jtp.add(name, sp); pack(); return r; } public void closeChat(int index) { Component t = jtp.getComponentAt(index); + jtp.remove(t); } public void displayMessage(int room, String text) { From 4abaa88442053c98555875af3171b4cee19dcfce Mon Sep 17 00:00:00 2001 From: Tobias Becker Date: Mon, 2 May 2016 15:39:46 +0200 Subject: [PATCH 040/161] added one VectorClock for every room --- .../java/dream/examples/chat/core/Chat.java | 10 +- .../java/dream/examples/chat/fifo/Chat.java | 110 ++++++++++++++++++ 2 files changed, 117 insertions(+), 3 deletions(-) create mode 100644 Dream2/src/examples/java/dream/examples/chat/fifo/Chat.java diff --git a/Dream2/src/examples/java/dream/examples/chat/core/Chat.java b/Dream2/src/examples/java/dream/examples/chat/core/Chat.java index 3b10c52..02e44ec 100644 --- a/Dream2/src/examples/java/dream/examples/chat/core/Chat.java +++ b/Dream2/src/examples/java/dream/examples/chat/core/Chat.java @@ -17,7 +17,7 @@ public class Chat { - private final String userName; + protected final String userName; private ChatGUI gui; private final Signal> onlineList; @@ -28,7 +28,7 @@ public class Chat { private Map> rooms = new HashMap<>(); private Map roomNames = new HashMap<>(); - private final Logger logger; + protected final Logger logger; private int posX; private int posY; @@ -153,8 +153,12 @@ protected void sendServerMessage(String message) { toServer.set(message); } - protected String newRoom(String roomName) { + private String newRoom(String roomName) { int roomNumber = gui.newChat(roomName); + return newRoom(roomName, roomNumber); + } + + protected String newRoom(String roomName, int roomNumber) { String roomVar = "room" + roomNumber; Var room = new Var(roomVar, ""); rooms.put(roomNumber, room); diff --git a/Dream2/src/examples/java/dream/examples/chat/fifo/Chat.java b/Dream2/src/examples/java/dream/examples/chat/fifo/Chat.java new file mode 100644 index 0000000..ebeb1a7 --- /dev/null +++ b/Dream2/src/examples/java/dream/examples/chat/fifo/Chat.java @@ -0,0 +1,110 @@ +package dream.examples.chat.fifo; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map.Entry; + +public class Chat extends dream.examples.chat.core.Chat { + + private HashMap roomClocks = new HashMap<>(); + private HashMap> messageCache = new HashMap<>(); + + public Chat(String username, int window_x, int window_y) { + super(username, window_x, window_y); + } + + @Override + protected void sendChatMessage(int roomNumber, String message) { + // increment VectorClock by 1 + VectorClock roomClock = roomClocks.get(roomNumber); + roomClock.incrementClock(userName); + // attach VectorClock to message + String newMessage = roomClock.toString() + "|" + message; + logger.fine("Sending message with VectorClock " + roomClock + ": " + message); + super.sendChatMessage(roomNumber, newMessage); + } + + @Override + protected void receivedChatMessage(int roomNumber, String sender, String message) { + String[] temp = message.split("\\|", 2); + VectorClock messageClock = new VectorClock(temp[0]); + String newMessage = temp[1]; + cacheMessage(messageClock, roomNumber, sender, newMessage); + processCachedMessages(roomNumber); + } + + private void cacheMessage(VectorClock messageClock, int roomNumber, String sender, String message) { + messageCache.get(roomNumber).add(new CachedMessage(messageClock, roomNumber, sender, message)); + } + + private void processCachedMessages(int roomNumber) { + List messages = messageCache.get(roomNumber); + VectorClock roomClock = roomClocks.get(roomNumber); + boolean removed = false; + for (CachedMessage cm : messages) { + if (cm.canSend(roomClock)) { + // increase received messages from the sender + roomClock.incrementClock(cm.getSender()); + // deliver the message + super.receivedChatMessage(cm.getRoom(), cm.getSender(), cm.getMessage()); + // remove message from cache + messages.remove(cm); + } + } + if (removed) + processCachedMessages(roomNumber); + } + + @Override + protected String newRoom(String roomName, int roomNumber) { + roomClocks.put(roomNumber, new VectorClock()); + messageCache.put(roomNumber, new ArrayList<>()); + return super.newRoom(roomName, roomNumber); + } +} + +class CachedMessage { + private VectorClock clock; + private int room; + private String sender; + private String message; + + public CachedMessage(VectorClock messageClock, int roomNumber, String sender, String message) { + this.clock = messageClock; + this.room = roomNumber; + this.sender = sender; + this.message = message; + } + + public boolean canSend(VectorClock roomClock) { + // delivered every message that the sender has sent before this message + boolean senderSent = clock.get(sender) == roomClock.get(sender) + 1; + // delivered every message that the sender has delivered before this + // message + boolean senderDelivered = true; + for (Entry e : clock.entrySet()) { + if (!e.getKey().equals(sender)) { + if (clock.get(e.getKey()) > roomClock.get(e.getKey())) + senderDelivered = false; + } + } + return senderSent && senderDelivered; + } + + public VectorClock getClock() { + return clock; + } + + public int getRoom() { + return room; + } + + public String getSender() { + return sender; + } + + public String getMessage() { + return message; + } +} From ad825cbdf05621bac22de81a25527b0d134744c8 Mon Sep 17 00:00:00 2001 From: Tobias Becker Date: Tue, 3 May 2016 20:40:02 +0200 Subject: [PATCH 041/161] commands to show a graph-representation --- .../java/dream/examples/chat/core/Chat.java | 7 ++- .../dream/examples/chat/core/ChatServer.java | 3 ++ .../examples/chat/util/DependencyGraph.java | 47 +++++++++++++++++++ 3 files changed, 56 insertions(+), 1 deletion(-) create mode 100644 Dream2/src/examples/java/dream/examples/chat/util/DependencyGraph.java diff --git a/Dream2/src/examples/java/dream/examples/chat/core/Chat.java b/Dream2/src/examples/java/dream/examples/chat/core/Chat.java index 02e44ec..0968b66 100644 --- a/Dream2/src/examples/java/dream/examples/chat/core/Chat.java +++ b/Dream2/src/examples/java/dream/examples/chat/core/Chat.java @@ -14,6 +14,7 @@ import dream.client.Signal; import dream.client.Var; import dream.common.Consts; +import dream.examples.chat.util.DependencyGraph; public class Chat { @@ -44,7 +45,7 @@ public Chat(String username, int window_x, int window_y) { // Establish new session with server RemoteVar> registeredClients = new RemoteVar>(ChatServer.NAME, ChatServer.SERVER_REGISTERED_CLIENTS); - onlineList = new Signal>("setup", () -> { + onlineList = new Signal>("onlineList", () -> { if (registeredClients.get() == null) return new ArrayList(); else @@ -201,6 +202,10 @@ private void processCommand(String text) { String[] temp1 = rest.split(" ", 2); String name = temp1[0]; initiateNewRoom(name, temp1[1]); + } else if (command.equalsIgnoreCase("graph")) { + DependencyGraph.show(); + } else if (command.equalsIgnoreCase("sgraph")) { + sendServerMessage("graph"); } logger.fine("Processed Command: " + text); } diff --git a/Dream2/src/examples/java/dream/examples/chat/core/ChatServer.java b/Dream2/src/examples/java/dream/examples/chat/core/ChatServer.java index c71a71d..22d294a 100644 --- a/Dream2/src/examples/java/dream/examples/chat/core/ChatServer.java +++ b/Dream2/src/examples/java/dream/examples/chat/core/ChatServer.java @@ -15,6 +15,7 @@ import dream.client.Signal; import dream.client.Var; import dream.common.Consts; +import dream.examples.chat.util.DependencyGraph; import dream.locking.LockManagerLauncher; import dream.server.ServerLauncher; @@ -163,6 +164,8 @@ protected void receivedMessage(String clientName, String message) { } logger.fine("Room: Finished setting up room " + roomName); } + } else if (command.equalsIgnoreCase("graph")) { + DependencyGraph.show(); } } } diff --git a/Dream2/src/examples/java/dream/examples/chat/util/DependencyGraph.java b/Dream2/src/examples/java/dream/examples/chat/util/DependencyGraph.java new file mode 100644 index 0000000..8eb1fc8 --- /dev/null +++ b/Dream2/src/examples/java/dream/examples/chat/util/DependencyGraph.java @@ -0,0 +1,47 @@ +package dream.examples.chat.util; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.Collection; +import java.util.Map; +import java.util.Map.Entry; + +public class DependencyGraph { + + private static File writeAndOpen(String data, String filenamePrefix, String filenameSuffix) { + File file = null; + try { + file = File.createTempFile(filenamePrefix, filenameSuffix); + + FileOutputStream fos = new java.io.FileOutputStream(file); + fos.write(data.getBytes("UTF-8")); + fos.close(); + java.awt.Desktop.getDesktop().open(file); + } catch (IOException e) { + e.printStackTrace(); + } + return file; + } + + private static String toDot(Map> nodes) { + return toDot(nodes, "forward", "1.0"); + } + + private static String toDot(Map> nodes, String dir, String ranksep) { + String s = "digraph G {\n\tdir=" + dir + ";\n\tranksep=" + ranksep + ";\n"; + for (Entry> e : nodes.entrySet()) { + for (String e2 : e.getValue()) { + s += "\t\"" + e.getKey() + "\" -> \"" + e2 + "\" [dir=" + dir + "];\n"; + } + } + s += "}"; + return s; + } + + public static void show() { + Map> graph = dream.common.utils.DependencyGraph.instance.getGraph(); + System.out.println(graph); + writeAndOpen(toDot(graph), "temp", ".dot"); + } +} From 65a433718175c5a10c8456d4822ddf8d44ef17b4 Mon Sep 17 00:00:00 2001 From: Tobias Becker Date: Wed, 4 May 2016 22:03:59 +0200 Subject: [PATCH 042/161] added clustering of host to visualization of dependency graph --- .../examples/chat/util/DependencyGraph.java | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/Dream2/src/examples/java/dream/examples/chat/util/DependencyGraph.java b/Dream2/src/examples/java/dream/examples/chat/util/DependencyGraph.java index 8eb1fc8..4325395 100644 --- a/Dream2/src/examples/java/dream/examples/chat/util/DependencyGraph.java +++ b/Dream2/src/examples/java/dream/examples/chat/util/DependencyGraph.java @@ -4,8 +4,11 @@ import java.io.FileOutputStream; import java.io.IOException; import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; import java.util.Map; import java.util.Map.Entry; +import java.util.Set; public class DependencyGraph { @@ -28,8 +31,32 @@ private static String toDot(Map> nodes) { return toDot(nodes, "forward", "1.0"); } + private static Map> addHostVar(Map> map, String hv) { + String[] temp = hv.split("@", 2); + String var = temp[0]; + String host = temp[1]; + if (!map.containsKey(host)) + map.put(host, new HashSet()); + map.get(host).add(var); + return map; + } + private static String toDot(Map> nodes, String dir, String ranksep) { String s = "digraph G {\n\tdir=" + dir + ";\n\tranksep=" + ranksep + ";\n"; + Map> hostVars = new HashMap<>(); + for (String str : nodes.keySet()) { + addHostVar(hostVars, str); + for (String str2 : nodes.get(str)) { + addHostVar(hostVars, str2); + } + } + for (String host : hostVars.keySet()) { + s += "subgraph cluster_" + host + " {\n\tnode [style=filled];\n"; + for (String var : hostVars.get(host)) { + s += "\t\"" + var + "@" + host + "\";\n"; + } + s += "\tlabel = \"" + host + "\";\n\tcolor=blue\n}\n\n"; + } for (Entry> e : nodes.entrySet()) { for (String e2 : e.getValue()) { s += "\t\"" + e.getKey() + "\" -> \"" + e2 + "\" [dir=" + dir + "];\n"; From af01a6fe42c7d6a14e2e8bc7da498a577b6a576c Mon Sep 17 00:00:00 2001 From: guidosalva Date: Fri, 6 May 2016 08:54:00 +0200 Subject: [PATCH 043/161] Update README.md --- README.md | 51 ++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 36 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 0c23258..eba8174 100644 --- a/README.md +++ b/README.md @@ -1,20 +1,41 @@ # dream-java -## IMPL project - Kim Berninger and Michael Raulf -The objective of this project was to use Java's generics in order to provide a -type-safe version of the _DREAM_ framework. This implementation can be found in -the `Dream2` folder. The original implementation using _ANTRL_ in order to -parse reactive expressions is contained in the `Dream` folder. +The relevant source code is in the Dream2 folder. -Both projects are compatible with _Eclipse_ as well as _Maven_. However at the -time of writing the _AspectJ Maven Plugin_ was not yet compatible with -_Java 8_, so it is recommended to run the _ANTRL_ dependend _DREAM_ using -_Eclipse_. -The `DreamSim` folder contains a custom version of _DREAM_ which is used in -order to run a _ProtoPeer_ based simulation for measuring the overall -performance of _DREAM_ in an example network. -The _Latex_ code for a PDF containing the resulting graphs can be found in -`Graphs`. Similarly there is a roadmap of the overall project in the -`ChangeReport` folder. \ No newline at end of file +Var a = new Var<>("a", Integer.valueOf(1)); + +Var b = new Var<>("b", Integer.valueOf(2)); + +Signal c = + new Signal<>("c", () -> a.get() + b.get(), a, b); + + + + + + Var> lst = + new Var<>("lst", new ArrayList()); + + Signal n = + new Signal<>("n", () -> lst.get().size(), lst); + + lst.modify(self -> { + self.add(1); + self.add(2); + self.add(3); + }); + + +## Contributors + +Alessandro Margara + +Tobias Becker + +Kim Berninger and Michael Raulf - IMPL Project + + + + From 24c8276dbe436260ffd7d0aba36eade2ba8be19b Mon Sep 17 00:00:00 2001 From: guidosalva Date: Fri, 6 May 2016 08:57:29 +0200 Subject: [PATCH 044/161] Update README.md --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index eba8174..8b37beb 100644 --- a/README.md +++ b/README.md @@ -4,11 +4,11 @@ The relevant source code is in the Dream2 folder. -Var a = new Var<>("a", Integer.valueOf(1)); + Var a = new Var<>("a", Integer.valueOf(1)); -Var b = new Var<>("b", Integer.valueOf(2)); + Var b = new Var<>("b", Integer.valueOf(2)); -Signal c = + Signal c = new Signal<>("c", () -> a.get() + b.get(), a, b); From 2a9b881ec4e7bffee1ed7868ec37906004c63e97 Mon Sep 17 00:00:00 2001 From: guidosalva Date: Fri, 6 May 2016 09:01:05 +0200 Subject: [PATCH 045/161] Update README.md --- README.md | 35 +++++++++++++++++------------------ 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 8b37beb..d54fe37 100644 --- a/README.md +++ b/README.md @@ -3,30 +3,29 @@ The relevant source code is in the Dream2 folder. - - Var a = new Var<>("a", Integer.valueOf(1)); +``` +Var a = new Var<>("a", Integer.valueOf(1)); - Var b = new Var<>("b", Integer.valueOf(2)); +Var b = new Var<>("b", Integer.valueOf(2)); - Signal c = - new Signal<>("c", () -> a.get() + b.get(), a, b); - - +Signal c = + new Signal<>("c", () -> a.get() + b.get(), a, b); +``` +Another example: - - Var> lst = - new Var<>("lst", new ArrayList()); +``` +Var> lst = new Var<>("lst", new ArrayList()); - Signal n = - new Signal<>("n", () -> lst.get().size(), lst); +Signal n = + new Signal<>("n", () -> lst.get().size(), lst); - lst.modify(self -> { - self.add(1); - self.add(2); - self.add(3); - }); - + lst.modify(self -> { + self.add(1); + self.add(2); + self.add(3); + }); +``` ## Contributors From a2969bc53c221bfce0f5256ecc92f1c619a5a310 Mon Sep 17 00:00:00 2001 From: guidosalva Date: Fri, 6 May 2016 09:17:46 +0200 Subject: [PATCH 046/161] Update README.md --- README.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/README.md b/README.md index d54fe37..cefc492 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,28 @@ The relevant source code is in the Dream2 folder. +We initially need to assign a name to the current host `Host1`. +``` +Consts.hostname = "Host1"; +``` +Now on `Host1` we can define a Var `myVar` that is visible remotely. The var `myVar` constains a string, it is advertised with the name `exVar` and it is initialized to the value `AAA`. +``` +Var myVar = new Var("exVar", "AAA"); +``` + +On a different host, `Host2` we can read the var defined on `Host1` whose name is `exVar`. We can also define a signal that builds a computation on top of it. + +``` +RemoteVar rv = new RemoteVar("Host1", "exVar"); + +Signal s = new Signal>("s", () -> { + return rv + "XXX"; + }, rv); +``` +If rv is not available yet, ... +More examples: + + ``` Var a = new Var<>("a", Integer.valueOf(1)); From 3526cc8346ac5985b0e263a8f7654ca9671b972f Mon Sep 17 00:00:00 2001 From: guidosalva Date: Fri, 6 May 2016 09:19:33 +0200 Subject: [PATCH 047/161] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index cefc492..ae80e7e 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ The relevant source code is in the Dream2 folder. We initially need to assign a name to the current host `Host1`. ``` -Consts.hostname = "Host1"; +Consts.hostName = "Host1"; ``` Now on `Host1` we can define a Var `myVar` that is visible remotely. The var `myVar` constains a string, it is advertised with the name `exVar` and it is initialized to the value `AAA`. ``` @@ -18,7 +18,7 @@ On a different host, `Host2` we can read the var defined on `Host1` whose name i RemoteVar rv = new RemoteVar("Host1", "exVar"); Signal s = new Signal>("s", () -> { - return rv + "XXX"; + return rv.get() + "XXX"; }, rv); ``` If rv is not available yet, ... From 80cfc95e9f36e19a156b1efaa50f82c4612d188a Mon Sep 17 00:00:00 2001 From: guidosalva Date: Fri, 6 May 2016 09:25:37 +0200 Subject: [PATCH 048/161] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ae80e7e..0989627 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ The relevant source code is in the Dream2 folder. We initially need to assign a name to the current host `Host1`. -``` +```java Consts.hostName = "Host1"; ``` Now on `Host1` we can define a Var `myVar` that is visible remotely. The var `myVar` constains a string, it is advertised with the name `exVar` and it is initialized to the value `AAA`. From 1fddb7014ff21e492a137e4a5d206afa031ae802 Mon Sep 17 00:00:00 2001 From: guidosalva Date: Fri, 6 May 2016 09:57:10 +0200 Subject: [PATCH 049/161] Update README.md --- README.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 0989627..3e1f952 100644 --- a/README.md +++ b/README.md @@ -7,14 +7,15 @@ We initially need to assign a name to the current host `Host1`. ```java Consts.hostName = "Host1"; ``` -Now on `Host1` we can define a Var `myVar` that is visible remotely. The var `myVar` constains a string, it is advertised with the name `exVar` and it is initialized to the value `AAA`. -``` +Now on `Host1` we can define a Var `myVar` that is visible remotely. The var `myVar` constains a string, it is advertised with the name `exVar` and it is initialized to the value `AAA`. Later on the value is changed to `BBB`. +```java Var myVar = new Var("exVar", "AAA"); +myVar.set("BBB"); ``` On a different host, `Host2` we can read the var defined on `Host1` whose name is `exVar`. We can also define a signal that builds a computation on top of it. -``` +```java RemoteVar rv = new RemoteVar("Host1", "exVar"); Signal s = new Signal>("s", () -> { @@ -25,7 +26,7 @@ If rv is not available yet, ... More examples: -``` +```java Var a = new Var<>("a", Integer.valueOf(1)); Var b = new Var<>("b", Integer.valueOf(2)); From 5af7fdc7f7d49c5c2d3b87b5b04e788d3e4831df Mon Sep 17 00:00:00 2001 From: rkamath3 Date: Fri, 6 May 2016 11:08:47 +0200 Subject: [PATCH 050/161] Examples for beginer --- .../dream/examples/biginer/ConsumerApp.java | 40 ++++++++++++++++++ .../dream/examples/biginer/ProducerApp.java | 41 +++++++++++++++++++ .../dream/examples/biginer/StartInfra.java | 20 +++++++++ 3 files changed, 101 insertions(+) create mode 100644 Dream2/src/examples/java/dream/examples/biginer/ConsumerApp.java create mode 100644 Dream2/src/examples/java/dream/examples/biginer/ProducerApp.java create mode 100644 Dream2/src/examples/java/dream/examples/biginer/StartInfra.java diff --git a/Dream2/src/examples/java/dream/examples/biginer/ConsumerApp.java b/Dream2/src/examples/java/dream/examples/biginer/ConsumerApp.java new file mode 100644 index 0000000..4eb0adb --- /dev/null +++ b/Dream2/src/examples/java/dream/examples/biginer/ConsumerApp.java @@ -0,0 +1,40 @@ +package dream.examples.biginer; + +import dream.client.RemoteVar; +import dream.client.Signal; +import dream.common.Consts; + +/** + * An app which consumes the variables + */ +public class ConsumerApp { + + public ConsumerApp() throws Exception { + // App will be running on host different from the producer + Consts.hostName = "Host2"; + + // Register a Subscription + RemoteVar rv = new RemoteVar("Host1", "exVar"); + + // On every change in remote variable rv create a signal which could + // trigger apprpriate action + Signal s = new Signal("s", () -> { + return rv.get() + "ABC"; + } , rv); + + // Register a handler which will be executed upon receiving the signal + s.change().addHandler((oldVal, val) -> System.out.println("Signal1: " + val)); + } + + public static void main(String args[]) { + try { + // Start Consumer + new ConsumerApp(); + + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + } +} diff --git a/Dream2/src/examples/java/dream/examples/biginer/ProducerApp.java b/Dream2/src/examples/java/dream/examples/biginer/ProducerApp.java new file mode 100644 index 0000000..803d43d --- /dev/null +++ b/Dream2/src/examples/java/dream/examples/biginer/ProducerApp.java @@ -0,0 +1,41 @@ +package dream.examples.biginer; + +import dream.client.Var; +import dream.common.Consts; + +/** + * An app which produces the variables + */ +public class ProducerApp { + + public ProducerApp() throws Exception { + // Mention the host(node) which is producing the value + Consts.hostName = "Host1"; + + // myVar is created and registered as exVar for remote consumption. + // exVar is initialized to AAA + Var myVar = new Var("exVar", "AAA"); + int iteration = 10; + while (iteration > 0) { + Thread.sleep(1000); + // change value of exVar + myVar.set("Val-" + (10 - iteration) + ""); + System.out.println("Changed myvar " + myVar.get()); + iteration--; + } + } + + public static void main(String args[]) { + try { + + // Start Producer + new ProducerApp(); + + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + } + +} diff --git a/Dream2/src/examples/java/dream/examples/biginer/StartInfra.java b/Dream2/src/examples/java/dream/examples/biginer/StartInfra.java new file mode 100644 index 0000000..f00fdd2 --- /dev/null +++ b/Dream2/src/examples/java/dream/examples/biginer/StartInfra.java @@ -0,0 +1,20 @@ +package dream.examples.biginer; + +import dream.locking.LockManagerLauncher; +import dream.server.ServerLauncher; + +public class StartInfra { + + public static void main(String args[]) { + try { + // Start the Server + ServerLauncher.start(); + + // Start the LockManager + LockManagerLauncher.start(); + } catch (Exception e) { + // TODO Auto-generated catch block + + } + } +} From a2da67bf4f5deb47607fb6e06201f3997a35c756 Mon Sep 17 00:00:00 2001 From: rkamath3 Date: Fri, 6 May 2016 11:15:24 +0200 Subject: [PATCH 051/161] Change in comment --- .../src/examples/java/dream/examples/biginer/ConsumerApp.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dream2/src/examples/java/dream/examples/biginer/ConsumerApp.java b/Dream2/src/examples/java/dream/examples/biginer/ConsumerApp.java index 66f0fe1..020ce43 100644 --- a/Dream2/src/examples/java/dream/examples/biginer/ConsumerApp.java +++ b/Dream2/src/examples/java/dream/examples/biginer/ConsumerApp.java @@ -17,7 +17,7 @@ public ConsumerApp() throws Exception { RemoteVar rv = new RemoteVar("Host1", "exVar"); // On every change in remote variable rv create a signal which could - // trigger apprpriate action + // trigger appropriate action Signal s = new Signal("s", () -> { return rv.get() + "ABC"; } , rv); From cfce5e991eff37cfabc718b773f8b3574cbc7a9c Mon Sep 17 00:00:00 2001 From: Tobias Becker Date: Fri, 6 May 2016 11:44:53 +0200 Subject: [PATCH 052/161] form example --- .../java/dream/examples/form/Boss.java | 28 ++++ .../java/dream/examples/form/FormClient.java | 49 +++++++ .../java/dream/examples/form/FormGUI.java | 84 +++++++++++ .../java/dream/examples/form/FormServer.java | 136 ++++++++++++++++++ .../java/dream/examples/form/Secretary.java | 28 ++++ 5 files changed, 325 insertions(+) create mode 100644 Dream2/src/examples/java/dream/examples/form/Boss.java create mode 100644 Dream2/src/examples/java/dream/examples/form/FormClient.java create mode 100644 Dream2/src/examples/java/dream/examples/form/FormGUI.java create mode 100644 Dream2/src/examples/java/dream/examples/form/FormServer.java create mode 100644 Dream2/src/examples/java/dream/examples/form/Secretary.java diff --git a/Dream2/src/examples/java/dream/examples/form/Boss.java b/Dream2/src/examples/java/dream/examples/form/Boss.java new file mode 100644 index 0000000..cc982e8 --- /dev/null +++ b/Dream2/src/examples/java/dream/examples/form/Boss.java @@ -0,0 +1,28 @@ +package dream.examples.form; + +import dream.client.Signal; +import dream.client.Var; + +public class Boss extends FormClient { + + private Var eph; + + public Boss() { + super("Boss"); + eph = new Var<>("euro_per_hour", 8.5); + new Signal<>("helper", () -> eph.get(), eph); + } + + @Override + public void typedText(String typedText) { + System.out.println("Boss: \"" + typedText + "\""); + Double value = Double.valueOf(typedText); + System.out.println("Boss: " + value); + eph.set(value); + } + + public static void main(String[] args) { + new Boss(); + } + +} diff --git a/Dream2/src/examples/java/dream/examples/form/FormClient.java b/Dream2/src/examples/java/dream/examples/form/FormClient.java new file mode 100644 index 0000000..8c1cf22 --- /dev/null +++ b/Dream2/src/examples/java/dream/examples/form/FormClient.java @@ -0,0 +1,49 @@ +package dream.examples.form; + +import java.awt.Color; +import java.util.logging.Level; +import java.util.logging.Logger; + +import dream.client.RemoteVar; +import dream.client.Signal; +import dream.common.Consts; + +public abstract class FormClient { + + private RemoteVar salary; + private RemoteVar settings; + private Signal remoteSalary; + private Signal remoteSettings; + private FormGUI gui; + + public FormClient(String name) { + Consts.hostName = name; + + Logger.getGlobal().setLevel(Level.ALL); + + gui = new FormGUI(name); + gui.setListener(this); + + salary = new RemoteVar<>("FormServer", "salary"); + settings = new RemoteVar<>("FormServer", "settingsOkay"); + + remoteSalary = new Signal<>("remoteSalary", () -> { + if (salary != null && salary.get() != null) + return salary.get(); + else + return 0.0; + }, salary); + remoteSettings = new Signal<>("remoteSettings", () -> { + if (settings != null && settings.get() != null) + return settings.get(); + else + return false; + }, settings); + gui.setText(""); + gui.setColor(Color.red); + remoteSalary.change().addHandler((o, n) -> gui.setText(n.toString())); + remoteSettings.change().addHandler((o, n) -> gui.setColor((n ? Color.green : Color.red))); + } + + public abstract void typedText(String typedText); +} diff --git a/Dream2/src/examples/java/dream/examples/form/FormGUI.java b/Dream2/src/examples/java/dream/examples/form/FormGUI.java new file mode 100644 index 0000000..1be8072 --- /dev/null +++ b/Dream2/src/examples/java/dream/examples/form/FormGUI.java @@ -0,0 +1,84 @@ +package dream.examples.form; + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.event.KeyEvent; +import java.awt.event.KeyListener; + +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JTextField; + +public class FormGUI extends JFrame { + + private static final long serialVersionUID = 9205614575242281482L; + private JTextField sendText; + private FormClient listener; + private JLabel display; + + public FormGUI(String name) { + initUI(); + setTitle(name); + } + + private void initUI() { + sendText = new JTextField(20); + sendText.addKeyListener(new KeyListener() { + + @Override + public void keyTyped(KeyEvent e) { + } + + @Override + public void keyReleased(KeyEvent e) { + } + + @Override + public void keyPressed(KeyEvent e) { + if (e.getKeyCode() == KeyEvent.VK_ENTER) + sendText(); + } + }); + JButton sendButton = new JButton("Set"); + sendButton.addActionListener((e) -> sendText()); + + display = new JLabel(""); + display.setPreferredSize(new Dimension(100, 30)); + display.setMinimumSize(new Dimension(100, 30)); + display.setOpaque(true); + BorderLayout lay = new BorderLayout(); + getContentPane().setLayout(lay); + getContentPane().add(sendText, BorderLayout.WEST); + getContentPane().add(sendButton, BorderLayout.EAST); + getContentPane().add(display, BorderLayout.NORTH); + // setSize(300, 200); + setLocationRelativeTo(null); + setDefaultCloseOperation(EXIT_ON_CLOSE); + + pack(); + setVisible(true); + } + + private void sendText() { + listener.typedText(getTypedText()); + } + + public void setListener(FormClient c) { + listener = c; + } + + public String getTypedText() { + return sendText.getText(); + } + + public void setText(String text) { + display.setText(text); + } + + public void setColor(Color bg) { + display.setBackground(bg); + } + +} diff --git a/Dream2/src/examples/java/dream/examples/form/FormServer.java b/Dream2/src/examples/java/dream/examples/form/FormServer.java new file mode 100644 index 0000000..791863c --- /dev/null +++ b/Dream2/src/examples/java/dream/examples/form/FormServer.java @@ -0,0 +1,136 @@ +package dream.examples.form; + +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; + +import dream.client.DreamClient; +import dream.client.RemoteVar; +import dream.client.Signal; +import dream.client.Var; +import dream.common.Consts; +import dream.locking.LockManagerLauncher; +import dream.server.ServerLauncher; + +public class FormServer { + + public static final String NAME = "FormServer"; + private boolean serverStarted; + private boolean lockManagerStarted; + private final Logger logger = Logger.getGlobal();// Logger("FormServer"); + + private RemoteVar working_hours; + private RemoteVar euro_per_hour; + private Signal minimumHours; + private Signal maximumHours; + private Signal minimumEuroPerHour; + private Signal salary; + private Signal settingsOkay; + + public FormServer() { + startServerIfNeeded(); + startLockManagerIfNeeded(); + + logger.setLevel(Level.ALL); + // logger.addHandler(Logger.getGlobal().getHandlers()[0]); + Consts.hostName = NAME; + new Var<>("helper", ""); + detectNewSession(); + } + + /** + * Look for new clients every 5 seconds + */ + private void detectNewSession() { + Set vars = DreamClient.instance.listVariables(); + for (String str : vars) { + String host = str.split("@")[1]; + String var = str.split("@")[0]; + if (working_hours == null && var.equalsIgnoreCase("working_hours")) { + working_hours = new RemoteVar<>(host, var); + System.out.println("Found Secreatary"); + updateDependencies(); + } + if (euro_per_hour == null && var.equalsIgnoreCase("euro_per_hour")) { + euro_per_hour = new RemoteVar<>(host, var); + System.out.println("Found Boss"); + updateDependencies(); + } + } + try { + Thread.sleep(500); + } catch (InterruptedException e) { + logger.log(Level.SEVERE, "Failed to sleep for 0.5 seconds", e); + } + + detectNewSession(); + } + + private void updateDependencies() { + if (euro_per_hour == null || working_hours == null) + return; + + System.out.println("update Dep"); + + minimumHours = new Signal<>("minimumHours", () -> { + if (working_hours.get() != null) + return working_hours.get() > 10; + else + return false; + }, working_hours); + maximumHours = new Signal<>("maximumHours", () -> { + if (working_hours.get() != null) + return working_hours.get() < 60; + else + return false; + }, working_hours); + minimumEuroPerHour = new Signal<>("minimumEuroPerHour", () -> { + if (euro_per_hour.get() != null) + return euro_per_hour.get() > 10; + else + return false; + }, working_hours); + settingsOkay = new Signal<>("settingsOkay", () -> { + if (minimumHours.get() != null && maximumHours.get() != null && minimumEuroPerHour.get() != null) + return minimumHours.get() && maximumHours.get() && minimumEuroPerHour.get(); + else + return false; + }, minimumHours, maximumHours, minimumEuroPerHour); + salary = new Signal<>("salary", () -> { + if (working_hours.get() != null && euro_per_hour.get() != null) + return working_hours.get() * euro_per_hour.get(); + else + return 0.0; + }, working_hours, euro_per_hour); + + System.out.println("update Dep finished"); + } + + public static void main(String[] args) { + new FormServer(); + } + + private final void startServerIfNeeded() { + if (!serverStarted) { + ServerLauncher.start(); + serverStarted = true; + } + try { + Thread.sleep(500); + } catch (InterruptedException e) { + logger.log(Level.SEVERE, "Failed to wait for Server starting", e); + } + } + + private final void startLockManagerIfNeeded() { + if (!lockManagerStarted) { + LockManagerLauncher.start(); + lockManagerStarted = true; + } + try { + Thread.sleep(500); + } catch (final InterruptedException e) { + logger.log(Level.SEVERE, "Failed to wait for LockManager starting", e); + } + } +} diff --git a/Dream2/src/examples/java/dream/examples/form/Secretary.java b/Dream2/src/examples/java/dream/examples/form/Secretary.java new file mode 100644 index 0000000..b849054 --- /dev/null +++ b/Dream2/src/examples/java/dream/examples/form/Secretary.java @@ -0,0 +1,28 @@ +package dream.examples.form; + +import dream.client.Signal; +import dream.client.Var; + +public class Secretary extends FormClient { + + private Var wh; + + public Secretary() { + super("Secretary"); + wh = new Var<>("working_hours", 5); + new Signal<>("helper", () -> wh.get(), wh); + } + + @Override + public void typedText(String typedText) { + System.out.println("Secretary: \"" + typedText + "\""); + Integer value = Integer.valueOf(typedText); + System.out.println("Secretary: " + value); + wh.set(value); + } + + public static void main(String[] args) { + new Secretary(); + } + +} From 3ef5797a0180137852732df86ddc9f9628872bc7 Mon Sep 17 00:00:00 2001 From: rkamath3 Date: Fri, 6 May 2016 14:39:29 +0200 Subject: [PATCH 053/161] Added an initial delay --- .../src/examples/java/dream/examples/biginer/ConsumerApp.java | 2 +- .../src/examples/java/dream/examples/biginer/ProducerApp.java | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Dream2/src/examples/java/dream/examples/biginer/ConsumerApp.java b/Dream2/src/examples/java/dream/examples/biginer/ConsumerApp.java index 020ce43..e490ae4 100644 --- a/Dream2/src/examples/java/dream/examples/biginer/ConsumerApp.java +++ b/Dream2/src/examples/java/dream/examples/biginer/ConsumerApp.java @@ -15,7 +15,7 @@ public ConsumerApp() throws Exception { // Register a Subscription RemoteVar rv = new RemoteVar("Host1", "exVar"); - + System.out.println("Consumer has started\n Please wait..initial communication may take upto 10 seconds"); // On every change in remote variable rv create a signal which could // trigger appropriate action Signal s = new Signal("s", () -> { diff --git a/Dream2/src/examples/java/dream/examples/biginer/ProducerApp.java b/Dream2/src/examples/java/dream/examples/biginer/ProducerApp.java index 0798a55..aa54c4f 100644 --- a/Dream2/src/examples/java/dream/examples/biginer/ProducerApp.java +++ b/Dream2/src/examples/java/dream/examples/biginer/ProducerApp.java @@ -15,9 +15,11 @@ public ProducerApp() throws Exception { // myVar is created and registered as exVar for remote consumption. // exVar is initialized to AAA Var myVar = new Var("exVar", "AAA"); + System.out.println("Producer has started\n Please wait..initial communication may take upto 10 seconds"); int iteration = 10; + Thread.sleep(10000); while (iteration > 0) { - Thread.sleep(1000); + Thread.sleep(100); // change value of exVar myVar.set("Value-" + (10 - iteration) + ""); System.out.println("Changed myvar " + myVar.get()); From 7d438fc8da63ce02c9a2aca6b2a7b5156a2d2f9b Mon Sep 17 00:00:00 2001 From: rkamath3 Date: Fri, 6 May 2016 15:15:41 +0200 Subject: [PATCH 054/161] Added an entrypoint for example moved jvm creator method to an utility class --- .../dream/examples/biginer/AppEntryPoint.java | 49 ++++++++++++++++++ .../dream/examples/biginer/ProducerApp.java | 2 +- .../dream/examples/util/NewJvmHelper.java | 50 +++++++++++++++++++ 3 files changed, 100 insertions(+), 1 deletion(-) create mode 100644 Dream2/src/examples/java/dream/examples/biginer/AppEntryPoint.java create mode 100644 Dream2/src/examples/java/dream/examples/util/NewJvmHelper.java diff --git a/Dream2/src/examples/java/dream/examples/biginer/AppEntryPoint.java b/Dream2/src/examples/java/dream/examples/biginer/AppEntryPoint.java new file mode 100644 index 0000000..e3265ab --- /dev/null +++ b/Dream2/src/examples/java/dream/examples/biginer/AppEntryPoint.java @@ -0,0 +1,49 @@ +/** + * + */ +package dream.examples.biginer; + +import dream.examples.util.NewJvmHelper; + +/** + * @author Ram + * + */ +public class AppEntryPoint { + public static void main(String args[]) { + Process infra = null; + Process producer = null; + Process consumer = null; + try { + // Init the infra + infra = new NewJvmHelper().startNewJVM(StartInfra.class); + + // Start Producer + producer = new NewJvmHelper().startNewJVM(ProducerApp.class); + + // Start Consumer + consumer = new NewJvmHelper().startNewJVM(ConsumerApp.class); + + Thread.sleep(5000); + + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + /** + * All of the JVMs should be closed before exit + */ + finally { + if (infra != null) { + infra.destroyForcibly(); + } + if (producer != null) { + producer.destroyForcibly(); + } + if (consumer != null) { + consumer.destroyForcibly(); + } + } + + } +} diff --git a/Dream2/src/examples/java/dream/examples/biginer/ProducerApp.java b/Dream2/src/examples/java/dream/examples/biginer/ProducerApp.java index aa54c4f..16f743d 100644 --- a/Dream2/src/examples/java/dream/examples/biginer/ProducerApp.java +++ b/Dream2/src/examples/java/dream/examples/biginer/ProducerApp.java @@ -17,7 +17,7 @@ public ProducerApp() throws Exception { Var myVar = new Var("exVar", "AAA"); System.out.println("Producer has started\n Please wait..initial communication may take upto 10 seconds"); int iteration = 10; - Thread.sleep(10000); + Thread.sleep(3000); while (iteration > 0) { Thread.sleep(100); // change value of exVar diff --git a/Dream2/src/examples/java/dream/examples/util/NewJvmHelper.java b/Dream2/src/examples/java/dream/examples/util/NewJvmHelper.java new file mode 100644 index 0000000..241f54c --- /dev/null +++ b/Dream2/src/examples/java/dream/examples/util/NewJvmHelper.java @@ -0,0 +1,50 @@ +/** + * + */ +package dream.examples.util; + +import java.io.IOException; +import java.util.concurrent.TimeUnit; + +/** + * @author Ram + * + */ +public class NewJvmHelper { + /** + * + * @param c + * class name which needs to be executed in new JVM + * @param args + * arguments which needs to be passed as a run time argument to + * the JVM + * @return process, which needs to be cleaned by the class which initializes + * the new JVM. + */ + public Process startNewJVM(Class c, String... args) { + System.out.println("Starting " + c.getName() + " ..."); + String separator = System.getProperty("file.separator"); + String classpath = System.getProperty("java.class.path"); + String path = System.getProperty("java.home") + separator + "bin" + separator + "java"; + String[] arguments = new String[args.length + 4]; + arguments[0] = path; + arguments[1] = "-cp"; + arguments[2] = classpath; + arguments[3] = c.getName(); + for (int i = 0; i < args.length; i++) { + arguments[i + 4] = args[i]; + } + ProcessBuilder processBuilder = new ProcessBuilder(arguments).inheritIO(); + Process process = null; + try { + process = processBuilder.start(); + process.waitFor(1, TimeUnit.SECONDS); + } catch (IOException e) { + e.printStackTrace(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + System.out.println(c.getName() + " started!"); + return process; + } +} From 9344a6ff50c988f7d770af96ef070845790d3b5d Mon Sep 17 00:00:00 2001 From: rkamath3 Date: Fri, 6 May 2016 15:57:42 +0200 Subject: [PATCH 055/161] Skeleton for task, needs to be updated with worker and master process logic --- .../dream/examples/tasks/MasterProcess.java | 20 +++++ .../java/dream/examples/tasks/Task.java | 78 +++++++++++++++++++ .../dream/examples/tasks/WorkerProcess.java | 43 ++++++++++ 3 files changed, 141 insertions(+) create mode 100644 Dream2/src/examples/java/dream/examples/tasks/MasterProcess.java create mode 100644 Dream2/src/examples/java/dream/examples/tasks/Task.java create mode 100644 Dream2/src/examples/java/dream/examples/tasks/WorkerProcess.java diff --git a/Dream2/src/examples/java/dream/examples/tasks/MasterProcess.java b/Dream2/src/examples/java/dream/examples/tasks/MasterProcess.java new file mode 100644 index 0000000..0ba83ee --- /dev/null +++ b/Dream2/src/examples/java/dream/examples/tasks/MasterProcess.java @@ -0,0 +1,20 @@ +/** + * + */ +package dream.examples.tasks; + +/** + * @author Ram + * + */ +public class MasterProcess { + + /** + * @param args + */ + public static void main(String[] args) { + // TODO Auto-generated method stub + + } + +} diff --git a/Dream2/src/examples/java/dream/examples/tasks/Task.java b/Dream2/src/examples/java/dream/examples/tasks/Task.java new file mode 100644 index 0000000..8196faa --- /dev/null +++ b/Dream2/src/examples/java/dream/examples/tasks/Task.java @@ -0,0 +1,78 @@ +/** + * + */ +package dream.examples.tasks; + +import java.util.ArrayList; + +/** + * @author Ram + * + */ +public class Task { + private String name; + private String assignee; + private String parentTask; + private ArrayList subTasks; + + /** + * @return the name + */ + public String getName() { + return name; + } + + /** + * @param name + * the name to set + */ + public void setName(String name) { + this.name = name; + } + + /** + * @return the assignee + */ + public String getAssignee() { + return assignee; + } + + /** + * @param assignee + * the assignee to set + */ + public void setAssignee(String assignee) { + this.assignee = assignee; + } + + /** + * @return the parentTask + */ + public String getParentTask() { + return parentTask; + } + + /** + * @param parentTask + * the parentTask to set + */ + public void setParentTask(String parentTask) { + this.parentTask = parentTask; + } + + /** + * @return the subTasks + */ + public ArrayList getSubTasks() { + return subTasks; + } + + /** + * @param subTasks + * the subTasks to set + */ + public void setSubTasks(ArrayList subTasks) { + this.subTasks = subTasks; + } + +} \ No newline at end of file diff --git a/Dream2/src/examples/java/dream/examples/tasks/WorkerProcess.java b/Dream2/src/examples/java/dream/examples/tasks/WorkerProcess.java new file mode 100644 index 0000000..c7f554c --- /dev/null +++ b/Dream2/src/examples/java/dream/examples/tasks/WorkerProcess.java @@ -0,0 +1,43 @@ +/** + * + */ +package dream.examples.tasks; + +/** + * @author Ram + * + */ +public class WorkerProcess { + /** + * @param args + */ + private String processName; + + /** + * @return the processName + */ + public String getProcessName() { + return processName; + } + + /** + * @param processName + * the processName to set + */ + public void setProcessName(String processName) { + this.processName = processName; + } + + public WorkerProcess(String processName, String host) { + this.setProcessName(processName); + } + + public static void main(String[] args) { + + if (args.length < 2) { + System.out.println("Usage WorkerProcess \n Example : WorkerProcess \"worker1\" \"Host1\""); + } + new WorkerProcess(args[0], args[1]); + } + +} From 6bb0601c21e713efe666297e8a7a2e6acc31041d Mon Sep 17 00:00:00 2001 From: Alessandro Margara Date: Sun, 8 May 2016 12:54:40 +0200 Subject: [PATCH 056/161] New (client) nodes that join the network now receive a copy of all the advertisements. --- .../java/dream/examples/form/Boss.java | 3 +- .../java/dream/examples/form/FormClient.java | 19 +++++- .../java/dream/examples/form/FormServer.java | 60 ++++++++----------- .../java/dream/examples/form/Secretary.java | 3 +- .../java/dream/server/AdvertisementTable.java | 5 ++ .../dream/server/ServerEventForwarder.java | 22 ++++++- .../java/dream/server/ServerLauncher.java | 2 +- 7 files changed, 73 insertions(+), 41 deletions(-) diff --git a/Dream2/src/examples/java/dream/examples/form/Boss.java b/Dream2/src/examples/java/dream/examples/form/Boss.java index cc982e8..70b699f 100644 --- a/Dream2/src/examples/java/dream/examples/form/Boss.java +++ b/Dream2/src/examples/java/dream/examples/form/Boss.java @@ -22,7 +22,8 @@ public void typedText(String typedText) { } public static void main(String[] args) { - new Boss(); + Boss b = new Boss(); + b.init("Boss"); } } diff --git a/Dream2/src/examples/java/dream/examples/form/FormClient.java b/Dream2/src/examples/java/dream/examples/form/FormClient.java index 8c1cf22..6b61a8a 100644 --- a/Dream2/src/examples/java/dream/examples/form/FormClient.java +++ b/Dream2/src/examples/java/dream/examples/form/FormClient.java @@ -4,6 +4,7 @@ import java.util.logging.Level; import java.util.logging.Logger; +import dream.client.DreamClient; import dream.client.RemoteVar; import dream.client.Signal; import dream.common.Consts; @@ -18,8 +19,18 @@ public abstract class FormClient { public FormClient(String name) { Consts.hostName = name; - Logger.getGlobal().setLevel(Level.ALL); + } + + protected void init(String name) { + while (!DreamClient.instance.listVariables().contains("salary@FormServer") || // + !DreamClient.instance.listVariables().contains("settingsOkay@FormServer")) { + try { + Thread.sleep(500); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } gui = new FormGUI(name); gui.setListener(this); @@ -28,17 +39,19 @@ public FormClient(String name) { settings = new RemoteVar<>("FormServer", "settingsOkay"); remoteSalary = new Signal<>("remoteSalary", () -> { - if (salary != null && salary.get() != null) + if (salary.get() != null) return salary.get(); else return 0.0; }, salary); + remoteSettings = new Signal<>("remoteSettings", () -> { - if (settings != null && settings.get() != null) + if (settings.get() != null) return settings.get(); else return false; }, settings); + gui.setText(""); gui.setColor(Color.red); remoteSalary.change().addHandler((o, n) -> gui.setText(n.toString())); diff --git a/Dream2/src/examples/java/dream/examples/form/FormServer.java b/Dream2/src/examples/java/dream/examples/form/FormServer.java index 791863c..1656932 100644 --- a/Dream2/src/examples/java/dream/examples/form/FormServer.java +++ b/Dream2/src/examples/java/dream/examples/form/FormServer.java @@ -1,6 +1,5 @@ package dream.examples.form; -import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; @@ -21,11 +20,6 @@ public class FormServer { private RemoteVar working_hours; private RemoteVar euro_per_hour; - private Signal minimumHours; - private Signal maximumHours; - private Signal minimumEuroPerHour; - private Signal salary; - private Signal settingsOkay; public FormServer() { startServerIfNeeded(); @@ -42,61 +36,59 @@ public FormServer() { * Look for new clients every 5 seconds */ private void detectNewSession() { - Set vars = DreamClient.instance.listVariables(); - for (String str : vars) { - String host = str.split("@")[1]; - String var = str.split("@")[0]; - if (working_hours == null && var.equalsIgnoreCase("working_hours")) { - working_hours = new RemoteVar<>(host, var); - System.out.println("Found Secreatary"); - updateDependencies(); + while (euro_per_hour == null || working_hours == null) { + for (String str : DreamClient.instance.listVariables()) { + String host = str.split("@")[1]; + String var = str.split("@")[0]; + if (working_hours == null && var.equalsIgnoreCase("working_hours")) { + working_hours = new RemoteVar<>(host, var); + System.out.println("Found Secreatary"); + } else if (euro_per_hour == null && var.equalsIgnoreCase("euro_per_hour")) { + euro_per_hour = new RemoteVar<>(host, var); + System.out.println("Found Boss"); + } } - if (euro_per_hour == null && var.equalsIgnoreCase("euro_per_hour")) { - euro_per_hour = new RemoteVar<>(host, var); - System.out.println("Found Boss"); - updateDependencies(); + try { + Thread.sleep(500); + } catch (InterruptedException e) { + logger.log(Level.SEVERE, "Failed to sleep for 0.5 seconds", e); } } - try { - Thread.sleep(500); - } catch (InterruptedException e) { - logger.log(Level.SEVERE, "Failed to sleep for 0.5 seconds", e); - } - - detectNewSession(); + updateDependencies(); } private void updateDependencies() { - if (euro_per_hour == null || working_hours == null) - return; - System.out.println("update Dep"); - minimumHours = new Signal<>("minimumHours", () -> { + final Signal minimumHours = new Signal<>("minimumHours", () -> { if (working_hours.get() != null) return working_hours.get() > 10; else return false; }, working_hours); - maximumHours = new Signal<>("maximumHours", () -> { + + final Signal maximumHours = new Signal<>("maximumHours", () -> { if (working_hours.get() != null) return working_hours.get() < 60; else return false; }, working_hours); - minimumEuroPerHour = new Signal<>("minimumEuroPerHour", () -> { + + final Signal minimumEuroPerHour = new Signal<>("minimumEuroPerHour", () -> { if (euro_per_hour.get() != null) return euro_per_hour.get() > 10; else return false; - }, working_hours); - settingsOkay = new Signal<>("settingsOkay", () -> { + }, euro_per_hour); + + new Signal<>("settingsOkay", () -> { if (minimumHours.get() != null && maximumHours.get() != null && minimumEuroPerHour.get() != null) return minimumHours.get() && maximumHours.get() && minimumEuroPerHour.get(); else return false; }, minimumHours, maximumHours, minimumEuroPerHour); - salary = new Signal<>("salary", () -> { + + new Signal<>("salary", () -> { if (working_hours.get() != null && euro_per_hour.get() != null) return working_hours.get() * euro_per_hour.get(); else diff --git a/Dream2/src/examples/java/dream/examples/form/Secretary.java b/Dream2/src/examples/java/dream/examples/form/Secretary.java index b849054..5844ef9 100644 --- a/Dream2/src/examples/java/dream/examples/form/Secretary.java +++ b/Dream2/src/examples/java/dream/examples/form/Secretary.java @@ -22,7 +22,8 @@ public void typedText(String typedText) { } public static void main(String[] args) { - new Secretary(); + Secretary s = new Secretary(); + s.init("Secretary"); } } diff --git a/Dream2/src/main/java/dream/server/AdvertisementTable.java b/Dream2/src/main/java/dream/server/AdvertisementTable.java index fbd83af..3d41140 100755 --- a/Dream2/src/main/java/dream/server/AdvertisementTable.java +++ b/Dream2/src/main/java/dream/server/AdvertisementTable.java @@ -3,6 +3,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; +import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.function.Predicate; @@ -47,4 +48,8 @@ final void removeAllAdvertisementsFor(NodeDescriptor node) { advs.remove(node); } + final Set getAllAdvertisements() { + return advs.values().stream().collect(HashSet::new, HashSet::addAll, HashSet::addAll); + } + } diff --git a/Dream2/src/main/java/dream/server/ServerEventForwarder.java b/Dream2/src/main/java/dream/server/ServerEventForwarder.java index 1c92995..024b0e7 100755 --- a/Dream2/src/main/java/dream/server/ServerEventForwarder.java +++ b/Dream2/src/main/java/dream/server/ServerEventForwarder.java @@ -1,5 +1,6 @@ package dream.server; +import java.io.IOException; import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; @@ -10,8 +11,11 @@ import dream.common.packets.AdvertisementPacket; import dream.common.packets.EventPacket; import dream.common.packets.SubscriptionPacket; +import dream.common.packets.content.AdvType; import polimi.reds.NodeDescriptor; import polimi.reds.broker.overlay.NeighborhoodChangeListener; +import polimi.reds.broker.overlay.NotRunningException; +import polimi.reds.broker.overlay.Overlay; import polimi.reds.broker.routing.Outbox; import polimi.reds.broker.routing.PacketForwarder; @@ -22,6 +26,12 @@ public class ServerEventForwarder implements PacketForwarder, NeighborhoodChange protected final SubscriptionTable brokersSubTable = new SubscriptionTable(); protected final AdvertisementTable advTable = new AdvertisementTable(); + private final Overlay overlay; + + public ServerEventForwarder(final Overlay overlay) { + this.overlay = overlay; + } + @Override public Collection forwardPacket(String subject, NodeDescriptor sender, Serializable packet, Collection neighbors, Outbox outbox) { @@ -116,7 +126,17 @@ private final void reactToRemovedNeighbor(NodeDescriptor node) { @Override public final void notifyNeighborAdded(NodeDescriptor node) { - // Nothing to do + if (node.isClient()) { + advTable.getAllAdvertisements().stream() // + .map(adv -> new AdvertisementPacket(adv, AdvType.ADV, true)) // + .forEach(advPkt -> { + try { + overlay.send(AdvertisementPacket.subject, advPkt, node); + } catch (IOException | NotRunningException e) { + e.printStackTrace(); + } + }); + } } @Override diff --git a/Dream2/src/main/java/dream/server/ServerLauncher.java b/Dream2/src/main/java/dream/server/ServerLauncher.java index bde0757..40927be 100755 --- a/Dream2/src/main/java/dream/server/ServerLauncher.java +++ b/Dream2/src/main/java/dream/server/ServerLauncher.java @@ -30,7 +30,7 @@ private ServerLauncher() { final TopologyManager tm = new SimpleTopologyManager(); overlay = new GenericOverlay(tm, tr); final GenericRouter router = new GenericRouter(overlay); - final ServerEventForwarder forwarder = new ServerEventForwarder(); + final ServerEventForwarder forwarder = new ServerEventForwarder(overlay); overlay.addNeighborhoodChangeListener(forwarder); router.setPacketForwarder(EventPacket.subject, forwarder); router.setPacketForwarder(SubscriptionPacket.subject, forwarder); From 0c7935b3c07bd4382420149ae55a589ce49dc82e Mon Sep 17 00:00:00 2001 From: Alessandro Margara Date: Sun, 8 May 2016 13:35:39 +0200 Subject: [PATCH 057/161] Fixed previous code to send advertisements to clients --- .../dream/client/ClientEventForwarder.java | 32 +++++++++---------- .../utils/InterSourceDependencyDetector.java | 2 +- .../dream/server/ServerEventForwarder.java | 21 ++++++------ 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/Dream2/src/main/java/dream/client/ClientEventForwarder.java b/Dream2/src/main/java/dream/client/ClientEventForwarder.java index 0be8dd5..c02b8c3 100755 --- a/Dream2/src/main/java/dream/client/ClientEventForwarder.java +++ b/Dream2/src/main/java/dream/client/ClientEventForwarder.java @@ -44,9 +44,9 @@ class ClientEventForwarder implements PacketForwarder { // Dependency detectors private final IntraSourceDependencyDetector intraDepDetector = IntraSourceDependencyDetector.instance; private final InterSourceDependencyDetector interDepDetector = // - Consts.consistencyType == ConsistencyType.ATOMIC // - ? new AtomicDependencyDetector() // - : new CompleteGlitchFreeDependencyDetector(); + Consts.consistencyType == ConsistencyType.ATOMIC // + ? new AtomicDependencyDetector() // + : new CompleteGlitchFreeDependencyDetector(); private final FinalNodesDetector finalNodesDetector = new FinalNodesDetector(); // Lock applicants waiting for a grant @@ -82,8 +82,8 @@ private ClientEventForwarder() { } @Override - public Collection forwardPacket(String subject, NodeDescriptor sender, Serializable packet, - Collection neighbors, Outbox outbox) { + public synchronized Collection forwardPacket(String subject, NodeDescriptor sender, + Serializable packet, Collection neighbors, Outbox outbox) { final Collection result = new ArrayList(); if (subject.equals(AdvertisementPacket.subject)) { assert packet instanceof AdvertisementPacket; @@ -107,7 +107,7 @@ public Collection forwardPacket(String subject, NodeDescriptor s return result; } - final void sendEvent(UUID id, Event ev, String initialVar) { + final synchronized void sendEvent(UUID id, Event ev, String initialVar) { logger.finer("Sending an event " + ev); Set lockReleaseNodes; switch (Consts.consistencyType) { @@ -129,7 +129,7 @@ final void sendEvent(UUID id, Event ev, String initialVar) { /** * Return false if the lock request is not needed */ - final void sendReadOnlyLockRequest(String node, LockApplicant applicant) { + final synchronized void sendReadOnlyLockRequest(String node, LockApplicant applicant) { if (Consts.consistencyType != ConsistencyType.ATOMIC) { assert false : Consts.consistencyType; logger.warning("Invoked sendReadOnlyLockRequest() even if the consistency level does not require it."); @@ -151,7 +151,7 @@ final void sendReadOnlyLockRequest(String node, LockApplicant applicant) { /** * Return false if the lock request is not needed */ - final boolean sendReadWriteLockRequest(String source, LockApplicant applicant) { + final synchronized boolean sendReadWriteLockRequest(String source, LockApplicant applicant) { if (Consts.consistencyType != ConsistencyType.COMPLETE_GLITCH_FREE && // Consts.consistencyType != ConsistencyType.ATOMIC) { assert false : Consts.consistencyType; @@ -176,7 +176,7 @@ final boolean sendReadWriteLockRequest(String source, LockApplicant applicant) { return true; } - final Set getLockReleaseNodesFor(String source) { + final synchronized Set getLockReleaseNodesFor(String source) { switch (Consts.consistencyType) { case COMPLETE_GLITCH_FREE: return interDepDetector.getNodesToLockFor(source); @@ -187,7 +187,7 @@ final Set getLockReleaseNodesFor(String source) { } } - final void sendLockRelease(UUID lockID) { + final synchronized void sendLockRelease(UUID lockID) { if (Consts.consistencyType != ConsistencyType.COMPLETE_GLITCH_FREE && // Consts.consistencyType != ConsistencyType.ATOMIC) { assert false : Consts.consistencyType; @@ -198,7 +198,7 @@ final void sendLockRelease(UUID lockID) { connectionManager.sendLockRelease(new LockReleasePacket(lockID)); } - final void advertise(Advertisement adv, boolean isPublic) { + final synchronized void advertise(Advertisement adv, boolean isPublic) { logger.fine("Sending advertisement " + adv); if (Consts.consistencyType == ConsistencyType.SINGLE_SOURCE_GLITCH_FREE || // Consts.consistencyType == ConsistencyType.COMPLETE_GLITCH_FREE || // @@ -209,7 +209,7 @@ final void advertise(Advertisement adv, boolean isPublic) { connectionManager.sendAdvertisement(adv, isPublic); } - final void unadvertise(Advertisement adv, boolean isPublic) { + final synchronized void unadvertise(Advertisement adv, boolean isPublic) { logger.fine("Sending unadvertisement " + adv); if (Consts.consistencyType == ConsistencyType.SINGLE_SOURCE_GLITCH_FREE || // Consts.consistencyType == ConsistencyType.COMPLETE_GLITCH_FREE || // @@ -220,7 +220,7 @@ final void unadvertise(Advertisement adv, boolean isPublic) { connectionManager.sendUnadvertisement(adv, isPublic); } - final void advertise(Advertisement adv, Set> subs, boolean isPublic) { + final synchronized void advertise(Advertisement adv, Set> subs, boolean isPublic) { logger.fine("Sending advertisement " + adv + " with subscriptions " + subs); if (Consts.consistencyType == ConsistencyType.SINGLE_SOURCE_GLITCH_FREE || // Consts.consistencyType == ConsistencyType.COMPLETE_GLITCH_FREE || // @@ -231,7 +231,7 @@ final void advertise(Advertisement adv, Set> subs, boolean isPub connectionManager.sendAdvertisement(adv, subs, isPublic); } - final void unadvertise(Advertisement adv, Set> subs, boolean isPublic) { + final synchronized void unadvertise(Advertisement adv, Set> subs, boolean isPublic) { logger.fine("Sending unadvertisement " + adv + " with subscriptions " + subs); if (Consts.consistencyType == ConsistencyType.SINGLE_SOURCE_GLITCH_FREE || // Consts.consistencyType == ConsistencyType.COMPLETE_GLITCH_FREE || // @@ -242,7 +242,7 @@ final void unadvertise(Advertisement adv, Set> subs, boolean isP connectionManager.sendUnadvertisement(adv, isPublic); } - final void addSubscription(Subscriber subscriber, Subscription subscription) { + final synchronized void addSubscription(Subscriber subscriber, Subscription subscription) { logger.fine("Adding subscription " + subscription); subTable.addSubscription(subscriber, subscription); if (needToSendToServer(subscription)) { @@ -250,7 +250,7 @@ final void addSubscription(Subscriber subscriber, Subscription subscription) } } - final void removeSubscription(Subscriber subscriber, Subscription subscription) { + final synchronized void removeSubscription(Subscriber subscriber, Subscription subscription) { logger.fine("Removing subscription " + subscription); subTable.addSubscription(subscriber, subscription); if (needToSendToServer(subscription)) { diff --git a/Dream2/src/main/java/dream/common/utils/InterSourceDependencyDetector.java b/Dream2/src/main/java/dream/common/utils/InterSourceDependencyDetector.java index ba7d68f..be4aa7f 100644 --- a/Dream2/src/main/java/dream/common/utils/InterSourceDependencyDetector.java +++ b/Dream2/src/main/java/dream/common/utils/InterSourceDependencyDetector.java @@ -6,7 +6,7 @@ /** * An InterSourceDependencyDetector is a dependency detector used to detect - * dependencies in the propagations from starting from different sources. + * dependencies in propagations starting from different sources. * * For each source, the detector returns the nodes that require to be locked * during the propagation. diff --git a/Dream2/src/main/java/dream/server/ServerEventForwarder.java b/Dream2/src/main/java/dream/server/ServerEventForwarder.java index 024b0e7..f75ed9c 100755 --- a/Dream2/src/main/java/dream/server/ServerEventForwarder.java +++ b/Dream2/src/main/java/dream/server/ServerEventForwarder.java @@ -4,6 +4,7 @@ import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; +import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.logging.Logger; @@ -11,7 +12,6 @@ import dream.common.packets.AdvertisementPacket; import dream.common.packets.EventPacket; import dream.common.packets.SubscriptionPacket; -import dream.common.packets.content.AdvType; import polimi.reds.NodeDescriptor; import polimi.reds.broker.overlay.NeighborhoodChangeListener; import polimi.reds.broker.overlay.NotRunningException; @@ -26,6 +26,7 @@ public class ServerEventForwarder implements PacketForwarder, NeighborhoodChange protected final SubscriptionTable brokersSubTable = new SubscriptionTable(); protected final AdvertisementTable advTable = new AdvertisementTable(); + private final Set allValidAdvertisements = new HashSet<>(); private final Overlay overlay; public ServerEventForwarder(final Overlay overlay) { @@ -93,9 +94,11 @@ private final void processAdvertisement(NodeDescriptor sender, AdvertisementPack if (packet.isPublic()) { switch (packet.getAdvType()) { case ADV: + allValidAdvertisements.add(packet); advTable.addAdvertisement(sender, packet.getAdvertisement()); break; case UNADV: + allValidAdvertisements.removeIf(p -> p.getAdvertisement().equals(packet.getAdvertisement())); advTable.removeAdvertisement(sender, packet.getAdvertisement()); break; } @@ -127,15 +130,13 @@ private final void reactToRemovedNeighbor(NodeDescriptor node) { @Override public final void notifyNeighborAdded(NodeDescriptor node) { if (node.isClient()) { - advTable.getAllAdvertisements().stream() // - .map(adv -> new AdvertisementPacket(adv, AdvType.ADV, true)) // - .forEach(advPkt -> { - try { - overlay.send(AdvertisementPacket.subject, advPkt, node); - } catch (IOException | NotRunningException e) { - e.printStackTrace(); - } - }); + allValidAdvertisements.forEach(advPkt -> { + try { + overlay.send(AdvertisementPacket.subject, advPkt, node); + } catch (IOException | NotRunningException e) { + e.printStackTrace(); + } + }); } } From e59397adb11e3ec3a3b6a634e206b7d87ada4e8b Mon Sep 17 00:00:00 2001 From: Alessandro Margara Date: Sun, 8 May 2016 14:35:57 +0200 Subject: [PATCH 058/161] Code to (read) lock multiple nodes in atomic consistency. Not tested. --- .../dream/client/ClientEventForwarder.java | 9 +--- .../main/java/dream/client/DreamClient.java | 43 +++++++++++++++- .../src/main/java/dream/client/LockToken.java | 25 ++++++++++ Dream2/src/main/java/dream/client/Signal.java | 50 ++----------------- 4 files changed, 74 insertions(+), 53 deletions(-) create mode 100644 Dream2/src/main/java/dream/client/LockToken.java diff --git a/Dream2/src/main/java/dream/client/ClientEventForwarder.java b/Dream2/src/main/java/dream/client/ClientEventForwarder.java index c02b8c3..8ab0cfd 100755 --- a/Dream2/src/main/java/dream/client/ClientEventForwarder.java +++ b/Dream2/src/main/java/dream/client/ClientEventForwarder.java @@ -126,19 +126,14 @@ final synchronized void sendEvent(UUID id, Event ev, String initialVar) { } } - /** - * Return false if the lock request is not needed - */ - final synchronized void sendReadOnlyLockRequest(String node, LockApplicant applicant) { + final synchronized void sendReadOnlyLockRequest(Set nodesToLock, LockApplicant applicant) { if (Consts.consistencyType != ConsistencyType.ATOMIC) { assert false : Consts.consistencyType; logger.warning("Invoked sendReadOnlyLockRequest() even if the consistency level does not require it."); return; } - logger.finer("Invoked sendReadOnlyLockRequest for node " + node); - final Set nodesToLock = new HashSet<>(); - nodesToLock.add(node); + logger.finer("Invoked sendReadOnlyLockRequest for nodes " + nodesToLock); final LockRequestPacket reqPkt = new LockRequestPacket(connectionManager.getNodeDescriptor(), nodesToLock, nodesToLock, LockType.READ_ONLY); diff --git a/Dream2/src/main/java/dream/client/DreamClient.java b/Dream2/src/main/java/dream/client/DreamClient.java index 6a6e8e3..fe8536f 100644 --- a/Dream2/src/main/java/dream/client/DreamClient.java +++ b/Dream2/src/main/java/dream/client/DreamClient.java @@ -13,9 +13,10 @@ public enum DreamClient { instance; private final DependencyGraph depGraph = DependencyGraph.instance; + private ClientEventForwarder eventForwarder; public final void connect() { - ClientEventForwarder.get(); + eventForwarder = ClientEventForwarder.get(); } public final Set listVariables() { @@ -25,4 +26,44 @@ public final Set listVariables() { return result; } + public final LockToken readLock(Set varsToLock) { + LockToken token = new LockToken(varsToLock.size()); + Lock lock = new Lock(); + eventForwarder.sendReadOnlyLockRequest(varsToLock, grant -> { + synchronized (lock) { + lock.unlock(); + token.setLockId(grant.getLockID()); + lock.notifyAll(); + } + }); + synchronized (lock) { + while (!lock.isUnlocked()) { + try { + lock.wait(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + return token; + } + + public final void unlock(LockToken token) { + for (int i = 0; i < token.getNumLocks(); i++) { + eventForwarder.sendLockRelease(token.getLockId()); + } + } + + private class Lock { + private boolean unlocked = false; + + final void unlock() { + unlocked = true; + } + + final boolean isUnlocked() { + return unlocked; + } + } + } diff --git a/Dream2/src/main/java/dream/client/LockToken.java b/Dream2/src/main/java/dream/client/LockToken.java new file mode 100644 index 0000000..2bd1063 --- /dev/null +++ b/Dream2/src/main/java/dream/client/LockToken.java @@ -0,0 +1,25 @@ +package dream.client; + +import java.util.UUID; + +public class LockToken { + private UUID lockId; + private final int numLocks; + + LockToken(int numLocks) { + this.numLocks = numLocks; + } + + final void setLockId(UUID lockId) { + this.lockId = lockId; + } + + final UUID getLockId() { + return lockId; + } + + final int getNumLocks() { + return numLocks; + } + +} diff --git a/Dream2/src/main/java/dream/client/Signal.java b/Dream2/src/main/java/dream/client/Signal.java index 2e7a178..4862298 100755 --- a/Dream2/src/main/java/dream/client/Signal.java +++ b/Dream2/src/main/java/dream/client/Signal.java @@ -9,7 +9,6 @@ import java.util.Map; import java.util.Queue; import java.util.Set; -import java.util.UUID; import java.util.function.Supplier; import java.util.logging.Logger; import java.util.stream.Collectors; @@ -21,10 +20,8 @@ import dream.common.packets.content.Advertisement; import dream.common.packets.content.Event; import dream.common.packets.content.Subscription; -import dream.common.packets.locking.LockGrantPacket; -public class Signal - implements TimeChangingValue, UpdateProducer, UpdateConsumer, LockApplicant { +public class Signal implements TimeChangingValue, UpdateProducer, UpdateConsumer { // Management of local subscribers private final Map>> consumers = new HashMap<>(); @@ -41,8 +38,6 @@ public class Signal private final Supplier evaluation; - private UUID lockID = null; - private T val; private final Logger logger = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME); @@ -111,9 +106,10 @@ private final void processUpdate(EventProducerPair update) { clientEventForwarder.sendEvent(anyPkt.getId(), event, anyPkt.getSource()); final Set satConsumers = // - consumers.entrySet().stream().filter(e -> e.getValue().stream().allMatch(constr -> constr.test(val)))// - .map(e -> e.getKey())// - .collect(Collectors.toSet()); + consumers.entrySet().stream() + .filter(e -> e.getValue().stream().allMatch(constr -> constr.test(val)))// + .map(e -> e.getKey())// + .collect(Collectors.toSet()); // Notify local subscribers if (!satConsumers.isEmpty()) { pairs.forEach(pair -> waitingProducers.add(pair.getUpdateProducer())); @@ -148,36 +144,6 @@ public T get() { return val; } - public T atomicGet() { - acquireLock(); - // TODO: this should actually be a copy of the object - final T currentVal = val; - releaseLock(); - return currentVal; - } - - private final synchronized void acquireLock() { - if (Consts.consistencyType != ConsistencyType.ATOMIC) { - return; - } - clientEventForwarder.sendReadOnlyLockRequest(object + "@" + host, this); - while (lockID == null) { - try { - wait(); - } catch (final InterruptedException e) { - e.printStackTrace(); - } - } - } - - private final synchronized void releaseLock() { - if (Consts.consistencyType != ConsistencyType.ATOMIC) { - return; - } - clientEventForwarder.sendLockRelease(lockID); - lockID = null; - } - @Override public UpdateProducer filter(SerializablePredicate constraint) { final List> constrList = new ArrayList<>(); @@ -227,12 +193,6 @@ public List> getConstraints() { return constraints; } - @Override - public final synchronized void notifyLockGranted(LockGrantPacket lockGrant) { - lockID = lockGrant.getLockID(); - notifyAll(); - } - @Override public ChangeEvent change() { return new ChangeEvent(this); From f03721a321832224e437d851755801d1caf3f2c6 Mon Sep 17 00:00:00 2001 From: Alessandro Margara Date: Sun, 8 May 2016 14:45:33 +0200 Subject: [PATCH 059/161] Removing unneeded helper --- Dream2/src/examples/java/dream/examples/form/Boss.java | 2 -- Dream2/src/examples/java/dream/examples/form/Secretary.java | 2 -- 2 files changed, 4 deletions(-) diff --git a/Dream2/src/examples/java/dream/examples/form/Boss.java b/Dream2/src/examples/java/dream/examples/form/Boss.java index 70b699f..9fd32f9 100644 --- a/Dream2/src/examples/java/dream/examples/form/Boss.java +++ b/Dream2/src/examples/java/dream/examples/form/Boss.java @@ -1,6 +1,5 @@ package dream.examples.form; -import dream.client.Signal; import dream.client.Var; public class Boss extends FormClient { @@ -10,7 +9,6 @@ public class Boss extends FormClient { public Boss() { super("Boss"); eph = new Var<>("euro_per_hour", 8.5); - new Signal<>("helper", () -> eph.get(), eph); } @Override diff --git a/Dream2/src/examples/java/dream/examples/form/Secretary.java b/Dream2/src/examples/java/dream/examples/form/Secretary.java index 5844ef9..3b9d674 100644 --- a/Dream2/src/examples/java/dream/examples/form/Secretary.java +++ b/Dream2/src/examples/java/dream/examples/form/Secretary.java @@ -1,6 +1,5 @@ package dream.examples.form; -import dream.client.Signal; import dream.client.Var; public class Secretary extends FormClient { @@ -10,7 +9,6 @@ public class Secretary extends FormClient { public Secretary() { super("Secretary"); wh = new Var<>("working_hours", 5); - new Signal<>("helper", () -> wh.get(), wh); } @Override From f2414fa0a5a0c6fa60b137fab17c455cce5bea49 Mon Sep 17 00:00:00 2001 From: Alessandro Margara Date: Sun, 8 May 2016 16:03:01 +0200 Subject: [PATCH 060/161] Added the missing connect to the FormClient and FormServer --- Dream2/src/examples/java/dream/examples/form/FormClient.java | 1 + Dream2/src/examples/java/dream/examples/form/FormServer.java | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Dream2/src/examples/java/dream/examples/form/FormClient.java b/Dream2/src/examples/java/dream/examples/form/FormClient.java index 6b61a8a..023dd33 100644 --- a/Dream2/src/examples/java/dream/examples/form/FormClient.java +++ b/Dream2/src/examples/java/dream/examples/form/FormClient.java @@ -20,6 +20,7 @@ public abstract class FormClient { public FormClient(String name) { Consts.hostName = name; Logger.getGlobal().setLevel(Level.ALL); + DreamClient.instance.connect(); } protected void init(String name) { diff --git a/Dream2/src/examples/java/dream/examples/form/FormServer.java b/Dream2/src/examples/java/dream/examples/form/FormServer.java index 1656932..da11024 100644 --- a/Dream2/src/examples/java/dream/examples/form/FormServer.java +++ b/Dream2/src/examples/java/dream/examples/form/FormServer.java @@ -6,7 +6,6 @@ import dream.client.DreamClient; import dream.client.RemoteVar; import dream.client.Signal; -import dream.client.Var; import dream.common.Consts; import dream.locking.LockManagerLauncher; import dream.server.ServerLauncher; @@ -28,7 +27,7 @@ public FormServer() { logger.setLevel(Level.ALL); // logger.addHandler(Logger.getGlobal().getHandlers()[0]); Consts.hostName = NAME; - new Var<>("helper", ""); + DreamClient.instance.connect(); detectNewSession(); } From 1ab587e178624bd4bfddcd79a350da70fad77e07 Mon Sep 17 00:00:00 2001 From: Tobias Becker Date: Tue, 10 May 2016 10:24:19 +0200 Subject: [PATCH 061/161] added image of dependency graph and cleaned up the code --- .../java/dream/examples/form/Boss.java | 7 +++--- .../java/dream/examples/form/FormClient.java | 20 ++++++++++++------ .../java/dream/examples/form/FormGUI.java | 16 ++++++++++---- .../java/dream/examples/form/FormServer.java | 13 ++++++------ .../java/dream/examples/form/Secretary.java | 7 +++--- .../java/dream/examples/form/graph.png | Bin 0 -> 67919 bytes 6 files changed, 39 insertions(+), 24 deletions(-) create mode 100644 Dream2/src/examples/java/dream/examples/form/graph.png diff --git a/Dream2/src/examples/java/dream/examples/form/Boss.java b/Dream2/src/examples/java/dream/examples/form/Boss.java index 9fd32f9..c6cd4ec 100644 --- a/Dream2/src/examples/java/dream/examples/form/Boss.java +++ b/Dream2/src/examples/java/dream/examples/form/Boss.java @@ -7,21 +7,20 @@ public class Boss extends FormClient { private Var eph; public Boss() { - super("Boss"); + super("Boss", "Euro/Hour"); eph = new Var<>("euro_per_hour", 8.5); } @Override public void typedText(String typedText) { - System.out.println("Boss: \"" + typedText + "\""); Double value = Double.valueOf(typedText); - System.out.println("Boss: " + value); eph.set(value); + logger.fine("Set Euro_Per_Hour to " + value); } public static void main(String[] args) { Boss b = new Boss(); - b.init("Boss"); + b.init(); } } diff --git a/Dream2/src/examples/java/dream/examples/form/FormClient.java b/Dream2/src/examples/java/dream/examples/form/FormClient.java index 023dd33..99c0496 100644 --- a/Dream2/src/examples/java/dream/examples/form/FormClient.java +++ b/Dream2/src/examples/java/dream/examples/form/FormClient.java @@ -15,15 +15,23 @@ public abstract class FormClient { private RemoteVar settings; private Signal remoteSalary; private Signal remoteSettings; + private FormGUI gui; + protected final Logger logger; + private String labelText; - public FormClient(String name) { + public FormClient(String name, String labelText) { Consts.hostName = name; - Logger.getGlobal().setLevel(Level.ALL); + this.labelText = labelText; + + logger = Logger.getLogger(name); + logger.setLevel(Level.ALL); + logger.addHandler(Logger.getGlobal().getHandlers()[0]); + DreamClient.instance.connect(); } - protected void init(String name) { + protected void init() { while (!DreamClient.instance.listVariables().contains("salary@FormServer") || // !DreamClient.instance.listVariables().contains("settingsOkay@FormServer")) { try { @@ -33,7 +41,7 @@ protected void init(String name) { } } - gui = new FormGUI(name); + gui = new FormGUI(Consts.hostName, labelText); gui.setListener(this); salary = new RemoteVar<>("FormServer", "salary"); @@ -53,9 +61,9 @@ protected void init(String name) { return false; }, settings); - gui.setText(""); + gui.setText("Salary: "); gui.setColor(Color.red); - remoteSalary.change().addHandler((o, n) -> gui.setText(n.toString())); + remoteSalary.change().addHandler((o, n) -> gui.setText("Salary: " + n.toString())); remoteSettings.change().addHandler((o, n) -> gui.setColor((n ? Color.green : Color.red))); } diff --git a/Dream2/src/examples/java/dream/examples/form/FormGUI.java b/Dream2/src/examples/java/dream/examples/form/FormGUI.java index 1be8072..3167635 100644 --- a/Dream2/src/examples/java/dream/examples/form/FormGUI.java +++ b/Dream2/src/examples/java/dream/examples/form/FormGUI.java @@ -9,6 +9,7 @@ import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JLabel; +import javax.swing.JPanel; import javax.swing.JTextField; public class FormGUI extends JFrame { @@ -18,12 +19,12 @@ public class FormGUI extends JFrame { private FormClient listener; private JLabel display; - public FormGUI(String name) { - initUI(); + public FormGUI(String name, String labelText) { + initUI(labelText); setTitle(name); } - private void initUI() { + private void initUI(String labelText) { sendText = new JTextField(20); sendText.addKeyListener(new KeyListener() { @@ -48,9 +49,16 @@ public void keyPressed(KeyEvent e) { display.setPreferredSize(new Dimension(100, 30)); display.setMinimumSize(new Dimension(100, 30)); display.setOpaque(true); + + JLabel label = new JLabel(labelText + ": "); + + JPanel p = new JPanel(); + p.add(label); + p.add(sendText); + BorderLayout lay = new BorderLayout(); getContentPane().setLayout(lay); - getContentPane().add(sendText, BorderLayout.WEST); + getContentPane().add(p, BorderLayout.WEST); getContentPane().add(sendButton, BorderLayout.EAST); getContentPane().add(display, BorderLayout.NORTH); // setSize(300, 200); diff --git a/Dream2/src/examples/java/dream/examples/form/FormServer.java b/Dream2/src/examples/java/dream/examples/form/FormServer.java index da11024..8f83a0b 100644 --- a/Dream2/src/examples/java/dream/examples/form/FormServer.java +++ b/Dream2/src/examples/java/dream/examples/form/FormServer.java @@ -15,7 +15,7 @@ public class FormServer { public static final String NAME = "FormServer"; private boolean serverStarted; private boolean lockManagerStarted; - private final Logger logger = Logger.getGlobal();// Logger("FormServer"); + private final Logger logger = Logger.getLogger(NAME); private RemoteVar working_hours; private RemoteVar euro_per_hour; @@ -25,7 +25,8 @@ public FormServer() { startLockManagerIfNeeded(); logger.setLevel(Level.ALL); - // logger.addHandler(Logger.getGlobal().getHandlers()[0]); + logger.addHandler(Logger.getGlobal().getHandlers()[0]); + Consts.hostName = NAME; DreamClient.instance.connect(); detectNewSession(); @@ -41,10 +42,10 @@ private void detectNewSession() { String var = str.split("@")[0]; if (working_hours == null && var.equalsIgnoreCase("working_hours")) { working_hours = new RemoteVar<>(host, var); - System.out.println("Found Secreatary"); + logger.fine("Found Secretary"); } else if (euro_per_hour == null && var.equalsIgnoreCase("euro_per_hour")) { euro_per_hour = new RemoteVar<>(host, var); - System.out.println("Found Boss"); + logger.fine("Found Boss"); } } try { @@ -57,7 +58,7 @@ private void detectNewSession() { } private void updateDependencies() { - System.out.println("update Dep"); + logger.fine("Building Dependencies"); final Signal minimumHours = new Signal<>("minimumHours", () -> { if (working_hours.get() != null) @@ -94,7 +95,7 @@ private void updateDependencies() { return 0.0; }, working_hours, euro_per_hour); - System.out.println("update Dep finished"); + logger.fine("Finished building Dependencies"); } public static void main(String[] args) { diff --git a/Dream2/src/examples/java/dream/examples/form/Secretary.java b/Dream2/src/examples/java/dream/examples/form/Secretary.java index 3b9d674..aa5baa2 100644 --- a/Dream2/src/examples/java/dream/examples/form/Secretary.java +++ b/Dream2/src/examples/java/dream/examples/form/Secretary.java @@ -7,21 +7,20 @@ public class Secretary extends FormClient { private Var wh; public Secretary() { - super("Secretary"); + super("Secretary", "Working Hours"); wh = new Var<>("working_hours", 5); } @Override public void typedText(String typedText) { - System.out.println("Secretary: \"" + typedText + "\""); Integer value = Integer.valueOf(typedText); - System.out.println("Secretary: " + value); wh.set(value); + logger.fine("Set Working_Hours to " + value); } public static void main(String[] args) { Secretary s = new Secretary(); - s.init("Secretary"); + s.init(); } } diff --git a/Dream2/src/examples/java/dream/examples/form/graph.png b/Dream2/src/examples/java/dream/examples/form/graph.png new file mode 100644 index 0000000000000000000000000000000000000000..c2275dc7f8d3d8c95123aba83607b1f0dbd3e423 GIT binary patch literal 67919 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 literal 0 HcmV?d00001 From 9c5b57ced4537b6e43d8d51c393b4c8d9d3e52bf Mon Sep 17 00:00:00 2001 From: rkamath3 Date: Tue, 10 May 2016 11:00:19 +0200 Subject: [PATCH 062/161] Added task --- .../dream/examples/tasks/MasterProcess.java | 18 ++++++++- .../java/dream/examples/tasks/Task.java | 40 +++---------------- .../dream/examples/tasks/WorkerProcess.java | 20 ++++++++-- 3 files changed, 39 insertions(+), 39 deletions(-) diff --git a/Dream2/src/examples/java/dream/examples/tasks/MasterProcess.java b/Dream2/src/examples/java/dream/examples/tasks/MasterProcess.java index 0ba83ee..98db012 100644 --- a/Dream2/src/examples/java/dream/examples/tasks/MasterProcess.java +++ b/Dream2/src/examples/java/dream/examples/tasks/MasterProcess.java @@ -3,6 +3,9 @@ */ package dream.examples.tasks; +import dream.client.Var; +import dream.common.Consts; + /** * @author Ram * @@ -13,8 +16,21 @@ public class MasterProcess { * @param args */ public static void main(String[] args) { - // TODO Auto-generated method stub + Consts.hostName = "Host1"; + + Var myVar = new Var("TASK", new Task("Task1")); + try { + int i = 0; + while (true) { + myVar.set(new Task("Task" + i)); + Thread.sleep(2000); + i++; + } + } catch (InterruptedException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } } } diff --git a/Dream2/src/examples/java/dream/examples/tasks/Task.java b/Dream2/src/examples/java/dream/examples/tasks/Task.java index 8196faa..0467b73 100644 --- a/Dream2/src/examples/java/dream/examples/tasks/Task.java +++ b/Dream2/src/examples/java/dream/examples/tasks/Task.java @@ -9,11 +9,13 @@ * @author Ram * */ -public class Task { +public class Task extends ArrayList { private String name; private String assignee; - private String parentTask; - private ArrayList subTasks; + + public Task(String name) { + this.setName(name); + } /** * @return the name @@ -45,34 +47,4 @@ public void setAssignee(String assignee) { this.assignee = assignee; } - /** - * @return the parentTask - */ - public String getParentTask() { - return parentTask; - } - - /** - * @param parentTask - * the parentTask to set - */ - public void setParentTask(String parentTask) { - this.parentTask = parentTask; - } - - /** - * @return the subTasks - */ - public ArrayList getSubTasks() { - return subTasks; - } - - /** - * @param subTasks - * the subTasks to set - */ - public void setSubTasks(ArrayList subTasks) { - this.subTasks = subTasks; - } - -} \ No newline at end of file +} diff --git a/Dream2/src/examples/java/dream/examples/tasks/WorkerProcess.java b/Dream2/src/examples/java/dream/examples/tasks/WorkerProcess.java index c7f554c..46eecdf 100644 --- a/Dream2/src/examples/java/dream/examples/tasks/WorkerProcess.java +++ b/Dream2/src/examples/java/dream/examples/tasks/WorkerProcess.java @@ -3,6 +3,10 @@ */ package dream.examples.tasks; +import dream.client.RemoteVar; +import dream.client.Signal; +import dream.common.Consts; + /** * @author Ram * @@ -34,10 +38,18 @@ public WorkerProcess(String processName, String host) { public static void main(String[] args) { - if (args.length < 2) { - System.out.println("Usage WorkerProcess \n Example : WorkerProcess \"worker1\" \"Host1\""); - } - new WorkerProcess(args[0], args[1]); + Consts.hostName = "Host2"; + RemoteVar rv = new RemoteVar("Host1", "TASK"); + + Signal s = new Signal("s", () -> { + System.out.println("received New Object" + rv.get().getName()); + return rv.get(); + } , rv); + + // Register a handler which will be executed upon receiving the signal + s.change().addHandler((oldVal, val) -> { + System.out.println("Consumed: " + val.getName()); + }); } } From 369813ce3dcc238aacaf4260c4862ccc4b101570 Mon Sep 17 00:00:00 2001 From: Tobias Becker Date: Tue, 10 May 2016 11:21:15 +0200 Subject: [PATCH 063/161] ChangeEvent relies now on UUID instead of value --- Dream2/src/main/java/dream/client/ChangeEvent.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Dream2/src/main/java/dream/client/ChangeEvent.java b/Dream2/src/main/java/dream/client/ChangeEvent.java index 5ccbf60..e7f646e 100644 --- a/Dream2/src/main/java/dream/client/ChangeEvent.java +++ b/Dream2/src/main/java/dream/client/ChangeEvent.java @@ -2,12 +2,14 @@ import java.util.HashSet; import java.util.Set; +import java.util.UUID; import dream.common.packets.EventPacket; public class ChangeEvent implements UpdateConsumer { private T latest; + private UUID latestId; private Set> handlers = new HashSet>(); @@ -30,8 +32,9 @@ private void notifyHandler(T oldValue) { @Override public void updateFromProducer(EventPacket packet, UpdateProducer producer) { T newVal = (T) packet.getEvent().getVal(); - if (latest == null || !latest.equals(newVal)) { + if (latest == null || !packet.getId().equals(latestId)) { T old = latest; + latestId = packet.getId(); this.latest = newVal; notifyHandler(old); } From 7eef3dce64b9cd157d1c03fc4da298a3cfafc74c Mon Sep 17 00:00:00 2001 From: rkamath3 Date: Tue, 10 May 2016 11:24:50 +0200 Subject: [PATCH 064/161] Added new class for deligation of task --- .../examples/tasks/DeligatorProcess.java | 55 +++++++++++++++++++ .../dream/examples/tasks/WorkerProcess.java | 13 +++-- 2 files changed, 64 insertions(+), 4 deletions(-) create mode 100644 Dream2/src/examples/java/dream/examples/tasks/DeligatorProcess.java diff --git a/Dream2/src/examples/java/dream/examples/tasks/DeligatorProcess.java b/Dream2/src/examples/java/dream/examples/tasks/DeligatorProcess.java new file mode 100644 index 0000000..9f2dd46 --- /dev/null +++ b/Dream2/src/examples/java/dream/examples/tasks/DeligatorProcess.java @@ -0,0 +1,55 @@ +/** + * + */ +package dream.examples.tasks; + +import dream.client.RemoteVar; +import dream.client.Signal; +import dream.common.Consts; + +/** + * @author Ram + * + */ +public class DeligatorProcess { + /** + * @param args + */ + private String processName; + + /** + * @return the processName + */ + public String getProcessName() { + return processName; + } + + /** + * @param processName + * the processName to set + */ + public void setProcessName(String processName) { + this.processName = processName; + } + + public DeligatorProcess(String processName, String host) { + this.setProcessName(processName); + } + + public static void main(String[] args) { + + Consts.hostName = "Host2"; + RemoteVar rv = new RemoteVar("Host1", "TASK_"); + + Signal s = new Signal("s", () -> { + System.out.println("received New Object" + rv.get().getName()); + return rv.get(); + } , rv); + + // Register a handler which will be executed upon receiving the signal + s.change().addHandler((oldVal, val) -> { + System.out.println("Consumed: " + val.getName()); + }); + } + +} diff --git a/Dream2/src/examples/java/dream/examples/tasks/WorkerProcess.java b/Dream2/src/examples/java/dream/examples/tasks/WorkerProcess.java index 799a517..dc0f88f 100644 --- a/Dream2/src/examples/java/dream/examples/tasks/WorkerProcess.java +++ b/Dream2/src/examples/java/dream/examples/tasks/WorkerProcess.java @@ -5,6 +5,7 @@ import dream.client.RemoteVar; import dream.client.Signal; +import dream.client.Var; import dream.common.Consts; /** @@ -12,6 +13,7 @@ * */ public class WorkerProcess { + static int i = 0; /** * @param args */ @@ -39,16 +41,19 @@ public WorkerProcess(String processName, String host) { public static void main(String[] args) { Consts.hostName = "Host2"; - RemoteVar rv = new RemoteVar("Host1", "TASK_"); - Signal s = new Signal("s", () -> { - System.out.println("received New Object" + rv.get().getName()); + RemoteVar rv = new RemoteVar("Host1", "TASK"); + Var myVar = new Var("TASK_ASSIGNED", ""); + + Signal s = new Signal("s", () -> { + System.out.println("received New Object" + rv.get()); return rv.get(); } , rv); // Register a handler which will be executed upon receiving the signal s.change().addHandler((oldVal, val) -> { - System.out.println("Consumed: " + val.getName()); + System.out.println("Deligating Task : " + val); + myVar.set(val + "@" + i++); }); } From 8dce4c16cdbeee0710d5188d15588a89cf349aaa Mon Sep 17 00:00:00 2001 From: rkamath3 Date: Tue, 10 May 2016 11:47:33 +0200 Subject: [PATCH 065/161] Added basic structure of Task example --- ...igatorProcess.java => DeligatProcess.java} | 16 ++++++---- .../dream/examples/tasks/MasterProcess.java | 6 ++-- .../dream/examples/tasks/WorkerProcess.java | 29 ++++++++++++------- 3 files changed, 32 insertions(+), 19 deletions(-) rename Dream2/src/examples/java/dream/examples/tasks/{DeligatorProcess.java => DeligatProcess.java} (72%) diff --git a/Dream2/src/examples/java/dream/examples/tasks/DeligatorProcess.java b/Dream2/src/examples/java/dream/examples/tasks/DeligatProcess.java similarity index 72% rename from Dream2/src/examples/java/dream/examples/tasks/DeligatorProcess.java rename to Dream2/src/examples/java/dream/examples/tasks/DeligatProcess.java index 9f2dd46..88d4388 100644 --- a/Dream2/src/examples/java/dream/examples/tasks/DeligatorProcess.java +++ b/Dream2/src/examples/java/dream/examples/tasks/DeligatProcess.java @@ -5,13 +5,16 @@ import dream.client.RemoteVar; import dream.client.Signal; +import dream.client.Var; import dream.common.Consts; /** * @author Ram * */ -public class DeligatorProcess { +public class DeligatProcess { + + static int i; /** * @param args */ @@ -32,23 +35,26 @@ public void setProcessName(String processName) { this.processName = processName; } - public DeligatorProcess(String processName, String host) { + public DeligatProcess(String processName, String host) { this.setProcessName(processName); } public static void main(String[] args) { Consts.hostName = "Host2"; - RemoteVar rv = new RemoteVar("Host1", "TASK_"); + RemoteVar rv = new RemoteVar("Host1", "TASK"); + Var myVar = new Var("TASK_ASSIGNED", null); Signal s = new Signal("s", () -> { - System.out.println("received New Object" + rv.get().getName()); return rv.get(); } , rv); // Register a handler which will be executed upon receiving the signal s.change().addHandler((oldVal, val) -> { - System.out.println("Consumed: " + val.getName()); + + val.setAssignee(i++ + ""); + myVar.set(val); + }); } diff --git a/Dream2/src/examples/java/dream/examples/tasks/MasterProcess.java b/Dream2/src/examples/java/dream/examples/tasks/MasterProcess.java index c633fee..ecda745 100644 --- a/Dream2/src/examples/java/dream/examples/tasks/MasterProcess.java +++ b/Dream2/src/examples/java/dream/examples/tasks/MasterProcess.java @@ -18,16 +18,16 @@ public class MasterProcess { public static void main(String[] args) { Consts.hostName = "Host1"; - Var myVar = new Var("TASK_", new Task("Task1")); + Var myVar = new Var("TASK", new Task("Task1")); try { int i = 0; while (true) { - myVar.set(new Task("Task" + i)); Thread.sleep(2000); + myVar.set(new Task("Task" + i)); i++; } - } catch (InterruptedException e) { + } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } diff --git a/Dream2/src/examples/java/dream/examples/tasks/WorkerProcess.java b/Dream2/src/examples/java/dream/examples/tasks/WorkerProcess.java index dc0f88f..f0e18ba 100644 --- a/Dream2/src/examples/java/dream/examples/tasks/WorkerProcess.java +++ b/Dream2/src/examples/java/dream/examples/tasks/WorkerProcess.java @@ -5,7 +5,6 @@ import dream.client.RemoteVar; import dream.client.Signal; -import dream.client.Var; import dream.common.Consts; /** @@ -40,20 +39,28 @@ public WorkerProcess(String processName, String host) { public static void main(String[] args) { - Consts.hostName = "Host2"; + Consts.hostName = "Host3"; - RemoteVar rv = new RemoteVar("Host1", "TASK"); - Var myVar = new Var("TASK_ASSIGNED", ""); + RemoteVar task = new RemoteVar("Host1", "TASK"); + RemoteVar taskDeligated = new RemoteVar("Host2", "TASK_ASSIGNED"); - Signal s = new Signal("s", () -> { - System.out.println("received New Object" + rv.get()); - return rv.get(); - } , rv); + Signal signalFromMaster = new Signal("s", () -> { + return task.get(); + } , task); + Signal signalFromDeligator = new Signal("s1", () -> { + return taskDeligated.get(); + } , taskDeligated); // Register a handler which will be executed upon receiving the signal - s.change().addHandler((oldVal, val) -> { - System.out.println("Deligating Task : " + val); - myVar.set(val + "@" + i++); + // from master process + signalFromMaster.change().addHandler((oldVal, val) -> { + System.out.println("FROM MASTER : " + val.getAssignee() + " " + val.getName()); + }); + + // Register a handler which will be executed upon receiving the signal + // from delegate process + signalFromDeligator.change().addHandler((oldVal, val) -> { + System.out.println("FROM DELIGATOR : " + val.getAssignee() + " " + val.getName()); }); } From c0c302d63942318d428b3a85a01c2c9fc3ec4c00 Mon Sep 17 00:00:00 2001 From: Tobias Becker Date: Tue, 10 May 2016 13:35:45 +0200 Subject: [PATCH 066/161] moved chat utility classes to examples.util --- .../examples/chat/{util => }/Starter.java | 2 +- .../java/dream/examples/chat/core/Chat.java | 26 ++----------------- .../dream/examples/chat/core/ChatServer.java | 5 ++-- .../DependencyVisualization.java} | 4 +-- .../java/dream/examples/util/Pair.java | 21 +++++++++++++++ 5 files changed, 29 insertions(+), 29 deletions(-) rename Dream2/src/examples/java/dream/examples/chat/{util => }/Starter.java (98%) rename Dream2/src/examples/java/dream/examples/{chat/util/DependencyGraph.java => util/DependencyVisualization.java} (96%) create mode 100644 Dream2/src/examples/java/dream/examples/util/Pair.java diff --git a/Dream2/src/examples/java/dream/examples/chat/util/Starter.java b/Dream2/src/examples/java/dream/examples/chat/Starter.java similarity index 98% rename from Dream2/src/examples/java/dream/examples/chat/util/Starter.java rename to Dream2/src/examples/java/dream/examples/chat/Starter.java index b6864d5..4e2ba73 100644 --- a/Dream2/src/examples/java/dream/examples/chat/util/Starter.java +++ b/Dream2/src/examples/java/dream/examples/chat/Starter.java @@ -1,4 +1,4 @@ -package dream.examples.chat.util; +package dream.examples.chat; import java.io.IOException; import java.util.ArrayList; diff --git a/Dream2/src/examples/java/dream/examples/chat/core/Chat.java b/Dream2/src/examples/java/dream/examples/chat/core/Chat.java index 0968b66..f71e6fd 100644 --- a/Dream2/src/examples/java/dream/examples/chat/core/Chat.java +++ b/Dream2/src/examples/java/dream/examples/chat/core/Chat.java @@ -14,7 +14,7 @@ import dream.client.Signal; import dream.client.Var; import dream.common.Consts; -import dream.examples.chat.util.DependencyGraph; +import dream.examples.util.DependencyVisualization; public class Chat { @@ -203,7 +203,7 @@ private void processCommand(String text) { String name = temp1[0]; initiateNewRoom(name, temp1[1]); } else if (command.equalsIgnoreCase("graph")) { - DependencyGraph.show(); + DependencyVisualization.show(); } else if (command.equalsIgnoreCase("sgraph")) { sendServerMessage("graph"); } @@ -223,10 +223,6 @@ public static void main(String[] args) { return; } int x, y; - for (String s : args) { - System.out.print(s + ","); - } - System.out.println(); if (args.length < 3) y = -1; else @@ -238,22 +234,4 @@ public static void main(String[] args) { x = Integer.parseInt(args[1]); EventQueue.invokeLater(() -> new Chat(args[0], x, y)); } -} - -class Pair { - private final S first; - private final T second; - - Pair(S a, T b) { - this.first = a; - this.second = b; - } - - public S getFirst() { - return first; - } - - public T getSecond() { - return second; - } } \ No newline at end of file diff --git a/Dream2/src/examples/java/dream/examples/chat/core/ChatServer.java b/Dream2/src/examples/java/dream/examples/chat/core/ChatServer.java index 22d294a..218b555 100644 --- a/Dream2/src/examples/java/dream/examples/chat/core/ChatServer.java +++ b/Dream2/src/examples/java/dream/examples/chat/core/ChatServer.java @@ -15,7 +15,8 @@ import dream.client.Signal; import dream.client.Var; import dream.common.Consts; -import dream.examples.chat.util.DependencyGraph; +import dream.examples.util.DependencyVisualization; +import dream.examples.util.Pair; import dream.locking.LockManagerLauncher; import dream.server.ServerLauncher; @@ -165,7 +166,7 @@ protected void receivedMessage(String clientName, String message) { logger.fine("Room: Finished setting up room " + roomName); } } else if (command.equalsIgnoreCase("graph")) { - DependencyGraph.show(); + DependencyVisualization.show(); } } } diff --git a/Dream2/src/examples/java/dream/examples/chat/util/DependencyGraph.java b/Dream2/src/examples/java/dream/examples/util/DependencyVisualization.java similarity index 96% rename from Dream2/src/examples/java/dream/examples/chat/util/DependencyGraph.java rename to Dream2/src/examples/java/dream/examples/util/DependencyVisualization.java index 4325395..92f90b2 100644 --- a/Dream2/src/examples/java/dream/examples/chat/util/DependencyGraph.java +++ b/Dream2/src/examples/java/dream/examples/util/DependencyVisualization.java @@ -1,4 +1,4 @@ -package dream.examples.chat.util; +package dream.examples.util; import java.io.File; import java.io.FileOutputStream; @@ -10,7 +10,7 @@ import java.util.Map.Entry; import java.util.Set; -public class DependencyGraph { +public class DependencyVisualization { private static File writeAndOpen(String data, String filenamePrefix, String filenameSuffix) { File file = null; diff --git a/Dream2/src/examples/java/dream/examples/util/Pair.java b/Dream2/src/examples/java/dream/examples/util/Pair.java new file mode 100644 index 0000000..754df33 --- /dev/null +++ b/Dream2/src/examples/java/dream/examples/util/Pair.java @@ -0,0 +1,21 @@ +package dream.examples.util; + +import java.io.Serializable; + +public class Pair implements Serializable { + private final S first; + private final T second; + + public Pair(S a, T b) { + this.first = a; + this.second = b; + } + + public S getFirst() { + return first; + } + + public T getSecond() { + return second; + } +} \ No newline at end of file From b7988635e79b48353c19e35dc1e30c79341640aa Mon Sep 17 00:00:00 2001 From: Tobias Becker Date: Tue, 10 May 2016 13:36:29 +0200 Subject: [PATCH 067/161] implemented glitch freedom for form example --- .../java/dream/examples/form/FormServer.java | 10 +-- .../examples/form/GlitchFreeFormServer.java | 84 +++++++++++++++++++ 2 files changed, 89 insertions(+), 5 deletions(-) create mode 100644 Dream2/src/examples/java/dream/examples/form/GlitchFreeFormServer.java diff --git a/Dream2/src/examples/java/dream/examples/form/FormServer.java b/Dream2/src/examples/java/dream/examples/form/FormServer.java index 8f83a0b..4805604 100644 --- a/Dream2/src/examples/java/dream/examples/form/FormServer.java +++ b/Dream2/src/examples/java/dream/examples/form/FormServer.java @@ -15,10 +15,10 @@ public class FormServer { public static final String NAME = "FormServer"; private boolean serverStarted; private boolean lockManagerStarted; - private final Logger logger = Logger.getLogger(NAME); + protected final Logger logger = Logger.getLogger(NAME); - private RemoteVar working_hours; - private RemoteVar euro_per_hour; + protected RemoteVar working_hours; + protected RemoteVar euro_per_hour; public FormServer() { startServerIfNeeded(); @@ -54,10 +54,10 @@ private void detectNewSession() { logger.log(Level.SEVERE, "Failed to sleep for 0.5 seconds", e); } } - updateDependencies(); + createDependencies(); } - private void updateDependencies() { + protected void createDependencies() { logger.fine("Building Dependencies"); final Signal minimumHours = new Signal<>("minimumHours", () -> { diff --git a/Dream2/src/examples/java/dream/examples/form/GlitchFreeFormServer.java b/Dream2/src/examples/java/dream/examples/form/GlitchFreeFormServer.java new file mode 100644 index 0000000..ee5cd3a --- /dev/null +++ b/Dream2/src/examples/java/dream/examples/form/GlitchFreeFormServer.java @@ -0,0 +1,84 @@ +package dream.examples.form; + +import java.util.LinkedList; + +import dream.client.Signal; +import dream.examples.util.Pair; + +public class GlitchFreeFormServer extends FormServer { + + @Override + protected void createDependencies() { + logger.fine("Building Dependencies"); + + final UpdateCounter minimumCounter = new UpdateCounter(); + final UpdateCounter maximumCounter = new UpdateCounter(); + + final Signal> minimumHours = new Signal<>("minimumHours", () -> { + return new Pair<>(working_hours.get() > 10, minimumCounter.incAndGet()); + }, working_hours); + + final Signal> maximumHours = new Signal<>("maximumHours", () -> { + return new Pair<>(working_hours.get() < 60, maximumCounter.incAndGet()); + }, working_hours); + + final Signal minimumEuroPerHour = new Signal<>("minimumEuroPerHour", () -> { + return euro_per_hour.get() > 10; + }, euro_per_hour); + + final LinkedList> minimumQueue = new LinkedList<>(); + final LinkedList> maximumQueue = new LinkedList<>(); + final OldValue currentValue = new OldValue<>(); + + new Signal<>("settingsOkay", () -> { + if (minimumQueue.getLast().getSecond() < minimumHours.get().getSecond()) + minimumQueue.add(minimumHours.get()); + else if (maximumQueue.getLast().getSecond() < maximumHours.get().getSecond()) + maximumQueue.add(maximumHours.get()); + + if (minimumQueue.size() > 0 && maximumQueue.size() > 0) + currentValue.set( + minimumQueue.pop().getFirst() && maximumQueue.pop().getFirst() && minimumEuroPerHour.get()); + return currentValue.get(); + }, minimumHours, maximumHours, minimumEuroPerHour); + + new Signal<>("salary", () -> { + return working_hours.get() * euro_per_hour.get(); + }, working_hours, euro_per_hour); + + logger.fine("Finished building Dependencies"); + } + + public static void main(String[] args) { + new GlitchFreeFormServer(); + } +} + +class UpdateCounter { + int i = 0; + + public void inc() { + i += 1; + } + + public int incAndGet() { + inc(); + return get(); + } + + public int get() { + return i; + } +} + +class OldValue { + T value; + + public T get() { + return value; + } + + public void set(T v) { + value = v; + } +} From 4bb40ea50d305673146e8ce886fc42e872eff492 Mon Sep 17 00:00:00 2001 From: Tobias Becker Date: Fri, 13 May 2016 18:24:05 +0200 Subject: [PATCH 068/161] fixed Glitch Freedom in Form example --- .../examples/form/GlitchFreeFormServer.java | 28 +++++++++++++------ 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/Dream2/src/examples/java/dream/examples/form/GlitchFreeFormServer.java b/Dream2/src/examples/java/dream/examples/form/GlitchFreeFormServer.java index ee5cd3a..ed01918 100644 --- a/Dream2/src/examples/java/dream/examples/form/GlitchFreeFormServer.java +++ b/Dream2/src/examples/java/dream/examples/form/GlitchFreeFormServer.java @@ -28,22 +28,27 @@ protected void createDependencies() { final LinkedList> minimumQueue = new LinkedList<>(); final LinkedList> maximumQueue = new LinkedList<>(); - final OldValue currentValue = new OldValue<>(); + final Value currentValue = new Value<>(false); new Signal<>("settingsOkay", () -> { - if (minimumQueue.getLast().getSecond() < minimumHours.get().getSecond()) + if (minimumHours.get() != null + && (minimumQueue.isEmpty() || minimumQueue.getLast().getSecond() < minimumHours.get().getSecond())) minimumQueue.add(minimumHours.get()); - else if (maximumQueue.getLast().getSecond() < maximumHours.get().getSecond()) + if (maximumHours.get() != null + && (maximumQueue.isEmpty() || maximumQueue.getLast().getSecond() < maximumHours.get().getSecond())) maximumQueue.add(maximumHours.get()); - if (minimumQueue.size() > 0 && maximumQueue.size() > 0) + if (minimumQueue.size() > 0 && maximumQueue.size() > 0 && minimumEuroPerHour.get() != null) currentValue.set( minimumQueue.pop().getFirst() && maximumQueue.pop().getFirst() && minimumEuroPerHour.get()); return currentValue.get(); }, minimumHours, maximumHours, minimumEuroPerHour); new Signal<>("salary", () -> { - return working_hours.get() * euro_per_hour.get(); + if (working_hours.get() != null && euro_per_hour.get() != null) + return working_hours.get() * euro_per_hour.get(); + else + return 0.0; }, working_hours, euro_per_hour); logger.fine("Finished building Dependencies"); @@ -55,7 +60,7 @@ public static void main(String[] args) { } class UpdateCounter { - int i = 0; + private int i = 0; public void inc() { i += 1; @@ -71,8 +76,15 @@ public int get() { } } -class OldValue { - T value; +class Value { + private T value; + + public Value() { + } + + public Value(T init) { + set(init); + } public T get() { return value; From 97256266cfbb0e0247fd6cc3eec40c29cd95e916 Mon Sep 17 00:00:00 2001 From: "Mia.Min.Yang" Date: Fri, 13 May 2016 20:01:29 +0200 Subject: [PATCH 069/161] Adding task board example. --- .../java/dream/examples/taskBoard/Pair.java | 24 +++++ .../dream/examples/taskBoard/ServerHost.java | 95 +++++++++++++++++++ .../dream/examples/taskBoard/TaskReview.java | 90 ++++++++++++++++++ .../java/dream/examples/taskBoard/Tasks.java | 26 +++++ 4 files changed, 235 insertions(+) create mode 100644 Dream2/src/examples/java/dream/examples/taskBoard/Pair.java create mode 100644 Dream2/src/examples/java/dream/examples/taskBoard/ServerHost.java create mode 100644 Dream2/src/examples/java/dream/examples/taskBoard/TaskReview.java create mode 100644 Dream2/src/examples/java/dream/examples/taskBoard/Tasks.java diff --git a/Dream2/src/examples/java/dream/examples/taskBoard/Pair.java b/Dream2/src/examples/java/dream/examples/taskBoard/Pair.java new file mode 100644 index 0000000..0bcb4de --- /dev/null +++ b/Dream2/src/examples/java/dream/examples/taskBoard/Pair.java @@ -0,0 +1,24 @@ +package dream.examples.taskBoard; + +//pair class +public class Pair { + String host; + String Var; + + public String getHost() { + return host; + } + + public void setHost(String host) { + this.host = host; + } + + public String getVar() { + return Var; + } + + public void setVar(String var) { + Var = var; + } + +} diff --git a/Dream2/src/examples/java/dream/examples/taskBoard/ServerHost.java b/Dream2/src/examples/java/dream/examples/taskBoard/ServerHost.java new file mode 100644 index 0000000..ef1be33 --- /dev/null +++ b/Dream2/src/examples/java/dream/examples/taskBoard/ServerHost.java @@ -0,0 +1,95 @@ +package dream.examples.taskBoard; + +import java.util.ArrayList; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; + +import dream.client.DreamClient; +import dream.client.RemoteVar; +import dream.client.Signal; +import dream.client.Var; +import dream.common.Consts; +import dream.examples.util.Pair; +import dream.server.ServerLauncher; + +public class ServerHost { + public static final String NAME = "ServerHost"; + private boolean serverStarted = false; + private Var> myClients = null; + private final Logger log = Logger.getLogger("Server"); + private Var devTask = null; + private Var testTask = null; + + public static void main(String[] args) { + new ServerHost(); + } + + public ServerHost() { + startServerIfNeeded(); + log.setLevel(Level.ALL); + log.addHandler(log.getGlobal().getHandlers()[0]); + Consts.hostName = NAME; + myClients = new Var>("Server_registered_clients", new ArrayList()); + detectClients(); + } + + private void detectClients() { + Set vars = DreamClient.instance.listVariables(); + vars.stream().map(x -> new Pair(x.split("@")[1], x.split("@")[0])) + .filter(x -> !myClients.get().contains(x.getSecond() + "@" + x.getFirst()) + && x.getSecond().equalsIgnoreCase("toServerVar")) + .forEach(x -> createClient(x.getFirst(), x.getSecond())); + + try { + Thread.sleep(500); + } catch (InterruptedException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + detectClients(); + } + + private void createClient(String clientHost, String clientVar) { + RemoteVar rv = new RemoteVar(clientHost, clientVar); + Signal sig = new Signal("received_" + clientHost, () -> { + if (rv.get() != null) { + return rv.get(); + } else { + return ""; + } + }, rv); + log.info("Sig" + sig.toString()); + sig.change().addHandler((oldValue, newValue) -> createTaskLists(newValue)); + myClients.modify((old) -> old.add(clientVar + "@" + clientHost)); + } + + private void createTaskLists(String message) { + if (message != null) { + String newDev = message.split(":")[0]; + String newTest = message.split(":")[1]; + if (devTask == null) { + devTask = new Var("taskDevs", ""); + } + if (testTask == null) { + testTask = new Var("taskTests", ""); + } + // Set vars for remote querying + devTask.set(newDev); + testTask.set(newTest); + } + } + + private final void startServerIfNeeded() { + if (!serverStarted) { + log.info("M: server started"); + ServerLauncher.start(); + serverStarted = true; + } + try { + Thread.sleep(500); + } catch (InterruptedException e) { + log.log(Level.SEVERE, "Failed to wait for Server starting", e); + } + } +} \ No newline at end of file diff --git a/Dream2/src/examples/java/dream/examples/taskBoard/TaskReview.java b/Dream2/src/examples/java/dream/examples/taskBoard/TaskReview.java new file mode 100644 index 0000000..4f3521a --- /dev/null +++ b/Dream2/src/examples/java/dream/examples/taskBoard/TaskReview.java @@ -0,0 +1,90 @@ +package dream.examples.taskBoard; + +import java.util.ArrayList; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; + +import dream.client.DreamClient; +import dream.client.RemoteVar; +import dream.client.Signal; +import dream.client.Var; +import dream.common.Consts; +import dream.examples.util.Pair; + +public class TaskReview { + private final Logger log = Logger.getLogger("MagtClient"); + private Var> myServer = null; + RemoteVar devTasks = null; + RemoteVar testTasks = null; + + public TaskReview() { + Consts.hostName = "QueryClient"; + log.setLevel(Level.ALL); + log.addHandler(log.getGlobal().getHandlers()[0]); + myServer = new Var>("from_server", new ArrayList()); + detectQueryResults(); + } + + private void detectQueryResults() { + Set vars = DreamClient.instance.listVariables(); + vars.stream().map(x -> new Pair(x.split("@")[1], x.split("@")[0])) + .filter(x -> !myServer.get().contains(x.getSecond() + "@" + x.getFirst()) + && x.getSecond().equalsIgnoreCase("taskDevs")) + .forEach(x -> updateDevQuery(x.getFirst(), x.getSecond())); + + vars.stream().map(x -> new Pair(x.split("@")[1], x.split("@")[0])) + .filter(x -> !myServer.get().contains(x.getSecond() + "@" + x.getFirst()) + && x.getSecond().equalsIgnoreCase("taskTests")) + .forEach(x -> updateTestQuery(x.getFirst(), x.getSecond())); + + try { + Thread.sleep(500); + } catch (InterruptedException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + detectQueryResults(); + } + + private void updateDevQuery(String host, String value) { + RemoteVar rv = new RemoteVar(host, value); + + Signal sig = new Signal("received_" + host, () -> { + if (rv.get() != null) { + log.fine("M: not empty " + rv.get()); + return rv.get(); + } else { + return ""; + } + }, rv); + + sig.change().addHandler((oldValue, newValue) -> showQuery(newValue)); + myServer.modify((old) -> old.add(value + "@" + host)); + } + + private void updateTestQuery(String host, String value) { + RemoteVar rv = new RemoteVar(host, value); + + Signal sig = new Signal("received_" + host, () -> { + if (rv.get() != null) { + log.fine("M: not empty " + rv.get()); + return rv.get(); + } else { + return ""; + } + }, rv); + + sig.change().addHandler((oldValue, newValue) -> showQuery(newValue)); + myServer.modify((old) -> old.add(value + "@" + host)); + } + + private void showQuery(String x) { + log.info("showQuery:" + x); + } + + public static void main(String[] args) { + new TaskReview(); + } + +} diff --git a/Dream2/src/examples/java/dream/examples/taskBoard/Tasks.java b/Dream2/src/examples/java/dream/examples/taskBoard/Tasks.java new file mode 100644 index 0000000..6fd51ea --- /dev/null +++ b/Dream2/src/examples/java/dream/examples/taskBoard/Tasks.java @@ -0,0 +1,26 @@ +package dream.examples.taskBoard; + +import dream.client.Var; +import dream.common.Consts; + +public class Tasks { + public static void main(String[] args) { + new Tasks(); + } + + public Tasks() { + Consts.hostName = "host1"; + Var v = new Var("toServerVar", "D1:T1"); + try { + int i = 0; + while (i < 10) { + Thread.sleep(1000); + v.set("D" + i + ":" + "T" + i); + i++; + } + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + +} From 5d4bc6219cb94e8ed9f7393fd257042ebeb19ef6 Mon Sep 17 00:00:00 2001 From: "Mia.Min.Yang" Date: Fri, 13 May 2016 20:15:12 +0200 Subject: [PATCH 070/161] Little amendment. --- .../src/examples/java/dream/examples/taskBoard/Pair.java | 6 +++++- .../java/dream/examples/taskBoard/ServerHost.java | 8 +++++++- .../java/dream/examples/taskBoard/TaskReview.java | 6 ++++++ .../src/examples/java/dream/examples/taskBoard/Tasks.java | 6 ++++++ 4 files changed, 24 insertions(+), 2 deletions(-) diff --git a/Dream2/src/examples/java/dream/examples/taskBoard/Pair.java b/Dream2/src/examples/java/dream/examples/taskBoard/Pair.java index 0bcb4de..f141aab 100644 --- a/Dream2/src/examples/java/dream/examples/taskBoard/Pair.java +++ b/Dream2/src/examples/java/dream/examples/taskBoard/Pair.java @@ -1,6 +1,10 @@ package dream.examples.taskBoard; -//pair class +/** + * @author Min Yang + * @date May 13, 2016 + * @description common class for host-var pair + */ public class Pair { String host; String Var; diff --git a/Dream2/src/examples/java/dream/examples/taskBoard/ServerHost.java b/Dream2/src/examples/java/dream/examples/taskBoard/ServerHost.java index ef1be33..7f9fa6d 100644 --- a/Dream2/src/examples/java/dream/examples/taskBoard/ServerHost.java +++ b/Dream2/src/examples/java/dream/examples/taskBoard/ServerHost.java @@ -13,6 +13,13 @@ import dream.examples.util.Pair; import dream.server.ServerLauncher; +/** + * + * @author Min Yang + * @date May 13, 2016 + * @description run background tasks: read task, create task lists: development + * and test etc. + */ public class ServerHost { public static final String NAME = "ServerHost"; private boolean serverStarted = false; @@ -40,7 +47,6 @@ private void detectClients() { .filter(x -> !myClients.get().contains(x.getSecond() + "@" + x.getFirst()) && x.getSecond().equalsIgnoreCase("toServerVar")) .forEach(x -> createClient(x.getFirst(), x.getSecond())); - try { Thread.sleep(500); } catch (InterruptedException e) { diff --git a/Dream2/src/examples/java/dream/examples/taskBoard/TaskReview.java b/Dream2/src/examples/java/dream/examples/taskBoard/TaskReview.java index 4f3521a..b6cc5a6 100644 --- a/Dream2/src/examples/java/dream/examples/taskBoard/TaskReview.java +++ b/Dream2/src/examples/java/dream/examples/taskBoard/TaskReview.java @@ -12,6 +12,12 @@ import dream.common.Consts; import dream.examples.util.Pair; +/** + * + * @author Min Yang + * @date May 13, 2016 + * @description Review the tasks. + */ public class TaskReview { private final Logger log = Logger.getLogger("MagtClient"); private Var> myServer = null; diff --git a/Dream2/src/examples/java/dream/examples/taskBoard/Tasks.java b/Dream2/src/examples/java/dream/examples/taskBoard/Tasks.java index 6fd51ea..0196b54 100644 --- a/Dream2/src/examples/java/dream/examples/taskBoard/Tasks.java +++ b/Dream2/src/examples/java/dream/examples/taskBoard/Tasks.java @@ -3,6 +3,12 @@ import dream.client.Var; import dream.common.Consts; +/** + * + * @author Min Yang + * @date May 13, 2016 + * @description Creating tasks. + */ public class Tasks { public static void main(String[] args) { new Tasks(); From a7902077b55ff12540d8556f1a748d793494957d Mon Sep 17 00:00:00 2001 From: "Mia.Min.Yang" Date: Fri, 13 May 2016 21:30:48 +0200 Subject: [PATCH 071/161] Add InitBoard.java --- .../dream/examples/taskBoard/InitBoard.java | 11 ++++++++++ .../dream/examples/taskBoard/ServerHost.java | 20 +++++++++---------- .../dream/examples/taskBoard/TaskReview.java | 19 +++++++++--------- .../java/dream/examples/taskBoard/Tasks.java | 1 + 4 files changed, 31 insertions(+), 20 deletions(-) create mode 100644 Dream2/src/examples/java/dream/examples/taskBoard/InitBoard.java diff --git a/Dream2/src/examples/java/dream/examples/taskBoard/InitBoard.java b/Dream2/src/examples/java/dream/examples/taskBoard/InitBoard.java new file mode 100644 index 0000000..6a9c07b --- /dev/null +++ b/Dream2/src/examples/java/dream/examples/taskBoard/InitBoard.java @@ -0,0 +1,11 @@ +package dream.examples.taskBoard; + +/** + * + * @author Min Yang + * @date May 13, 2016 + * @description User can first run ServerHost.java + * (..examples.taskBoard.ServerHost.java), then run Tasks.java + * (..examples.taskBoard.Tasks.java), then run TaskReview.java + * (..examples.taskBoard.ServerHost.java) + */ diff --git a/Dream2/src/examples/java/dream/examples/taskBoard/ServerHost.java b/Dream2/src/examples/java/dream/examples/taskBoard/ServerHost.java index 7f9fa6d..c7f4e36 100644 --- a/Dream2/src/examples/java/dream/examples/taskBoard/ServerHost.java +++ b/Dream2/src/examples/java/dream/examples/taskBoard/ServerHost.java @@ -35,13 +35,16 @@ public static void main(String[] args) { public ServerHost() { startServerIfNeeded(); log.setLevel(Level.ALL); - log.addHandler(log.getGlobal().getHandlers()[0]); + log.addHandler(Logger.getGlobal().getHandlers()[0]); Consts.hostName = NAME; myClients = new Var>("Server_registered_clients", new ArrayList()); detectClients(); + new Tasks(); + new TaskReview(); } private void detectClients() { + log.info("inside ServerHost"); Set vars = DreamClient.instance.listVariables(); vars.stream().map(x -> new Pair(x.split("@")[1], x.split("@")[0])) .filter(x -> !myClients.get().contains(x.getSecond() + "@" + x.getFirst()) @@ -50,7 +53,6 @@ private void detectClients() { try { Thread.sleep(500); } catch (InterruptedException e) { - // TODO Auto-generated catch block e.printStackTrace(); } detectClients(); @@ -65,15 +67,14 @@ private void createClient(String clientHost, String clientVar) { return ""; } }, rv); - log.info("Sig" + sig.toString()); sig.change().addHandler((oldValue, newValue) -> createTaskLists(newValue)); myClients.modify((old) -> old.add(clientVar + "@" + clientHost)); } - private void createTaskLists(String message) { - if (message != null) { - String newDev = message.split(":")[0]; - String newTest = message.split(":")[1]; + private void createTaskLists(String newValue) { + if (newValue != null) { + String newDev = newValue.split(":")[0]; + String newTest = newValue.split(":")[1]; if (devTask == null) { devTask = new Var("taskDevs", ""); } @@ -81,14 +82,13 @@ private void createTaskLists(String message) { testTask = new Var("taskTests", ""); } // Set vars for remote querying - devTask.set(newDev); - testTask.set(newTest); + devTask.set(devTask.get().toString() + newDev); + testTask.set(testTask.get().toString() + newTest); } } private final void startServerIfNeeded() { if (!serverStarted) { - log.info("M: server started"); ServerLauncher.start(); serverStarted = true; } diff --git a/Dream2/src/examples/java/dream/examples/taskBoard/TaskReview.java b/Dream2/src/examples/java/dream/examples/taskBoard/TaskReview.java index b6cc5a6..0a17e64 100644 --- a/Dream2/src/examples/java/dream/examples/taskBoard/TaskReview.java +++ b/Dream2/src/examples/java/dream/examples/taskBoard/TaskReview.java @@ -27,12 +27,13 @@ public class TaskReview { public TaskReview() { Consts.hostName = "QueryClient"; log.setLevel(Level.ALL); - log.addHandler(log.getGlobal().getHandlers()[0]); + log.addHandler(Logger.getGlobal().getHandlers()[0]); myServer = new Var>("from_server", new ArrayList()); detectQueryResults(); } private void detectQueryResults() { + log.info("In TaskReview"); Set vars = DreamClient.instance.listVariables(); vars.stream().map(x -> new Pair(x.split("@")[1], x.split("@")[0])) .filter(x -> !myServer.get().contains(x.getSecond() + "@" + x.getFirst()) @@ -55,37 +56,35 @@ private void detectQueryResults() { private void updateDevQuery(String host, String value) { RemoteVar rv = new RemoteVar(host, value); - Signal sig = new Signal("received_" + host, () -> { if (rv.get() != null) { - log.fine("M: not empty " + rv.get()); return rv.get(); } else { return ""; } }, rv); - - sig.change().addHandler((oldValue, newValue) -> showQuery(newValue)); + sig.change().addHandler((oldValue, newValue) -> showDevQuery(newValue)); myServer.modify((old) -> old.add(value + "@" + host)); } private void updateTestQuery(String host, String value) { RemoteVar rv = new RemoteVar(host, value); - Signal sig = new Signal("received_" + host, () -> { if (rv.get() != null) { - log.fine("M: not empty " + rv.get()); return rv.get(); } else { return ""; } }, rv); - - sig.change().addHandler((oldValue, newValue) -> showQuery(newValue)); + sig.change().addHandler((oldValue, newValue) -> showTestQuery(newValue)); myServer.modify((old) -> old.add(value + "@" + host)); } - private void showQuery(String x) { + private void showTestQuery(String x) { + log.info("showQuery:" + x); + } + + private void showDevQuery(String x) { log.info("showQuery:" + x); } diff --git a/Dream2/src/examples/java/dream/examples/taskBoard/Tasks.java b/Dream2/src/examples/java/dream/examples/taskBoard/Tasks.java index 0196b54..c8f3317 100644 --- a/Dream2/src/examples/java/dream/examples/taskBoard/Tasks.java +++ b/Dream2/src/examples/java/dream/examples/taskBoard/Tasks.java @@ -16,6 +16,7 @@ public static void main(String[] args) { public Tasks() { Consts.hostName = "host1"; + System.out.println("In Tasks"); Var v = new Var("toServerVar", "D1:T1"); try { int i = 0; From 681158f6e03005a4f49f556402f6e9cdd06dbc24 Mon Sep 17 00:00:00 2001 From: "Mia.Min.Yang" Date: Fri, 13 May 2016 21:50:17 +0200 Subject: [PATCH 072/161] Add two more hosts for testing. --- .../dream/examples/taskBoard/ServerHost.java | 2 +- .../dream/examples/taskBoard/TaskReview.java | 5 ++-- .../java/dream/examples/taskBoard/Tasks.java | 26 +++++++++++-------- 3 files changed, 19 insertions(+), 14 deletions(-) diff --git a/Dream2/src/examples/java/dream/examples/taskBoard/ServerHost.java b/Dream2/src/examples/java/dream/examples/taskBoard/ServerHost.java index c7f4e36..4ccd2ea 100644 --- a/Dream2/src/examples/java/dream/examples/taskBoard/ServerHost.java +++ b/Dream2/src/examples/java/dream/examples/taskBoard/ServerHost.java @@ -44,7 +44,6 @@ public ServerHost() { } private void detectClients() { - log.info("inside ServerHost"); Set vars = DreamClient.instance.listVariables(); vars.stream().map(x -> new Pair(x.split("@")[1], x.split("@")[0])) .filter(x -> !myClients.get().contains(x.getSecond() + "@" + x.getFirst()) @@ -55,6 +54,7 @@ private void detectClients() { } catch (InterruptedException e) { e.printStackTrace(); } + log.info("inside ServerHost:" + vars.toString()); detectClients(); } diff --git a/Dream2/src/examples/java/dream/examples/taskBoard/TaskReview.java b/Dream2/src/examples/java/dream/examples/taskBoard/TaskReview.java index 0a17e64..a5f157c 100644 --- a/Dream2/src/examples/java/dream/examples/taskBoard/TaskReview.java +++ b/Dream2/src/examples/java/dream/examples/taskBoard/TaskReview.java @@ -19,6 +19,7 @@ * @description Review the tasks. */ public class TaskReview { + private final Logger log = Logger.getLogger("MagtClient"); private Var> myServer = null; RemoteVar devTasks = null; @@ -81,11 +82,11 @@ private void updateTestQuery(String host, String value) { } private void showTestQuery(String x) { - log.info("showQuery:" + x); + log.info("showTestQuery:" + x); } private void showDevQuery(String x) { - log.info("showQuery:" + x); + log.info("showDevQuery:" + x); } public static void main(String[] args) { diff --git a/Dream2/src/examples/java/dream/examples/taskBoard/Tasks.java b/Dream2/src/examples/java/dream/examples/taskBoard/Tasks.java index c8f3317..56879ba 100644 --- a/Dream2/src/examples/java/dream/examples/taskBoard/Tasks.java +++ b/Dream2/src/examples/java/dream/examples/taskBoard/Tasks.java @@ -15,18 +15,22 @@ public static void main(String[] args) { } public Tasks() { - Consts.hostName = "host1"; - System.out.println("In Tasks"); - Var v = new Var("toServerVar", "D1:T1"); - try { - int i = 0; - while (i < 10) { - Thread.sleep(1000); - v.set("D" + i + ":" + "T" + i); - i++; + int j = 0; + while (j < 3) { + Consts.hostName = "host" + j; + System.out.println("In Tasks"); + Var v = new Var("toServerVar", "D1:T1"); + try { + int i = 0; + while (i + j < 10) { + Thread.sleep(1000); + v.set("D" + i + "From host" + j + ":" + "T" + i + "From host" + j); + i++; + } + } catch (InterruptedException e) { + e.printStackTrace(); } - } catch (InterruptedException e) { - e.printStackTrace(); + j++; } } From 49937b231f1deee363e4937987a25bef7aa2d031 Mon Sep 17 00:00:00 2001 From: "Mia.Min.Yang" Date: Fri, 13 May 2016 22:42:10 +0200 Subject: [PATCH 073/161] little Update. --- .../src/examples/java/dream/examples/taskBoard/ServerHost.java | 2 +- .../src/examples/java/dream/examples/taskBoard/TaskReview.java | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/Dream2/src/examples/java/dream/examples/taskBoard/ServerHost.java b/Dream2/src/examples/java/dream/examples/taskBoard/ServerHost.java index 4ccd2ea..bc064d4 100644 --- a/Dream2/src/examples/java/dream/examples/taskBoard/ServerHost.java +++ b/Dream2/src/examples/java/dream/examples/taskBoard/ServerHost.java @@ -28,7 +28,7 @@ public class ServerHost { private Var devTask = null; private Var testTask = null; - public static void main(String[] args) { + public static void main(String... args) { new ServerHost(); } diff --git a/Dream2/src/examples/java/dream/examples/taskBoard/TaskReview.java b/Dream2/src/examples/java/dream/examples/taskBoard/TaskReview.java index a5f157c..f2511b3 100644 --- a/Dream2/src/examples/java/dream/examples/taskBoard/TaskReview.java +++ b/Dream2/src/examples/java/dream/examples/taskBoard/TaskReview.java @@ -19,7 +19,6 @@ * @description Review the tasks. */ public class TaskReview { - private final Logger log = Logger.getLogger("MagtClient"); private Var> myServer = null; RemoteVar devTasks = null; From 65a6d1ad215d82b6858e67ff50df4988349b109f Mon Sep 17 00:00:00 2001 From: rkamath3 Date: Sat, 14 May 2016 02:36:09 +0200 Subject: [PATCH 074/161] Implementation for distributed Task has been added --- .../dream/examples/tasks/DeligatProcess.java | 28 +++- .../dream/examples/tasks/MasterProcess.java | 32 +++- .../java/dream/examples/tasks/Message.java | 50 +++++++ .../java/dream/examples/tasks/Task.java | 53 +++++++ .../examples/tasks/UiUpdatesListner.java | 12 ++ .../dream/examples/tasks/WorkerHelper.java | 128 ++++++++++++++++ .../dream/examples/tasks/WorkerProcess.java | 140 ++++++++++++------ 7 files changed, 387 insertions(+), 56 deletions(-) create mode 100644 Dream2/src/examples/java/dream/examples/tasks/Message.java create mode 100644 Dream2/src/examples/java/dream/examples/tasks/UiUpdatesListner.java create mode 100644 Dream2/src/examples/java/dream/examples/tasks/WorkerHelper.java diff --git a/Dream2/src/examples/java/dream/examples/tasks/DeligatProcess.java b/Dream2/src/examples/java/dream/examples/tasks/DeligatProcess.java index 88d4388..e3c2cd0 100644 --- a/Dream2/src/examples/java/dream/examples/tasks/DeligatProcess.java +++ b/Dream2/src/examples/java/dream/examples/tasks/DeligatProcess.java @@ -15,6 +15,7 @@ public class DeligatProcess { static int i; + int clock; /** * @param args */ @@ -39,23 +40,38 @@ public DeligatProcess(String processName, String host) { this.setProcessName(processName); } - public static void main(String[] args) { + public DeligatProcess() { + // TODO Auto-generated constructor stub + } + private void init() { Consts.hostName = "Host2"; - RemoteVar rv = new RemoteVar("Host1", "TASK"); - Var myVar = new Var("TASK_ASSIGNED", null); + RemoteVar rv = new RemoteVar("Host1", "TASK"); + Var myVar = new Var("TASK_ASSIGNED", null); - Signal s = new Signal("s", () -> { + Signal s = new Signal("s", () -> { return rv.get(); } , rv); // Register a handler which will be executed upon receiving the signal s.change().addHandler((oldVal, val) -> { - - val.setAssignee(i++ + ""); + clock++; + try { + Thread.sleep(1000); + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + val.getTask().setAssignee(i++ % 10 + ""); + val.setClock(val.getClock() + "@p2:" + clock); myVar.set(val); }); } + public static void main(String[] args) { + new DeligatProcess().init(); + + } + } diff --git a/Dream2/src/examples/java/dream/examples/tasks/MasterProcess.java b/Dream2/src/examples/java/dream/examples/tasks/MasterProcess.java index ecda745..685c0bf 100644 --- a/Dream2/src/examples/java/dream/examples/tasks/MasterProcess.java +++ b/Dream2/src/examples/java/dream/examples/tasks/MasterProcess.java @@ -12,19 +12,28 @@ */ public class MasterProcess { - /** - * @param args - */ - public static void main(String[] args) { + private void init() { Consts.hostName = "Host1"; - - Var myVar = new Var("TASK", new Task("Task1")); + int clock = 0; + Var initTask = new Var("TASK", null); + Var initTask1 = new Var("TASK1", null); try { int i = 0; while (true) { - Thread.sleep(2000); - myVar.set(new Task("Task" + i)); + Message m = new Message(); + Thread.sleep(5000); + Task t = new Task("Task" + i); + t.setId(1000 + i); + m.setTask(t); + clock++; + m.setClock("@p1:" + clock); + initTask.set(m); + + t.setClock(clock); + t.setDescription("This is " + i + "th task"); + + initTask1.set(m); i++; } } catch (Exception e) { @@ -33,4 +42,11 @@ public static void main(String[] args) { } } + /** + * @param args + */ + public static void main(String[] args) { + new MasterProcess().init(); + } + } diff --git a/Dream2/src/examples/java/dream/examples/tasks/Message.java b/Dream2/src/examples/java/dream/examples/tasks/Message.java new file mode 100644 index 0000000..f8dbf04 --- /dev/null +++ b/Dream2/src/examples/java/dream/examples/tasks/Message.java @@ -0,0 +1,50 @@ +/** + * + */ +package dream.examples.tasks; + +import java.io.Serializable; + +/** + * Message format used for communication between different nodes + * + * @author Ram + * + */ +public class Message implements Serializable { + + private static final long serialVersionUID = -9119849212879479791L; + Task task; + String clock; + + /** + * @return the task + */ + public Task getTask() { + return task; + } + + /** + * @param task + * the task to set + */ + public void setTask(Task task) { + this.task = task; + } + + /** + * @return the clock + */ + public String getClock() { + return clock; + } + + /** + * @param clock + * the clock to set + */ + public void setClock(String clock) { + this.clock = clock; + } + +} diff --git a/Dream2/src/examples/java/dream/examples/tasks/Task.java b/Dream2/src/examples/java/dream/examples/tasks/Task.java index 0467b73..b8ee020 100644 --- a/Dream2/src/examples/java/dream/examples/tasks/Task.java +++ b/Dream2/src/examples/java/dream/examples/tasks/Task.java @@ -10,8 +10,61 @@ * */ public class Task extends ArrayList { + /** + * + */ + private static final long serialVersionUID = 7635579009380741902L; private String name; private String assignee; + private int id; + + /** + * @return the id + */ + public int getId() { + return id; + } + + /** + * @param id + * the id to set + */ + public void setId(int id) { + this.id = id; + } + + /** + * @return the clock + */ + public int getClock() { + return clock; + } + + /** + * @param clock + * the clock to set + */ + public void setClock(int clock) { + this.clock = clock; + } + + /** + * @return the description + */ + public String getDescription() { + return description; + } + + /** + * @param description + * the description to set + */ + public void setDescription(String description) { + this.description = description; + } + + private int clock; + private String description; public Task(String name) { this.setName(name); diff --git a/Dream2/src/examples/java/dream/examples/tasks/UiUpdatesListner.java b/Dream2/src/examples/java/dream/examples/tasks/UiUpdatesListner.java new file mode 100644 index 0000000..32df328 --- /dev/null +++ b/Dream2/src/examples/java/dream/examples/tasks/UiUpdatesListner.java @@ -0,0 +1,12 @@ +/** + * + */ +package dream.examples.tasks; + +/** + * @author Ram + * + */ +public interface UiUpdatesListner { + public void updateTasks(String task); +} diff --git a/Dream2/src/examples/java/dream/examples/tasks/WorkerHelper.java b/Dream2/src/examples/java/dream/examples/tasks/WorkerHelper.java new file mode 100644 index 0000000..d58e5e3 --- /dev/null +++ b/Dream2/src/examples/java/dream/examples/tasks/WorkerHelper.java @@ -0,0 +1,128 @@ +/** + * + */ +package dream.examples.tasks; + +import java.util.ArrayList; +import java.util.HashMap; + +import dream.client.RemoteVar; +import dream.client.Signal; +import dream.client.Var; +import dream.common.Consts; + +/** + * @author Ram + * + */ +public class WorkerHelper implements Runnable { + static int i = 0; + int localClock = 0; + UiUpdatesListner listner; + ArrayList> disClock = new ArrayList<>(); + HashMap p1 = new HashMap(); + HashMap p2 = new HashMap(); + HashMap p3 = new HashMap(); + Var complexEvent = new Var("COMPEVENT", null); + /** + * @param args + */ + private String processName; + + /** + * @return the processName + */ + public String getProcessName() { + return processName; + } + + /** + * @param processName + * the processName to set + */ + public void setProcessName(String processName) { + this.processName = processName; + } + + public WorkerHelper(String processName, String host) { + this.setProcessName(processName); + } + + public WorkerHelper() { + // TODO Auto-generated constructor stub + } + + public void isEvent() { + boolean COMPLEX_EVENT = false; + for (Integer i : p1.keySet()) { + for (Integer j : p2.keySet()) { + COMPLEX_EVENT = (p2.get(j).getId() == p1.get(i).getId() ? true : false); + if (COMPLEX_EVENT) { + Task task = p1.get(i); + task.setAssignee(p2.get(j).getAssignee()); + p1.remove(i); + p2.remove(j); + complexEvent.set(task); + break; + } + } + } + } + + public void run() { + + Consts.hostName = "Host3"; + + RemoteVar task = new RemoteVar("Host1", "TASK1"); + RemoteVar taskDeligated = new RemoteVar("Host2", "TASK_ASSIGNED"); + + Signal signalFromMaster = new Signal("s", () -> { + return task.get(); + } , task); + Signal signalFromDeligator = new Signal("s1", () -> { + return taskDeligated.get(); + } , taskDeligated); + + Signal signalForComplexEvent = new Signal("s2", () -> { + return complexEvent.get(); + } , complexEvent); + + // Register a handler which will be executed upon receiving the signal + // from master process + signalFromMaster.change().addHandler((oldVal, val) -> { + Task t = (Task) val.getTask(); + p1.put(t.getClock(), t); + if (t != null) { + System.out.println("[MASTER]\nAssignee:" + t.getAssignee() + "\nTaskName: " + t.getName() + + "\nDescription " + t.getDescription() + " \nAnnotated clock: " + val.getClock()); + + System.out.println("_________________________________________________"); + } + isEvent(); + }); + + // Register a handler which will be executed upon receiving the signal + // from delegate process + signalFromDeligator.change().addHandler((oldVal, val) -> { + Task t = (Task) val.getTask(); + if (t != null) { + System.out.println("[DELEGATOR]\nTask Id:" + t.getId() + "\nAssignee:" + t.getAssignee() + + "\nTaskName: " + t.getName() + "\nDescription " + t.getDescription() + "\nAnnotated clock: " + + val.getClock()); + System.out.println("_________________________________________________"); + } + p2.put(t.getClock(), t); + isEvent(); + }); + signalForComplexEvent.change().addHandler((oldVal, val) -> { + Task t = val; + listner.updateTasks("Task Id:" + t.getId() + "\nAssignee:" + t.getAssignee() + "\nTaskName: " + t.getName() + + "\nDescription " + t.getDescription()); + }); + } + + public void addListners(WorkerProcess dsUi) { + this.listner = dsUi; + } + +} diff --git a/Dream2/src/examples/java/dream/examples/tasks/WorkerProcess.java b/Dream2/src/examples/java/dream/examples/tasks/WorkerProcess.java index f0e18ba..f3df503 100644 --- a/Dream2/src/examples/java/dream/examples/tasks/WorkerProcess.java +++ b/Dream2/src/examples/java/dream/examples/tasks/WorkerProcess.java @@ -3,65 +3,121 @@ */ package dream.examples.tasks; -import dream.client.RemoteVar; -import dream.client.Signal; -import dream.common.Consts; +import javax.swing.JFrame; /** - * @author Ram * + * @author kamathar */ -public class WorkerProcess { - static int i = 0; - /** - * @param args - */ - private String processName; +public class WorkerProcess implements UiUpdatesListner { + private javax.swing.JFrame mainFrame; + private javax.swing.JPanel DisplayPanel; + private javax.swing.JTextArea infoTextArea; + private javax.swing.JScrollPane jScrollPane1; + private javax.swing.JLabel infoLabel; + WorkerHelper manager; /** - * @return the processName + * Creates new form WorkerProcess */ - public String getProcessName() { - return processName; + public WorkerProcess(WorkerHelper manager2) { + this.manager = manager2; + this.manager.addListners(this); + initComponents(); + } /** - * @param processName - * the processName to set + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. */ - public void setProcessName(String processName) { - this.processName = processName; - } + private void initComponents() { + mainFrame = new JFrame("Distributed Task Management"); + mainFrame.setResizable(true); + mainFrame.setSize(300, 100); + DisplayPanel = new javax.swing.JPanel(); + jScrollPane1 = new javax.swing.JScrollPane(); + infoTextArea = new javax.swing.JTextArea(); - public WorkerProcess(String processName, String host) { - this.setProcessName(processName); - } + infoLabel = new javax.swing.JLabel(); - public static void main(String[] args) { + mainFrame.setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE); + infoTextArea.setEditable(false); + infoTextArea.setColumns(100); + infoTextArea.setRows(20); + jScrollPane1.setViewportView(infoTextArea); - Consts.hostName = "Host3"; + javax.swing.GroupLayout DisplayPanelLayout = new javax.swing.GroupLayout(DisplayPanel); + DisplayPanel.setLayout(DisplayPanelLayout); + DisplayPanelLayout + .setHorizontalGroup(DisplayPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(DisplayPanelLayout.createSequentialGroup().addContainerGap() + .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 379, Short.MAX_VALUE) + .addContainerGap())); + DisplayPanelLayout.setVerticalGroup(DisplayPanelLayout + .createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING).addGroup(DisplayPanelLayout + .createSequentialGroup().addGap(0, 0, 0).addComponent(jScrollPane1).addContainerGap())); - RemoteVar task = new RemoteVar("Host1", "TASK"); - RemoteVar taskDeligated = new RemoteVar("Host2", "TASK_ASSIGNED"); + infoLabel.setFont(new java.awt.Font("Times New Roman", 1, 12)); // NOI18N + infoLabel.setText("Task List"); - Signal signalFromMaster = new Signal("s", () -> { - return task.get(); - } , task); - Signal signalFromDeligator = new Signal("s1", () -> { - return taskDeligated.get(); - } , taskDeligated); + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(mainFrame.getContentPane()); + mainFrame.getContentPane().setLayout(layout); + layout.setHorizontalGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup().addContainerGap() + .addComponent(DisplayPanel, javax.swing.GroupLayout.PREFERRED_SIZE, + javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGap(18, 18, 18).addGap(25, 25, 25)) + .addGroup(layout.createSequentialGroup().addGap(24, 24, 24) + .addComponent(infoLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 93, + javax.swing.GroupLayout.PREFERRED_SIZE) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))); + layout.setVerticalGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup().addContainerGap().addComponent(infoLabel).addGap(3, 3, 3) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING).addComponent( + DisplayPanel, javax.swing.GroupLayout.DEFAULT_SIZE, + javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addContainerGap())); - // Register a handler which will be executed upon receiving the signal - // from master process - signalFromMaster.change().addHandler((oldVal, val) -> { - System.out.println("FROM MASTER : " + val.getAssignee() + " " + val.getName()); - }); + mainFrame.setVisible(true); + mainFrame.pack(); + } + + void initUi() { + + try { + for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) { + if ("Nimbus".equals(info.getName())) { + javax.swing.UIManager.setLookAndFeel(info.getClassName()); + break; + } + } + } catch (ClassNotFoundException ex) { + java.util.logging.Logger.getLogger(WorkerProcess.class.getName()).log(java.util.logging.Level.SEVERE, null, ex); + } catch (InstantiationException ex) { + java.util.logging.Logger.getLogger(WorkerProcess.class.getName()).log(java.util.logging.Level.SEVERE, null, ex); + } catch (IllegalAccessException ex) { + java.util.logging.Logger.getLogger(WorkerProcess.class.getName()).log(java.util.logging.Level.SEVERE, null, ex); + } catch (javax.swing.UnsupportedLookAndFeelException ex) { + java.util.logging.Logger.getLogger(WorkerProcess.class.getName()).log(java.util.logging.Level.SEVERE, null, ex); + } - // Register a handler which will be executed upon receiving the signal - // from delegate process - signalFromDeligator.change().addHandler((oldVal, val) -> { - System.out.println("FROM DELIGATOR : " + val.getAssignee() + " " + val.getName()); - }); } -} + @Override + public void updateTasks(String task) { + infoTextArea.append("\n" + task); + infoTextArea.append("\n**************************************************************"); + mainFrame.repaint(); + mainFrame.revalidate(); + } + + public static void main(String args[]) { + WorkerHelper manager = new WorkerHelper(); + WorkerProcess snapshotUi = new WorkerProcess(manager); + snapshotUi.initUi(); + Thread S = new Thread(manager); + S.start(); + } +} \ No newline at end of file From 81e7232e1d60a90ea343cc9a15b634bd279aee3b Mon Sep 17 00:00:00 2001 From: rkamath3 Date: Sat, 14 May 2016 02:37:57 +0200 Subject: [PATCH 075/161] moved startInfra class to util --- .../java/dream/examples/util/StartInfra.java | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 Dream2/src/examples/java/dream/examples/util/StartInfra.java diff --git a/Dream2/src/examples/java/dream/examples/util/StartInfra.java b/Dream2/src/examples/java/dream/examples/util/StartInfra.java new file mode 100644 index 0000000..104e727 --- /dev/null +++ b/Dream2/src/examples/java/dream/examples/util/StartInfra.java @@ -0,0 +1,19 @@ +package dream.examples.util; + +import dream.locking.LockManagerLauncher; +import dream.server.ServerLauncher; + +public class StartInfra { + + public static void main(String args[]) { + try { + // Start the Server + ServerLauncher.start(); + // Start the LockManager + LockManagerLauncher.start(); + } catch (Exception e) { + // TODO Auto-generated catch block + + } + } +} From 323ddf006b5a5c01b241988ab889f3862e10e704 Mon Sep 17 00:00:00 2001 From: "Mia.Min.Yang" Date: Sun, 15 May 2016 21:24:42 +0200 Subject: [PATCH 076/161] Update GUI for monitoring. Resonalbe name of files. --- .../{InitBoard.java => InitApp.java} | 0 .../dream/examples/taskBoard/Monitor.java | 129 ++++++++++++++++++ .../{ServerHost.java => ServerNode.java} | 0 .../{TaskReview.java => TaskReviewer.java} | 0 4 files changed, 129 insertions(+) rename Dream2/src/examples/java/dream/examples/taskBoard/{InitBoard.java => InitApp.java} (100%) create mode 100644 Dream2/src/examples/java/dream/examples/taskBoard/Monitor.java rename Dream2/src/examples/java/dream/examples/taskBoard/{ServerHost.java => ServerNode.java} (100%) rename Dream2/src/examples/java/dream/examples/taskBoard/{TaskReview.java => TaskReviewer.java} (100%) diff --git a/Dream2/src/examples/java/dream/examples/taskBoard/InitBoard.java b/Dream2/src/examples/java/dream/examples/taskBoard/InitApp.java similarity index 100% rename from Dream2/src/examples/java/dream/examples/taskBoard/InitBoard.java rename to Dream2/src/examples/java/dream/examples/taskBoard/InitApp.java diff --git a/Dream2/src/examples/java/dream/examples/taskBoard/Monitor.java b/Dream2/src/examples/java/dream/examples/taskBoard/Monitor.java new file mode 100644 index 0000000..242ca13 --- /dev/null +++ b/Dream2/src/examples/java/dream/examples/taskBoard/Monitor.java @@ -0,0 +1,129 @@ +package dream.examples.taskBoard; + +import java.awt.Container; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.logging.Logger; + +import javax.swing.GroupLayout; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JTextArea; +import javax.swing.JTextField; +import javax.swing.LayoutStyle; + +public class Monitor { + public static JTextField textField1; + static boolean flag = false; + static Logger log = Logger.getLogger("Monitor"); + + static boolean taskValid() { + return flag; + } + + public static void main(String[] args) { + javax.swing.SwingUtilities.invokeLater(new Runnable() { + public void run() { + new Monitor().initComponents(); + } + }); + } + + void initComponents() { + // JFormDesigner - Component initialization - DO NOT MODIFY + // //GEN-BEGIN:initComponents + // Generated using JFormDesigner Evaluation license - Min Yang + frame1 = new JFrame(); + textField1 = new JTextField(); + button1 = new JButton(); + textArea2 = new JTextArea(); + label1 = new JLabel(); + label2 = new JLabel(); + textArea3 = new JTextArea(); + + // ======== frame1 ======== + { + Container frame1ContentPane = frame1.getContentPane(); + + // ---- button1 ---- + button1.setText("Add Task"); + button1.addActionListener(new ButtonListener()); + button1.setActionCommand("ADD"); + + // ---- label1 ---- + label1.setText("Current devs:"); + + // ---- label2 ---- + label2.setText("Current tests:"); + + GroupLayout frame1ContentPaneLayout = new GroupLayout(frame1ContentPane); + frame1ContentPane.setLayout(frame1ContentPaneLayout); + frame1ContentPaneLayout.setHorizontalGroup(frame1ContentPaneLayout.createParallelGroup() + .addGroup(frame1ContentPaneLayout.createSequentialGroup().addGroup(frame1ContentPaneLayout + .createParallelGroup() + .addGroup(frame1ContentPaneLayout.createSequentialGroup().addGap(20, 20, 20).addComponent( + textField1, GroupLayout.PREFERRED_SIZE, 300, GroupLayout.PREFERRED_SIZE)) + .addGroup(frame1ContentPaneLayout.createSequentialGroup().addContainerGap().addComponent( + textArea3, GroupLayout.PREFERRED_SIZE, 300, GroupLayout.PREFERRED_SIZE)) + .addGroup(frame1ContentPaneLayout.createSequentialGroup().addContainerGap() + .addComponent(label1))) + .addGap(18, 18, 18) + .addGroup(frame1ContentPaneLayout.createParallelGroup() + .addComponent(textArea2, GroupLayout.PREFERRED_SIZE, 300, + GroupLayout.PREFERRED_SIZE) + .addComponent(button1).addComponent(label2)) + .addContainerGap(30, Short.MAX_VALUE))); + frame1ContentPaneLayout.setVerticalGroup(frame1ContentPaneLayout.createParallelGroup() + .addGroup(frame1ContentPaneLayout.createSequentialGroup().addGap(19, 19, 19) + .addGroup(frame1ContentPaneLayout.createParallelGroup(GroupLayout.Alignment.BASELINE) + .addComponent(button1).addComponent(textField1, GroupLayout.PREFERRED_SIZE, + GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)) + .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED, 200, Short.MAX_VALUE) + .addGroup(frame1ContentPaneLayout.createParallelGroup(GroupLayout.Alignment.BASELINE) + .addComponent(label2).addComponent(label1)) + .addGap(13, 13, 13) + .addGroup(frame1ContentPaneLayout.createParallelGroup(GroupLayout.Alignment.BASELINE) + .addComponent(textArea2, GroupLayout.PREFERRED_SIZE, 300, + GroupLayout.PREFERRED_SIZE) + .addComponent(textArea3, GroupLayout.PREFERRED_SIZE, 300, + GroupLayout.PREFERRED_SIZE)) + .addContainerGap())); + frame1.pack(); + frame1.setLocationRelativeTo(frame1.getOwner()); + frame1.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + frame1.setVisible(true); + } + // //GEN-END:initComponents + + } + + // JFormDesigner - Variables declaration - DO NOT MODIFY + // //GEN-BEGIN:variables + // Generated using JFormDesigner Evaluation license - Min Yang + private JFrame frame1; + private JButton button1; + private JTextArea textArea2; + private JLabel label1; + private JLabel label2; + private JTextArea textArea3; + + // JFormDesigner - End of variables declaration //GEN-END:variables + static class ButtonListener implements ActionListener { + + @Override + public void actionPerformed(ActionEvent paramActionEvent) { + if (paramActionEvent.getActionCommand() == "ADD") { + String toTasks = textField1.getText(); + // TODO task format valid + if (toTasks.contains(":") && toTasks.split(":")[0].matches("D\\d*") + && toTasks.split(":")[1].matches("T\\d*")) { + flag = true; + } else { + log.info("Wrong input pattern of tasks"); + } + flag = true; + } + } + } +} diff --git a/Dream2/src/examples/java/dream/examples/taskBoard/ServerHost.java b/Dream2/src/examples/java/dream/examples/taskBoard/ServerNode.java similarity index 100% rename from Dream2/src/examples/java/dream/examples/taskBoard/ServerHost.java rename to Dream2/src/examples/java/dream/examples/taskBoard/ServerNode.java diff --git a/Dream2/src/examples/java/dream/examples/taskBoard/TaskReview.java b/Dream2/src/examples/java/dream/examples/taskBoard/TaskReviewer.java similarity index 100% rename from Dream2/src/examples/java/dream/examples/taskBoard/TaskReview.java rename to Dream2/src/examples/java/dream/examples/taskBoard/TaskReviewer.java From c963c9ce1fccd0a07581c23b36e3ee8402fa3630 Mon Sep 17 00:00:00 2001 From: "Mia.Min.Yang" Date: Sun, 15 May 2016 21:27:59 +0200 Subject: [PATCH 077/161] Little update. --- .../dream/examples/taskBoard/InitApp.java | 3 + .../dream/examples/taskBoard/ServerNode.java | 8 +-- .../examples/taskBoard/TaskReviewer.java | 6 +- .../java/dream/examples/taskBoard/Tasks.java | 64 ++++++++++++++++++- 4 files changed, 72 insertions(+), 9 deletions(-) diff --git a/Dream2/src/examples/java/dream/examples/taskBoard/InitApp.java b/Dream2/src/examples/java/dream/examples/taskBoard/InitApp.java index 6a9c07b..4f7f9e1 100644 --- a/Dream2/src/examples/java/dream/examples/taskBoard/InitApp.java +++ b/Dream2/src/examples/java/dream/examples/taskBoard/InitApp.java @@ -9,3 +9,6 @@ * (..examples.taskBoard.Tasks.java), then run TaskReview.java * (..examples.taskBoard.ServerHost.java) */ +// TODO run the whole package together +public class InitApp { +} \ No newline at end of file diff --git a/Dream2/src/examples/java/dream/examples/taskBoard/ServerNode.java b/Dream2/src/examples/java/dream/examples/taskBoard/ServerNode.java index bc064d4..aff27e5 100644 --- a/Dream2/src/examples/java/dream/examples/taskBoard/ServerNode.java +++ b/Dream2/src/examples/java/dream/examples/taskBoard/ServerNode.java @@ -20,7 +20,7 @@ * @description run background tasks: read task, create task lists: development * and test etc. */ -public class ServerHost { +public class ServerNode { public static final String NAME = "ServerHost"; private boolean serverStarted = false; private Var> myClients = null; @@ -29,10 +29,10 @@ public class ServerHost { private Var testTask = null; public static void main(String... args) { - new ServerHost(); + new ServerNode(); } - public ServerHost() { + public ServerNode() { startServerIfNeeded(); log.setLevel(Level.ALL); log.addHandler(Logger.getGlobal().getHandlers()[0]); @@ -40,7 +40,7 @@ public ServerHost() { myClients = new Var>("Server_registered_clients", new ArrayList()); detectClients(); new Tasks(); - new TaskReview(); + new TaskReviewer(); } private void detectClients() { diff --git a/Dream2/src/examples/java/dream/examples/taskBoard/TaskReviewer.java b/Dream2/src/examples/java/dream/examples/taskBoard/TaskReviewer.java index f2511b3..c67758c 100644 --- a/Dream2/src/examples/java/dream/examples/taskBoard/TaskReviewer.java +++ b/Dream2/src/examples/java/dream/examples/taskBoard/TaskReviewer.java @@ -18,13 +18,13 @@ * @date May 13, 2016 * @description Review the tasks. */ -public class TaskReview { +public class TaskReviewer { private final Logger log = Logger.getLogger("MagtClient"); private Var> myServer = null; RemoteVar devTasks = null; RemoteVar testTasks = null; - public TaskReview() { + public TaskReviewer() { Consts.hostName = "QueryClient"; log.setLevel(Level.ALL); log.addHandler(Logger.getGlobal().getHandlers()[0]); @@ -89,7 +89,7 @@ private void showDevQuery(String x) { } public static void main(String[] args) { - new TaskReview(); + new TaskReviewer(); } } diff --git a/Dream2/src/examples/java/dream/examples/taskBoard/Tasks.java b/Dream2/src/examples/java/dream/examples/taskBoard/Tasks.java index 56879ba..dabd7db 100644 --- a/Dream2/src/examples/java/dream/examples/taskBoard/Tasks.java +++ b/Dream2/src/examples/java/dream/examples/taskBoard/Tasks.java @@ -1,5 +1,10 @@ package dream.examples.taskBoard; +import java.awt.event.WindowEvent; +import java.awt.event.WindowListener; + +import javax.swing.JFrame; + import dream.client.Var; import dream.common.Consts; @@ -9,9 +14,22 @@ * @date May 13, 2016 * @description Creating tasks. */ -public class Tasks { +public class Tasks extends JFrame implements WindowListener { + + /** + * Default UID + */ + private static final long serialVersionUID = 1L; + public static void main(String[] args) { + new Tasks(); + // TODO + /* + * javax.swing.SwingUtilities.invokeLater(new Runnable() { public void + * run() { Monitor user1 = new Monitor(); user1.initComponents(); if + * (Monitor.taskValid()) { new Tasks(); } } }); + */ } public Tasks() { @@ -19,7 +37,7 @@ public Tasks() { while (j < 3) { Consts.hostName = "host" + j; System.out.println("In Tasks"); - Var v = new Var("toServerVar", "D1:T1"); + Var v = new Var("toServerVar", ""); try { int i = 0; while (i + j < 10) { @@ -34,4 +52,46 @@ public Tasks() { } } + @Override + public void windowActivated(WindowEvent paramWindowEvent) { + // TODO Auto-generated method stub + + } + + @Override + public void windowClosed(WindowEvent paramWindowEvent) { + // TODO Auto-generated method stub + + } + + @Override + public void windowClosing(WindowEvent paramWindowEvent) { + // TODO Auto-generated method stub + + } + + @Override + public void windowDeactivated(WindowEvent paramWindowEvent) { + // TODO Auto-generated method stub + + } + + @Override + public void windowDeiconified(WindowEvent paramWindowEvent) { + // TODO Auto-generated method stub + + } + + @Override + public void windowIconified(WindowEvent paramWindowEvent) { + // TODO Auto-generated method stub + + } + + @Override + public void windowOpened(WindowEvent paramWindowEvent) { + // TODO Auto-generated method stub + + } + } From 648e0807504985057421636700605a9da98737e9 Mon Sep 17 00:00:00 2001 From: "Mia.Min.Yang" Date: Sun, 15 May 2016 22:30:58 +0200 Subject: [PATCH 078/161] Remove unused methods and add comments. --- .../dream/examples/taskBoard/Monitor.java | 6 +++ .../java/dream/examples/taskBoard/Tasks.java | 50 +------------------ 2 files changed, 8 insertions(+), 48 deletions(-) diff --git a/Dream2/src/examples/java/dream/examples/taskBoard/Monitor.java b/Dream2/src/examples/java/dream/examples/taskBoard/Monitor.java index 242ca13..cbce6dd 100644 --- a/Dream2/src/examples/java/dream/examples/taskBoard/Monitor.java +++ b/Dream2/src/examples/java/dream/examples/taskBoard/Monitor.java @@ -13,6 +13,12 @@ import javax.swing.JTextField; import javax.swing.LayoutStyle; +/** + * + * @author Min Yang + * @date May 15, 2016 + * @description + */ public class Monitor { public static JTextField textField1; static boolean flag = false; diff --git a/Dream2/src/examples/java/dream/examples/taskBoard/Tasks.java b/Dream2/src/examples/java/dream/examples/taskBoard/Tasks.java index dabd7db..e82bba2 100644 --- a/Dream2/src/examples/java/dream/examples/taskBoard/Tasks.java +++ b/Dream2/src/examples/java/dream/examples/taskBoard/Tasks.java @@ -1,8 +1,5 @@ package dream.examples.taskBoard; -import java.awt.event.WindowEvent; -import java.awt.event.WindowListener; - import javax.swing.JFrame; import dream.client.Var; @@ -14,7 +11,7 @@ * @date May 13, 2016 * @description Creating tasks. */ -public class Tasks extends JFrame implements WindowListener { +public class Tasks extends JFrame { /** * Default UID @@ -24,7 +21,7 @@ public class Tasks extends JFrame implements WindowListener { public static void main(String[] args) { new Tasks(); - // TODO + // TODO accept inputs of new tasks from Monitor /* * javax.swing.SwingUtilities.invokeLater(new Runnable() { public void * run() { Monitor user1 = new Monitor(); user1.initComponents(); if @@ -51,47 +48,4 @@ public Tasks() { j++; } } - - @Override - public void windowActivated(WindowEvent paramWindowEvent) { - // TODO Auto-generated method stub - - } - - @Override - public void windowClosed(WindowEvent paramWindowEvent) { - // TODO Auto-generated method stub - - } - - @Override - public void windowClosing(WindowEvent paramWindowEvent) { - // TODO Auto-generated method stub - - } - - @Override - public void windowDeactivated(WindowEvent paramWindowEvent) { - // TODO Auto-generated method stub - - } - - @Override - public void windowDeiconified(WindowEvent paramWindowEvent) { - // TODO Auto-generated method stub - - } - - @Override - public void windowIconified(WindowEvent paramWindowEvent) { - // TODO Auto-generated method stub - - } - - @Override - public void windowOpened(WindowEvent paramWindowEvent) { - // TODO Auto-generated method stub - - } - } From 18728c2e76821419f46c3b1e1b45a62e85f02f49 Mon Sep 17 00:00:00 2001 From: Tobias Becker Date: Tue, 17 May 2016 09:38:02 +0200 Subject: [PATCH 079/161] fixed a bug with null value on advertisement --- Dream2/src/main/java/dream/common/packets/content/Event.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dream2/src/main/java/dream/common/packets/content/Event.java b/Dream2/src/main/java/dream/common/packets/content/Event.java index 3a8c8e3..535def1 100755 --- a/Dream2/src/main/java/dream/common/packets/content/Event.java +++ b/Dream2/src/main/java/dream/common/packets/content/Event.java @@ -33,7 +33,7 @@ public final String getSignature() { @Override public String toString() { - return objectId + "@" + hostId + "(val = " + val.toString() + ")"; + return objectId + "@" + hostId + "(val = " + (val == null ? "null" : val.toString()) + ")"; } } From 712159e8b0927acea49502ecc7b7b1e6f94265c6 Mon Sep 17 00:00:00 2001 From: Tobias Becker Date: Wed, 18 May 2016 09:09:59 +0200 Subject: [PATCH 080/161] added missing connect to financial example --- .../java/dream/examples/financial/FinancialApp.java | 2 ++ .../examples/java/dream/examples/financial/Model1.java | 4 +++- .../examples/java/dream/examples/financial/Model2.java | 10 ++++++---- .../examples/java/dream/examples/financial/Model3.java | 10 ++++++---- 4 files changed, 17 insertions(+), 9 deletions(-) diff --git a/Dream2/src/examples/java/dream/examples/financial/FinancialApp.java b/Dream2/src/examples/java/dream/examples/financial/FinancialApp.java index 3817c33..0dc12e4 100644 --- a/Dream2/src/examples/java/dream/examples/financial/FinancialApp.java +++ b/Dream2/src/examples/java/dream/examples/financial/FinancialApp.java @@ -1,6 +1,7 @@ package dream.examples.financial; import dream.client.ChangeEventHandler; +import dream.client.DreamClient; import dream.client.RemoteVar; import dream.client.Signal; import dream.common.Consts; @@ -20,6 +21,7 @@ public static void main(String[] args) { public void start() { Consts.hostName = "Local"; + DreamClient.instance.connect(); try { Thread.sleep(2000); diff --git a/Dream2/src/examples/java/dream/examples/financial/Model1.java b/Dream2/src/examples/java/dream/examples/financial/Model1.java index 0625bf1..be25ed1 100644 --- a/Dream2/src/examples/java/dream/examples/financial/Model1.java +++ b/Dream2/src/examples/java/dream/examples/financial/Model1.java @@ -1,5 +1,6 @@ package dream.examples.financial; +import dream.client.DreamClient; import dream.client.RemoteVar; import dream.client.Signal; import dream.common.Consts; @@ -7,6 +8,7 @@ public class Model1 { public void start() { Consts.hostName = "Model1"; + DreamClient.instance.connect(); final RemoteVar marketIndex = new RemoteVar<>("InputModel", "marketIndex"); final RemoteVar stockOpts = new RemoteVar<>("InputModel", "stockOpts"); @@ -17,7 +19,7 @@ public void start() { } else { return marketIndex.get() * 2 + stockOpts.get(); } - } , marketIndex, stockOpts); + }, marketIndex, stockOpts); f1.change().addHandler((oldVal, newVal) -> System.out.println("New value for f1: " + newVal)); } diff --git a/Dream2/src/examples/java/dream/examples/financial/Model2.java b/Dream2/src/examples/java/dream/examples/financial/Model2.java index a5124e9..2802542 100644 --- a/Dream2/src/examples/java/dream/examples/financial/Model2.java +++ b/Dream2/src/examples/java/dream/examples/financial/Model2.java @@ -1,5 +1,6 @@ package dream.examples.financial; +import dream.client.DreamClient; import dream.client.RemoteVar; import dream.client.Signal; import dream.common.Consts; @@ -7,17 +8,18 @@ public class Model2 { public void start() { Consts.hostName = "Model2"; + DreamClient.instance.connect(); - RemoteVar marketIndex = new RemoteVar<>("InputModel", "marketIndex"); - RemoteVar stockOpts = new RemoteVar<>("InputModel", "stockOpts"); + final RemoteVar marketIndex = new RemoteVar<>("InputModel", "marketIndex"); + final RemoteVar stockOpts = new RemoteVar<>("InputModel", "stockOpts"); - Signal f2 = new Signal<>("f2", () -> { + final Signal f2 = new Signal<>("f2", () -> { if (marketIndex.get() == null || stockOpts.get() == null) { return null; } else { return marketIndex.get() + stockOpts.get() * 2; } - } , marketIndex, stockOpts); + }, marketIndex, stockOpts); f2.change().addHandler((oldVal, newVal) -> System.out.println("New value for f2: " + newVal)); } diff --git a/Dream2/src/examples/java/dream/examples/financial/Model3.java b/Dream2/src/examples/java/dream/examples/financial/Model3.java index c575ec6..311e482 100644 --- a/Dream2/src/examples/java/dream/examples/financial/Model3.java +++ b/Dream2/src/examples/java/dream/examples/financial/Model3.java @@ -1,5 +1,6 @@ package dream.examples.financial; +import dream.client.DreamClient; import dream.client.RemoteVar; import dream.client.Signal; import dream.common.Consts; @@ -7,17 +8,18 @@ public class Model3 { public void start() { Consts.hostName = "Model3"; + DreamClient.instance.connect(); - RemoteVar marketIndex = new RemoteVar<>("InputModel", "marketIndex"); - RemoteVar news = new RemoteVar<>("InputModel", "news"); + final RemoteVar marketIndex = new RemoteVar<>("InputModel", "marketIndex"); + final RemoteVar news = new RemoteVar<>("InputModel", "news"); - Signal f3 = new Signal<>("f3", () -> { + final Signal f3 = new Signal<>("f3", () -> { if (marketIndex.get() == null || news.get() == null) { return null; } else { return marketIndex.get() + news.get(); } - } , marketIndex, news); + }, marketIndex, news); f3.change().addHandler((oldVal, newVal) -> System.out.println("New value for f3: " + newVal)); } From 8879bf1aa591f2316cefeb6b1a764d379335aa5a Mon Sep 17 00:00:00 2001 From: Tobias Becker Date: Wed, 18 May 2016 09:24:55 +0200 Subject: [PATCH 081/161] removing duplicate code --- .../java/dream/examples/chat/Starter.java | 32 ++----------------- .../dream/examples/util/NewJvmHelper.java | 10 ++++-- .../java/dream/examples/util/Pair.java | 1 + 3 files changed, 10 insertions(+), 33 deletions(-) diff --git a/Dream2/src/examples/java/dream/examples/chat/Starter.java b/Dream2/src/examples/java/dream/examples/chat/Starter.java index 4e2ba73..a99b327 100644 --- a/Dream2/src/examples/java/dream/examples/chat/Starter.java +++ b/Dream2/src/examples/java/dream/examples/chat/Starter.java @@ -1,11 +1,10 @@ package dream.examples.chat; -import java.io.IOException; import java.util.ArrayList; -import java.util.concurrent.TimeUnit; import dream.examples.chat.core.Chat; import dream.examples.chat.core.ChatServer; +import dream.examples.util.NewJvmHelper; /** * Convenience class to start ChatServer and x Chats (x = CHAT_COUNT), each in @@ -37,7 +36,7 @@ private void start() { int x = 0; int y = 0; for (int i = 0; i < CHAT_COUNT; i++) { - startSecondJVM(Chat.class, getName(i), Integer.toString(x), Integer.toString(y)); + NewJvmHelper.startNewJVM(Chat.class, getName(i), Integer.toString(x), Integer.toString(y)); x += xStep; if (x >= 3 * xStep) { x = 0; @@ -89,31 +88,4 @@ private void onExit() { System.out.println("Destroying " + p.toString()); } } - - public void startSecondJVM(Class c, String... args) { - System.out.println("Starting " + c.getName() + " ..."); - String separator = System.getProperty("file.separator"); - String classpath = System.getProperty("java.class.path"); - String path = System.getProperty("java.home") + separator + "bin" + separator + "java"; - String[] arguments = new String[args.length + 4]; - arguments[0] = path; - arguments[1] = "-cp"; - arguments[2] = classpath; - arguments[3] = c.getName(); - for (int i = 0; i < args.length; i++) { - arguments[i + 4] = args[i]; - } - ProcessBuilder processBuilder = new ProcessBuilder(arguments).inheritIO(); - Process process; - try { - process = processBuilder.start(); - processes.add(process); - process.waitFor(1, TimeUnit.SECONDS); - } catch (IOException e) { - e.printStackTrace(); - } catch (InterruptedException e) { - e.printStackTrace(); - } - System.out.println(c.getName() + " started!"); - } } diff --git a/Dream2/src/examples/java/dream/examples/util/NewJvmHelper.java b/Dream2/src/examples/java/dream/examples/util/NewJvmHelper.java index 241f54c..f1473a1 100644 --- a/Dream2/src/examples/java/dream/examples/util/NewJvmHelper.java +++ b/Dream2/src/examples/java/dream/examples/util/NewJvmHelper.java @@ -5,12 +5,16 @@ import java.io.IOException; import java.util.concurrent.TimeUnit; +import java.util.logging.Logger; /** * @author Ram * */ public class NewJvmHelper { + + private static Logger logger = Logger.getGlobal(); + /** * * @param c @@ -21,8 +25,8 @@ public class NewJvmHelper { * @return process, which needs to be cleaned by the class which initializes * the new JVM. */ - public Process startNewJVM(Class c, String... args) { - System.out.println("Starting " + c.getName() + " ..."); + public static Process startNewJVM(Class c, String... args) { + logger.fine("Starting " + c.getName() + " ..."); String separator = System.getProperty("file.separator"); String classpath = System.getProperty("java.class.path"); String path = System.getProperty("java.home") + separator + "bin" + separator + "java"; @@ -44,7 +48,7 @@ public Process startNewJVM(Class c, String... args) { } catch (InterruptedException e) { e.printStackTrace(); } - System.out.println(c.getName() + " started!"); + logger.fine(c.getName() + " started!"); return process; } } diff --git a/Dream2/src/examples/java/dream/examples/util/Pair.java b/Dream2/src/examples/java/dream/examples/util/Pair.java index 754df33..caa061b 100644 --- a/Dream2/src/examples/java/dream/examples/util/Pair.java +++ b/Dream2/src/examples/java/dream/examples/util/Pair.java @@ -3,6 +3,7 @@ import java.io.Serializable; public class Pair implements Serializable { + private static final long serialVersionUID = 8685298368867903814L; private final S first; private final T second; From 68bab4ab5fd7f42e8943bfdcbe0a670d5eba7014 Mon Sep 17 00:00:00 2001 From: Tobias Becker Date: Wed, 18 May 2016 09:36:19 +0200 Subject: [PATCH 082/161] added a convencience client class for easier use of the framework --- .../java/dream/examples/chat/core/Chat.java | 17 +-- .../dream/examples/chat/core/ChatServer.java | 38 +---- .../java/dream/examples/chat/fifo/Chat.java | 2 +- .../java/dream/examples/form/Boss.java | 6 +- .../java/dream/examples/form/FormClient.java | 34 ++--- .../java/dream/examples/form/FormServer.java | 43 +----- .../java/dream/examples/form/Secretary.java | 6 +- .../java/dream/examples/util/Client.java | 132 ++++++++++++++++++ 8 files changed, 165 insertions(+), 113 deletions(-) create mode 100644 Dream2/src/examples/java/dream/examples/util/Client.java diff --git a/Dream2/src/examples/java/dream/examples/chat/core/Chat.java b/Dream2/src/examples/java/dream/examples/chat/core/Chat.java index f71e6fd..c80227e 100644 --- a/Dream2/src/examples/java/dream/examples/chat/core/Chat.java +++ b/Dream2/src/examples/java/dream/examples/chat/core/Chat.java @@ -5,7 +5,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.logging.Level; import java.util.logging.Logger; import java.util.stream.Collectors; @@ -13,12 +12,11 @@ import dream.client.RemoteVar; import dream.client.Signal; import dream.client.Var; -import dream.common.Consts; +import dream.examples.util.Client; import dream.examples.util.DependencyVisualization; -public class Chat { +public class Chat extends Client { - protected final String userName; private ChatGUI gui; private final Signal> onlineList; @@ -29,18 +27,13 @@ public class Chat { private Map> rooms = new HashMap<>(); private Map roomNames = new HashMap<>(); - protected final Logger logger; private int posX; private int posY; public Chat(String username, int window_x, int window_y) { - this.userName = username; - Consts.hostName = userName; + super(username); this.posX = window_x; this.posY = window_y; - logger = Logger.getLogger("Chat_" + userName); - logger.addHandler(Logger.getGlobal().getHandlers()[0]); - logger.setLevel(Level.ALL); // Establish new session with server RemoteVar> registeredClients = new RemoteVar>(ChatServer.NAME, @@ -106,7 +99,7 @@ private void setup() { fromServer.change().addHandler((oldValue, newValue) -> receivedServerMessage(newValue)); logger.fine("Setup: Starting GUI"); - gui = new ChatGUI(userName, posX, posY); + gui = new ChatGUI(getHostName(), posX, posY); gui.setListener(this); // main room: @@ -169,7 +162,7 @@ protected String newRoom(String roomName, int roomNumber) { } private void createConnection(int roomNumber, String roomName, String clientName, String clientVar) { - if (clientName.equals(userName)) + if (clientName.equals(getHostName())) return; RemoteVar r = new RemoteVar<>(clientName, clientVar); Signal s = new Signal<>(roomName + "_" + clientName, () -> { diff --git a/Dream2/src/examples/java/dream/examples/chat/core/ChatServer.java b/Dream2/src/examples/java/dream/examples/chat/core/ChatServer.java index 218b555..18a61f4 100644 --- a/Dream2/src/examples/java/dream/examples/chat/core/ChatServer.java +++ b/Dream2/src/examples/java/dream/examples/chat/core/ChatServer.java @@ -14,18 +14,13 @@ import dream.client.RemoteVar; import dream.client.Signal; import dream.client.Var; -import dream.common.Consts; +import dream.examples.util.Client; import dream.examples.util.DependencyVisualization; import dream.examples.util.Pair; -import dream.locking.LockManagerLauncher; -import dream.server.ServerLauncher; -public class ChatServer { +public class ChatServer extends Client { public static final String NAME = "ChatServer"; - private boolean serverStarted = false; - private boolean lockManagerStarted = false; - private final static SecureRandom r = new SecureRandom(); private final Var> clients; @@ -41,12 +36,9 @@ public static void main(String[] args) { } public ChatServer() { - startServerIfNeeded(); - startLockManagerIfNeeded(); - + super(NAME); logger.setLevel(Level.ALL); logger.addHandler(Logger.getGlobal().getHandlers()[0]); - Consts.hostName = NAME; clientVars = new HashMap<>(); rooms = new HashMap<>(); clients = new Var>(SERVER_REGISTERED_CLIENTS, new ArrayList()); @@ -177,28 +169,4 @@ protected void receivedMessage(String clientName, String message) { public static String getRandom() { return new BigInteger(130, r).toString(32); } - - private final void startServerIfNeeded() { - if (!serverStarted) { - ServerLauncher.start(); - serverStarted = true; - } - try { - Thread.sleep(500); - } catch (InterruptedException e) { - logger.log(Level.SEVERE, "Failed to wait for Server starting", e); - } - } - - private final void startLockManagerIfNeeded() { - if (!lockManagerStarted) { - LockManagerLauncher.start(); - lockManagerStarted = true; - } - try { - Thread.sleep(500); - } catch (final InterruptedException e) { - logger.log(Level.SEVERE, "Failed to wait for LockManager starting", e); - } - } } diff --git a/Dream2/src/examples/java/dream/examples/chat/fifo/Chat.java b/Dream2/src/examples/java/dream/examples/chat/fifo/Chat.java index ebeb1a7..a529577 100644 --- a/Dream2/src/examples/java/dream/examples/chat/fifo/Chat.java +++ b/Dream2/src/examples/java/dream/examples/chat/fifo/Chat.java @@ -18,7 +18,7 @@ public Chat(String username, int window_x, int window_y) { protected void sendChatMessage(int roomNumber, String message) { // increment VectorClock by 1 VectorClock roomClock = roomClocks.get(roomNumber); - roomClock.incrementClock(userName); + roomClock.incrementClock(getHostName()); // attach VectorClock to message String newMessage = roomClock.toString() + "|" + message; logger.fine("Sending message with VectorClock " + roomClock + ": " + message); diff --git a/Dream2/src/examples/java/dream/examples/form/Boss.java b/Dream2/src/examples/java/dream/examples/form/Boss.java index c6cd4ec..1d9f33f 100644 --- a/Dream2/src/examples/java/dream/examples/form/Boss.java +++ b/Dream2/src/examples/java/dream/examples/form/Boss.java @@ -8,6 +8,10 @@ public class Boss extends FormClient { public Boss() { super("Boss", "Euro/Hour"); + } + + @Override + protected void init() { eph = new Var<>("euro_per_hour", 8.5); } @@ -20,7 +24,7 @@ public void typedText(String typedText) { public static void main(String[] args) { Boss b = new Boss(); - b.init(); + b.start(); } } diff --git a/Dream2/src/examples/java/dream/examples/form/FormClient.java b/Dream2/src/examples/java/dream/examples/form/FormClient.java index 99c0496..9ece458 100644 --- a/Dream2/src/examples/java/dream/examples/form/FormClient.java +++ b/Dream2/src/examples/java/dream/examples/form/FormClient.java @@ -1,15 +1,14 @@ package dream.examples.form; import java.awt.Color; -import java.util.logging.Level; -import java.util.logging.Logger; +import java.util.Arrays; +import java.util.List; -import dream.client.DreamClient; import dream.client.RemoteVar; import dream.client.Signal; -import dream.common.Consts; +import dream.examples.util.Client; -public abstract class FormClient { +public abstract class FormClient extends Client { private RemoteVar salary; private RemoteVar settings; @@ -17,31 +16,20 @@ public abstract class FormClient { private Signal remoteSettings; private FormGUI gui; - protected final Logger logger; private String labelText; public FormClient(String name, String labelText) { - Consts.hostName = name; + super(name); this.labelText = labelText; - - logger = Logger.getLogger(name); - logger.setLevel(Level.ALL); - logger.addHandler(Logger.getGlobal().getHandlers()[0]); - - DreamClient.instance.connect(); } - protected void init() { - while (!DreamClient.instance.listVariables().contains("salary@FormServer") || // - !DreamClient.instance.listVariables().contains("settingsOkay@FormServer")) { - try { - Thread.sleep(500); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } + @Override + protected List waitForVars() { + return Arrays.asList("salary@FormServer", "settingsOkay@FormServer"); + } - gui = new FormGUI(Consts.hostName, labelText); + protected void start() { + gui = new FormGUI(getHostName(), labelText); gui.setListener(this); salary = new RemoteVar<>("FormServer", "salary"); diff --git a/Dream2/src/examples/java/dream/examples/form/FormServer.java b/Dream2/src/examples/java/dream/examples/form/FormServer.java index 4805604..1fc4e08 100644 --- a/Dream2/src/examples/java/dream/examples/form/FormServer.java +++ b/Dream2/src/examples/java/dream/examples/form/FormServer.java @@ -1,34 +1,21 @@ package dream.examples.form; import java.util.logging.Level; -import java.util.logging.Logger; import dream.client.DreamClient; import dream.client.RemoteVar; import dream.client.Signal; -import dream.common.Consts; -import dream.locking.LockManagerLauncher; -import dream.server.ServerLauncher; +import dream.examples.util.Client; -public class FormServer { +public class FormServer extends Client { public static final String NAME = "FormServer"; - private boolean serverStarted; - private boolean lockManagerStarted; - protected final Logger logger = Logger.getLogger(NAME); protected RemoteVar working_hours; protected RemoteVar euro_per_hour; public FormServer() { - startServerIfNeeded(); - startLockManagerIfNeeded(); - - logger.setLevel(Level.ALL); - logger.addHandler(Logger.getGlobal().getHandlers()[0]); - - Consts.hostName = NAME; - DreamClient.instance.connect(); + super("FormServer"); detectNewSession(); } @@ -101,28 +88,4 @@ protected void createDependencies() { public static void main(String[] args) { new FormServer(); } - - private final void startServerIfNeeded() { - if (!serverStarted) { - ServerLauncher.start(); - serverStarted = true; - } - try { - Thread.sleep(500); - } catch (InterruptedException e) { - logger.log(Level.SEVERE, "Failed to wait for Server starting", e); - } - } - - private final void startLockManagerIfNeeded() { - if (!lockManagerStarted) { - LockManagerLauncher.start(); - lockManagerStarted = true; - } - try { - Thread.sleep(500); - } catch (final InterruptedException e) { - logger.log(Level.SEVERE, "Failed to wait for LockManager starting", e); - } - } } diff --git a/Dream2/src/examples/java/dream/examples/form/Secretary.java b/Dream2/src/examples/java/dream/examples/form/Secretary.java index aa5baa2..9c2b9b3 100644 --- a/Dream2/src/examples/java/dream/examples/form/Secretary.java +++ b/Dream2/src/examples/java/dream/examples/form/Secretary.java @@ -8,6 +8,10 @@ public class Secretary extends FormClient { public Secretary() { super("Secretary", "Working Hours"); + } + + @Override + protected void init() { wh = new Var<>("working_hours", 5); } @@ -20,7 +24,7 @@ public void typedText(String typedText) { public static void main(String[] args) { Secretary s = new Secretary(); - s.init(); + s.start(); } } diff --git a/Dream2/src/examples/java/dream/examples/util/Client.java b/Dream2/src/examples/java/dream/examples/util/Client.java new file mode 100644 index 0000000..eda07c3 --- /dev/null +++ b/Dream2/src/examples/java/dream/examples/util/Client.java @@ -0,0 +1,132 @@ +package dream.examples.util; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; + +import dream.client.DreamClient; +import dream.common.Consts; +import dream.locking.LockManagerLauncher; +import dream.server.ServerLauncher; +import polimi.reds.broker.overlay.TCPTransport; + +/** + * A helper class for client implementations that ensures that a instance of the + * Dream Server and LockManager are running, as well as setting up basic + * infrastructure for clients. + * + * @author Tobias Becker + */ +public abstract class Client { + + private static boolean lockManagerStarted; + private static boolean serverStarted; + protected Logger logger; + + public Client(String name) { + // set up a logger + logger = Logger.getLogger(name); + + // make sure a DreamServer is running + startDream(); + + // set hostName + Consts.hostName = name; + + // connect to the dependency graph + DreamClient.instance.connect(); + + init(); + + // wait for vars needed by the client + try { + if (!waitForVars().isEmpty()) + logger.fine("Waiting for Vars: " + waitForVars()); + while (!allVarsAvailable()) { + Thread.sleep(500); + } + if (!waitForVars().isEmpty()) + logger.fine("Vars are now all available."); + logger.fine("Client initialization finished."); + Thread.sleep(500); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + private boolean allVarsAvailable() { + for (String var : waitForVars()) { + if (!DreamClient.instance.listVariables().contains(var)) + return false; + } + return true; + } + + /** + * Override this method if you need certain variables to be available before + * your computation can start. The constructor will block until they are on + * the dependency graph. + * + * @return a list of variables to be available before continuing + */ + protected List waitForVars() { + return new ArrayList(); + } + + /** + * Override this method if you want to initialize something before waiting + * for the vars in {@link waitForVars()}. + * + */ + protected void init() { + + } + + private void startDream() { + try { + TCPTransport test = new TCPTransport(Consts.serverPort); + test.start(); + test.stop(); + } catch (IOException e) { + serverStarted = true; + logger.fine("Server already running"); + } + try { + TCPTransport test = new TCPTransport(Consts.lockManagerPort); + test.start(); + test.stop(); + } catch (IOException e) { + lockManagerStarted = true; + logger.fine("LockManager already running"); + } + try { + startServer(); + startLockManager(); + } catch (InterruptedException e) { + logger.log(Level.SEVERE, "Failed to sleep", e); + } + } + + private static final void startServer() throws InterruptedException { + if (!serverStarted) { + ServerLauncher.start(); + serverStarted = true; + } + Thread.sleep(100); + } + + private static final void startLockManager() throws InterruptedException { + if (!lockManagerStarted) { + LockManagerLauncher.start(); + lockManagerStarted = true; + } + Thread.sleep(100); + } + + public String getHostName() { + return Consts.hostName; + } + +} From b0076850c746283506e7ded6163251ed54e7eba2 Mon Sep 17 00:00:00 2001 From: "Mia.Min.Yang" Date: Thu, 19 May 2016 11:02:33 +0200 Subject: [PATCH 083/161] Fixing dead code and add comments. --- Dream2/src/examples/java/dream/examples/taskBoard/Monitor.java | 2 +- .../src/examples/java/dream/examples/taskBoard/ServerNode.java | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/Dream2/src/examples/java/dream/examples/taskBoard/Monitor.java b/Dream2/src/examples/java/dream/examples/taskBoard/Monitor.java index cbce6dd..b8b4427 100644 --- a/Dream2/src/examples/java/dream/examples/taskBoard/Monitor.java +++ b/Dream2/src/examples/java/dream/examples/taskBoard/Monitor.java @@ -121,11 +121,11 @@ static class ButtonListener implements ActionListener { public void actionPerformed(ActionEvent paramActionEvent) { if (paramActionEvent.getActionCommand() == "ADD") { String toTasks = textField1.getText(); - // TODO task format valid if (toTasks.contains(":") && toTasks.split(":")[0].matches("D\\d*") && toTasks.split(":")[1].matches("T\\d*")) { flag = true; } else { + // TODO task format invalid handle log.info("Wrong input pattern of tasks"); } flag = true; diff --git a/Dream2/src/examples/java/dream/examples/taskBoard/ServerNode.java b/Dream2/src/examples/java/dream/examples/taskBoard/ServerNode.java index aff27e5..db3a257 100644 --- a/Dream2/src/examples/java/dream/examples/taskBoard/ServerNode.java +++ b/Dream2/src/examples/java/dream/examples/taskBoard/ServerNode.java @@ -39,8 +39,6 @@ public ServerNode() { Consts.hostName = NAME; myClients = new Var>("Server_registered_clients", new ArrayList()); detectClients(); - new Tasks(); - new TaskReviewer(); } private void detectClients() { From a5c3c6fbb37d938f7978aa61604b0a0a9adec78d Mon Sep 17 00:00:00 2001 From: "Mia.Min.Yang" Date: Thu, 19 May 2016 11:28:29 +0200 Subject: [PATCH 084/161] From imperative to declarative. --- .../dream/examples/taskBoard/ServerNode.java | 43 +++++++++++-------- .../examples/taskBoard/TaskReviewer.java | 1 - 2 files changed, 26 insertions(+), 18 deletions(-) diff --git a/Dream2/src/examples/java/dream/examples/taskBoard/ServerNode.java b/Dream2/src/examples/java/dream/examples/taskBoard/ServerNode.java index db3a257..2fbdd0a 100644 --- a/Dream2/src/examples/java/dream/examples/taskBoard/ServerNode.java +++ b/Dream2/src/examples/java/dream/examples/taskBoard/ServerNode.java @@ -65,26 +65,35 @@ private void createClient(String clientHost, String clientVar) { return ""; } }, rv); - sig.change().addHandler((oldValue, newValue) -> createTaskLists(newValue)); - myClients.modify((old) -> old.add(clientVar + "@" + clientHost)); - } - - private void createTaskLists(String newValue) { - if (newValue != null) { - String newDev = newValue.split(":")[0]; - String newTest = newValue.split(":")[1]; - if (devTask == null) { - devTask = new Var("taskDevs", ""); + // sig.change().addHandler((oldValue, newValue) -> + // createTaskLists(newValue)); + sig.change().addHandler((oldValue, newValue) -> { + if (newValue != null) { + String newDev = newValue.split(":")[0]; + String newTest = newValue.split(":")[1]; + if (devTask == null) { + devTask = new Var("taskDevs", ""); + } + if (testTask == null) { + testTask = new Var("taskTests", ""); + } + // Set vars for remote querying + devTask.set(devTask.get().toString() + newDev); + testTask.set(testTask.get().toString() + newTest); } - if (testTask == null) { - testTask = new Var("taskTests", ""); - } - // Set vars for remote querying - devTask.set(devTask.get().toString() + newDev); - testTask.set(testTask.get().toString() + newTest); - } + }); + myClients.modify((old) -> old.add(clientVar + "@" + clientHost)); } + /* + * private void createTaskLists(String newValue) { if (newValue != null) { + * String newDev = newValue.split(":")[0]; String newTest = + * newValue.split(":")[1]; if (devTask == null) { devTask = new + * Var("taskDevs", ""); } if (testTask == null) { testTask = new + * Var("taskTests", ""); } // Set vars for remote querying + * devTask.set(devTask.get().toString() + newDev); + * testTask.set(testTask.get().toString() + newTest); } } + */ private final void startServerIfNeeded() { if (!serverStarted) { ServerLauncher.start(); diff --git a/Dream2/src/examples/java/dream/examples/taskBoard/TaskReviewer.java b/Dream2/src/examples/java/dream/examples/taskBoard/TaskReviewer.java index c67758c..37249a7 100644 --- a/Dream2/src/examples/java/dream/examples/taskBoard/TaskReviewer.java +++ b/Dream2/src/examples/java/dream/examples/taskBoard/TaskReviewer.java @@ -48,7 +48,6 @@ private void detectQueryResults() { try { Thread.sleep(500); } catch (InterruptedException e) { - // TODO Auto-generated catch block e.printStackTrace(); } detectQueryResults(); From 237e91edbfe49f2251f4a1c97013855047e44757 Mon Sep 17 00:00:00 2001 From: "Mia.Min.Yang" Date: Thu, 19 May 2016 11:39:38 +0200 Subject: [PATCH 085/161] Declarative and single JVM. --- .../examples/taskBoard/TaskReviewer.java | 18 ++++++------- .../java/dream/examples/taskBoard/Tasks.java | 27 +++++++++---------- 2 files changed, 20 insertions(+), 25 deletions(-) diff --git a/Dream2/src/examples/java/dream/examples/taskBoard/TaskReviewer.java b/Dream2/src/examples/java/dream/examples/taskBoard/TaskReviewer.java index 37249a7..c9127d1 100644 --- a/Dream2/src/examples/java/dream/examples/taskBoard/TaskReviewer.java +++ b/Dream2/src/examples/java/dream/examples/taskBoard/TaskReviewer.java @@ -62,7 +62,10 @@ private void updateDevQuery(String host, String value) { return ""; } }, rv); - sig.change().addHandler((oldValue, newValue) -> showDevQuery(newValue)); + sig.change().addHandler((oldValue, newValue) -> { + // TODO show current dev tasks on monitor + log.info("Current dev tasks are:" + newValue); + }); myServer.modify((old) -> old.add(value + "@" + host)); } @@ -75,18 +78,13 @@ private void updateTestQuery(String host, String value) { return ""; } }, rv); - sig.change().addHandler((oldValue, newValue) -> showTestQuery(newValue)); + sig.change().addHandler((oldValue, newValue) -> { + // TODO show current dev tasks on monitor + log.info("Current test tasks are: " + newValue); + }); myServer.modify((old) -> old.add(value + "@" + host)); } - private void showTestQuery(String x) { - log.info("showTestQuery:" + x); - } - - private void showDevQuery(String x) { - log.info("showDevQuery:" + x); - } - public static void main(String[] args) { new TaskReviewer(); } diff --git a/Dream2/src/examples/java/dream/examples/taskBoard/Tasks.java b/Dream2/src/examples/java/dream/examples/taskBoard/Tasks.java index e82bba2..f5b26c3 100644 --- a/Dream2/src/examples/java/dream/examples/taskBoard/Tasks.java +++ b/Dream2/src/examples/java/dream/examples/taskBoard/Tasks.java @@ -30,22 +30,19 @@ public static void main(String[] args) { } public Tasks() { - int j = 0; - while (j < 3) { - Consts.hostName = "host" + j; - System.out.println("In Tasks"); - Var v = new Var("toServerVar", ""); - try { - int i = 0; - while (i + j < 10) { - Thread.sleep(1000); - v.set("D" + i + "From host" + j + ":" + "T" + i + "From host" + j); - i++; - } - } catch (InterruptedException e) { - e.printStackTrace(); + Consts.hostName = "host"; + System.out.println("In Tasks"); + Var v = new Var("toServerVar", ""); + try { + int i = 0; + while (i < 10) { + Thread.sleep(1000); + v.set("D" + i + ":" + "T" + i); + i++; } - j++; + } catch (InterruptedException e) { + e.printStackTrace(); } + } } From ad2aa91921bdd3491a45d53f69693568cacca2b8 Mon Sep 17 00:00:00 2001 From: Tobias Becker Date: Thu, 19 May 2016 14:42:25 +0200 Subject: [PATCH 086/161] fixed financial example --- .../java/dream/examples/financial/FinancialApp.java | 10 +++------- .../common/utils/IntraSourceDependencyDetector.java | 2 +- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/Dream2/src/examples/java/dream/examples/financial/FinancialApp.java b/Dream2/src/examples/java/dream/examples/financial/FinancialApp.java index 0dc12e4..ab804b8 100644 --- a/Dream2/src/examples/java/dream/examples/financial/FinancialApp.java +++ b/Dream2/src/examples/java/dream/examples/financial/FinancialApp.java @@ -29,11 +29,9 @@ public void start() { e.printStackTrace(); } - f1 = new RemoteVar<>("f1@Model1"); - f2 = new RemoteVar<>("f2@Model2"); - f3 = new RemoteVar<>("f3@Model3"); - - final RemoteVar model1 = new RemoteVar<>("Model1", "model1"); + f1 = new RemoteVar<>("Model1", "f1"); + f2 = new RemoteVar<>("Model2", "f2"); + f3 = new RemoteVar<>("Model3", "f3"); f1Signal = new Signal<>("f1Signal", () -> f1.get(), f1); f2Signal = new Signal<>("f2Signal", () -> f2.get(), f2); @@ -52,8 +50,6 @@ public void start() { System.out.println(f1.get()); System.out.println(f2.get()); System.out.println(f3.get()); - - System.out.println(model1.get()); } @Override diff --git a/Dream2/src/main/java/dream/common/utils/IntraSourceDependencyDetector.java b/Dream2/src/main/java/dream/common/utils/IntraSourceDependencyDetector.java index 9e18991..5056ea9 100755 --- a/Dream2/src/main/java/dream/common/utils/IntraSourceDependencyDetector.java +++ b/Dream2/src/main/java/dream/common/utils/IntraSourceDependencyDetector.java @@ -74,7 +74,7 @@ private final void storeRecommendationsFor(String expr, String initialExpr) { */ private final Set computeDependentSiblingsFor(String expr, String initialExpression) { return depGraph.getGraph().get(expr).stream().// - filter(dep -> relevantSources.get(dep).contains(initialExpression)).// + filter(dep -> relevantSources.getOrDefault(dep, new HashSet<>()).contains(initialExpression)).// collect(Collectors.toSet()); } From fd4248712e6cafe0ea32c037b412b706ecf2b352 Mon Sep 17 00:00:00 2001 From: Tobias Becker Date: Thu, 19 May 2016 15:05:07 +0200 Subject: [PATCH 087/161] using client class in financial example --- .../examples/financial/FinancialApp.java | 27 ++++++------ .../dream/examples/financial/InputModel.java | 41 +++---------------- .../java/dream/examples/financial/Model1.java | 22 ++++++---- .../java/dream/examples/financial/Model2.java | 22 ++++++---- .../java/dream/examples/financial/Model3.java | 23 +++++++---- 5 files changed, 67 insertions(+), 68 deletions(-) diff --git a/Dream2/src/examples/java/dream/examples/financial/FinancialApp.java b/Dream2/src/examples/java/dream/examples/financial/FinancialApp.java index ab804b8..77823f6 100644 --- a/Dream2/src/examples/java/dream/examples/financial/FinancialApp.java +++ b/Dream2/src/examples/java/dream/examples/financial/FinancialApp.java @@ -1,12 +1,19 @@ package dream.examples.financial; +import java.util.Arrays; +import java.util.List; + import dream.client.ChangeEventHandler; -import dream.client.DreamClient; import dream.client.RemoteVar; import dream.client.Signal; -import dream.common.Consts; +import dream.examples.util.Client; + +public class FinancialApp extends Client implements ChangeEventHandler { + + public FinancialApp() { + super("Local"); + } -public class FinancialApp implements ChangeEventHandler { private Signal f1Signal; private Signal f2Signal; private Signal f3Signal; @@ -19,16 +26,12 @@ public static void main(String[] args) { new FinancialApp().start(); } - public void start() { - Consts.hostName = "Local"; - DreamClient.instance.connect(); - - try { - Thread.sleep(2000); - } catch (final InterruptedException e) { - e.printStackTrace(); - } + @Override + protected List waitForVars() { + return Arrays.asList("f1@Model1", "f2@Model2", "f3@Model3"); + } + public void start() { f1 = new RemoteVar<>("Model1", "f1"); f2 = new RemoteVar<>("Model2", "f2"); f3 = new RemoteVar<>("Model3", "f3"); diff --git a/Dream2/src/examples/java/dream/examples/financial/InputModel.java b/Dream2/src/examples/java/dream/examples/financial/InputModel.java index dcb9fc1..42f6b90 100644 --- a/Dream2/src/examples/java/dream/examples/financial/InputModel.java +++ b/Dream2/src/examples/java/dream/examples/financial/InputModel.java @@ -3,24 +3,19 @@ import java.util.Random; import dream.client.Var; -import dream.common.Consts; -import dream.locking.LockManagerLauncher; -import dream.server.ServerLauncher; +import dream.examples.util.Client; -public class InputModel { - private boolean serverStarted = false; - private boolean lockManagerStarted = false; +public class InputModel extends Client { + + public InputModel() { + super("InputModel"); + } public static void main(String[] args) { new InputModel().start(); } public void start() { - startServerIfNeeded(); - startLockManagerIfNeeded(); - - Consts.hostName = "InputModel"; - final Var marketIndex = new Var<>("marketIndex", 1); final Var stockOpts = new Var<>("stockOpts", 1); final Var news = new Var<>("news", 1); @@ -41,28 +36,4 @@ public void start() { } } } - - private final void startServerIfNeeded() { - if (!serverStarted) { - ServerLauncher.start(); - serverStarted = true; - } - try { - Thread.sleep(500); - } catch (final InterruptedException e) { - e.printStackTrace(); - } - } - - private final void startLockManagerIfNeeded() { - if (!lockManagerStarted) { - LockManagerLauncher.start(); - lockManagerStarted = true; - } - try { - Thread.sleep(500); - } catch (final InterruptedException e) { - e.printStackTrace(); - } - } } diff --git a/Dream2/src/examples/java/dream/examples/financial/Model1.java b/Dream2/src/examples/java/dream/examples/financial/Model1.java index be25ed1..3d7e9c9 100644 --- a/Dream2/src/examples/java/dream/examples/financial/Model1.java +++ b/Dream2/src/examples/java/dream/examples/financial/Model1.java @@ -1,15 +1,23 @@ package dream.examples.financial; -import dream.client.DreamClient; +import java.util.Arrays; +import java.util.List; + import dream.client.RemoteVar; import dream.client.Signal; -import dream.common.Consts; +import dream.examples.util.Client; -public class Model1 { - public void start() { - Consts.hostName = "Model1"; - DreamClient.instance.connect(); +public class Model1 extends Client { + public Model1() { + super("Model1"); + } + @Override + protected List waitForVars() { + return Arrays.asList("marketIndex@InputModel", "stockOpts@InputModel"); + } + + public void start() { final RemoteVar marketIndex = new RemoteVar<>("InputModel", "marketIndex"); final RemoteVar stockOpts = new RemoteVar<>("InputModel", "stockOpts"); @@ -19,7 +27,7 @@ public void start() { } else { return marketIndex.get() * 2 + stockOpts.get(); } - }, marketIndex, stockOpts); + } , marketIndex, stockOpts); f1.change().addHandler((oldVal, newVal) -> System.out.println("New value for f1: " + newVal)); } diff --git a/Dream2/src/examples/java/dream/examples/financial/Model2.java b/Dream2/src/examples/java/dream/examples/financial/Model2.java index 2802542..a63825b 100644 --- a/Dream2/src/examples/java/dream/examples/financial/Model2.java +++ b/Dream2/src/examples/java/dream/examples/financial/Model2.java @@ -1,15 +1,23 @@ package dream.examples.financial; -import dream.client.DreamClient; +import java.util.Arrays; +import java.util.List; + import dream.client.RemoteVar; import dream.client.Signal; -import dream.common.Consts; +import dream.examples.util.Client; -public class Model2 { - public void start() { - Consts.hostName = "Model2"; - DreamClient.instance.connect(); +public class Model2 extends Client { + public Model2() { + super("Model2"); + } + @Override + protected List waitForVars() { + return Arrays.asList("marketIndex@InputModel", "stockOpts@InputModel"); + } + + public void start() { final RemoteVar marketIndex = new RemoteVar<>("InputModel", "marketIndex"); final RemoteVar stockOpts = new RemoteVar<>("InputModel", "stockOpts"); @@ -19,7 +27,7 @@ public void start() { } else { return marketIndex.get() + stockOpts.get() * 2; } - }, marketIndex, stockOpts); + } , marketIndex, stockOpts); f2.change().addHandler((oldVal, newVal) -> System.out.println("New value for f2: " + newVal)); } diff --git a/Dream2/src/examples/java/dream/examples/financial/Model3.java b/Dream2/src/examples/java/dream/examples/financial/Model3.java index 311e482..f85bdce 100644 --- a/Dream2/src/examples/java/dream/examples/financial/Model3.java +++ b/Dream2/src/examples/java/dream/examples/financial/Model3.java @@ -1,15 +1,24 @@ package dream.examples.financial; -import dream.client.DreamClient; +import java.util.Arrays; +import java.util.List; + import dream.client.RemoteVar; import dream.client.Signal; -import dream.common.Consts; +import dream.examples.util.Client; -public class Model3 { - public void start() { - Consts.hostName = "Model3"; - DreamClient.instance.connect(); +public class Model3 extends Client { + + public Model3() { + super("Model3"); + } + @Override + protected List waitForVars() { + return Arrays.asList("marketIndex@InputModel", "news@InputModel"); + } + + public void start() { final RemoteVar marketIndex = new RemoteVar<>("InputModel", "marketIndex"); final RemoteVar news = new RemoteVar<>("InputModel", "news"); @@ -19,7 +28,7 @@ public void start() { } else { return marketIndex.get() + news.get(); } - }, marketIndex, news); + } , marketIndex, news); f3.change().addHandler((oldVal, newVal) -> System.out.println("New value for f3: " + newVal)); } From 8c1926a4cc08087a19d590320faa6cd443b023ff Mon Sep 17 00:00:00 2001 From: "Mia.Min.Yang" Date: Sat, 21 May 2016 00:10:50 +0200 Subject: [PATCH 088/161] Seperate GUIs and accept data from GUI. --- .../dream/examples/taskBoard/InitApp.java | 9 ++- .../{Monitor.java => NewTaskGUI.java} | 73 +++++-------------- .../dream/examples/taskBoard/ServerNode.java | 5 +- .../java/dream/examples/taskBoard/Tasks.java | 36 +++------ 4 files changed, 40 insertions(+), 83 deletions(-) rename Dream2/src/examples/java/dream/examples/taskBoard/{Monitor.java => NewTaskGUI.java} (53%) diff --git a/Dream2/src/examples/java/dream/examples/taskBoard/InitApp.java b/Dream2/src/examples/java/dream/examples/taskBoard/InitApp.java index 4f7f9e1..f2652a8 100644 --- a/Dream2/src/examples/java/dream/examples/taskBoard/InitApp.java +++ b/Dream2/src/examples/java/dream/examples/taskBoard/InitApp.java @@ -4,10 +4,11 @@ * * @author Min Yang * @date May 13, 2016 - * @description User can first run ServerHost.java - * (..examples.taskBoard.ServerHost.java), then run Tasks.java - * (..examples.taskBoard.Tasks.java), then run TaskReview.java - * (..examples.taskBoard.ServerHost.java) + * @description User can first run + * ServerNode.java(..examples.taskBoard.ServerNode.java), then run + * NewTaskGUI.java (..examples.taskBoard.NewTaskGUI.java), then run + * TaskReviewer.java (..examples.taskBoard.TaskReviewer.java) + * */ // TODO run the whole package together public class InitApp { diff --git a/Dream2/src/examples/java/dream/examples/taskBoard/Monitor.java b/Dream2/src/examples/java/dream/examples/taskBoard/NewTaskGUI.java similarity index 53% rename from Dream2/src/examples/java/dream/examples/taskBoard/Monitor.java rename to Dream2/src/examples/java/dream/examples/taskBoard/NewTaskGUI.java index b8b4427..3a0ef8a 100644 --- a/Dream2/src/examples/java/dream/examples/taskBoard/Monitor.java +++ b/Dream2/src/examples/java/dream/examples/taskBoard/NewTaskGUI.java @@ -8,10 +8,8 @@ import javax.swing.GroupLayout; import javax.swing.JButton; import javax.swing.JFrame; -import javax.swing.JLabel; -import javax.swing.JTextArea; +import javax.swing.JOptionPane; import javax.swing.JTextField; -import javax.swing.LayoutStyle; /** * @@ -19,21 +17,20 @@ * @date May 15, 2016 * @description */ -public class Monitor { +public class NewTaskGUI { public static JTextField textField1; - static boolean flag = false; static Logger log = Logger.getLogger("Monitor"); - static boolean taskValid() { - return flag; - } - public static void main(String[] args) { + javax.swing.SwingUtilities.invokeLater(new Runnable() { public void run() { - new Monitor().initComponents(); + NewTaskGUI user = new NewTaskGUI(); + user.initComponents(); + } }); + } void initComponents() { @@ -43,58 +40,30 @@ void initComponents() { frame1 = new JFrame(); textField1 = new JTextField(); button1 = new JButton(); - textArea2 = new JTextArea(); - label1 = new JLabel(); - label2 = new JLabel(); - textArea3 = new JTextArea(); + button1.setActionCommand("ADD"); // ======== frame1 ======== { Container frame1ContentPane = frame1.getContentPane(); // ---- button1 ---- - button1.setText("Add Task"); + button1.setText("New Task"); button1.addActionListener(new ButtonListener()); - button1.setActionCommand("ADD"); - - // ---- label1 ---- - label1.setText("Current devs:"); - - // ---- label2 ---- - label2.setText("Current tests:"); GroupLayout frame1ContentPaneLayout = new GroupLayout(frame1ContentPane); frame1ContentPane.setLayout(frame1ContentPaneLayout); - frame1ContentPaneLayout.setHorizontalGroup(frame1ContentPaneLayout.createParallelGroup() - .addGroup(frame1ContentPaneLayout.createSequentialGroup().addGroup(frame1ContentPaneLayout - .createParallelGroup() - .addGroup(frame1ContentPaneLayout.createSequentialGroup().addGap(20, 20, 20).addComponent( - textField1, GroupLayout.PREFERRED_SIZE, 300, GroupLayout.PREFERRED_SIZE)) - .addGroup(frame1ContentPaneLayout.createSequentialGroup().addContainerGap().addComponent( - textArea3, GroupLayout.PREFERRED_SIZE, 300, GroupLayout.PREFERRED_SIZE)) - .addGroup(frame1ContentPaneLayout.createSequentialGroup().addContainerGap() - .addComponent(label1))) - .addGap(18, 18, 18) - .addGroup(frame1ContentPaneLayout.createParallelGroup() - .addComponent(textArea2, GroupLayout.PREFERRED_SIZE, 300, + frame1ContentPaneLayout + .setHorizontalGroup(frame1ContentPaneLayout.createParallelGroup() + .addGroup(frame1ContentPaneLayout.createSequentialGroup().addGap(20, 20, 20) + .addComponent(textField1, GroupLayout.PREFERRED_SIZE, 590, GroupLayout.PREFERRED_SIZE) - .addComponent(button1).addComponent(label2)) - .addContainerGap(30, Short.MAX_VALUE))); + .addGap(18, 18, 18).addComponent(button1).addContainerGap(34, Short.MAX_VALUE))); frame1ContentPaneLayout.setVerticalGroup(frame1ContentPaneLayout.createParallelGroup() .addGroup(frame1ContentPaneLayout.createSequentialGroup().addGap(19, 19, 19) .addGroup(frame1ContentPaneLayout.createParallelGroup(GroupLayout.Alignment.BASELINE) .addComponent(button1).addComponent(textField1, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)) - .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED, 200, Short.MAX_VALUE) - .addGroup(frame1ContentPaneLayout.createParallelGroup(GroupLayout.Alignment.BASELINE) - .addComponent(label2).addComponent(label1)) - .addGap(13, 13, 13) - .addGroup(frame1ContentPaneLayout.createParallelGroup(GroupLayout.Alignment.BASELINE) - .addComponent(textArea2, GroupLayout.PREFERRED_SIZE, 300, - GroupLayout.PREFERRED_SIZE) - .addComponent(textArea3, GroupLayout.PREFERRED_SIZE, 300, - GroupLayout.PREFERRED_SIZE)) - .addContainerGap())); + .addContainerGap(24, Short.MAX_VALUE))); frame1.pack(); frame1.setLocationRelativeTo(frame1.getOwner()); frame1.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); @@ -109,26 +78,22 @@ void initComponents() { // Generated using JFormDesigner Evaluation license - Min Yang private JFrame frame1; private JButton button1; - private JTextArea textArea2; - private JLabel label1; - private JLabel label2; - private JTextArea textArea3; // JFormDesigner - End of variables declaration //GEN-END:variables static class ButtonListener implements ActionListener { - @Override public void actionPerformed(ActionEvent paramActionEvent) { if (paramActionEvent.getActionCommand() == "ADD") { String toTasks = textField1.getText(); if (toTasks.contains(":") && toTasks.split(":")[0].matches("D\\d*") && toTasks.split(":")[1].matches("T\\d*")) { - flag = true; + // TODO: think! to make it more user friendly + new Tasks(toTasks); } else { - // TODO task format invalid handle + textField1.setText(""); + JOptionPane.showMessageDialog(null, "Please input the right pattern of task."); log.info("Wrong input pattern of tasks"); } - flag = true; } } } diff --git a/Dream2/src/examples/java/dream/examples/taskBoard/ServerNode.java b/Dream2/src/examples/java/dream/examples/taskBoard/ServerNode.java index 2fbdd0a..5eed961 100644 --- a/Dream2/src/examples/java/dream/examples/taskBoard/ServerNode.java +++ b/Dream2/src/examples/java/dream/examples/taskBoard/ServerNode.java @@ -52,16 +52,19 @@ private void detectClients() { } catch (InterruptedException e) { e.printStackTrace(); } - log.info("inside ServerHost:" + vars.toString()); + log.info("inside ServerNode:" + vars.toString()); detectClients(); } private void createClient(String clientHost, String clientVar) { + log.info("inside"); RemoteVar rv = new RemoteVar(clientHost, clientVar); Signal sig = new Signal("received_" + clientHost, () -> { if (rv.get() != null) { + return rv.get(); } else { + return ""; } }, rv); diff --git a/Dream2/src/examples/java/dream/examples/taskBoard/Tasks.java b/Dream2/src/examples/java/dream/examples/taskBoard/Tasks.java index f5b26c3..60485be 100644 --- a/Dream2/src/examples/java/dream/examples/taskBoard/Tasks.java +++ b/Dream2/src/examples/java/dream/examples/taskBoard/Tasks.java @@ -1,5 +1,7 @@ package dream.examples.taskBoard; +import java.util.logging.Logger; + import javax.swing.JFrame; import dream.client.Var; @@ -12,37 +14,23 @@ * @description Creating tasks. */ public class Tasks extends JFrame { - + Logger log = Logger.getLogger("Tasks"); /** * Default UID */ private static final long serialVersionUID = 1L; - public static void main(String[] args) { - - new Tasks(); - // TODO accept inputs of new tasks from Monitor - /* - * javax.swing.SwingUtilities.invokeLater(new Runnable() { public void - * run() { Monitor user1 = new Monitor(); user1.initComponents(); if - * (Monitor.taskValid()) { new Tasks(); } } }); - */ - } - - public Tasks() { + public Tasks(String input) { Consts.hostName = "host"; - System.out.println("In Tasks"); - Var v = new Var("toServerVar", ""); - try { - int i = 0; - while (i < 10) { - Thread.sleep(1000); - v.set("D" + i + ":" + "T" + i); - i++; + Var v = new Var("toServerVar", input); + for (int i = 0; i < 10; i++) { + try { + Thread.sleep(500); + } catch (InterruptedException e) { + // TODO Auto-generated catch block + e.printStackTrace(); } - } catch (InterruptedException e) { - e.printStackTrace(); + v.set(input); } - } } From 7e4557fe4f9c7243ff3aab6aea9282664ad85427 Mon Sep 17 00:00:00 2001 From: "Mia.Min.Yang" Date: Sat, 21 May 2016 00:12:14 +0200 Subject: [PATCH 089/161] Add new monitor for manager. --- .../dream/examples/taskBoard/Monitor.java | 92 +++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 Dream2/src/examples/java/dream/examples/taskBoard/Monitor.java diff --git a/Dream2/src/examples/java/dream/examples/taskBoard/Monitor.java b/Dream2/src/examples/java/dream/examples/taskBoard/Monitor.java new file mode 100644 index 0000000..adf967e --- /dev/null +++ b/Dream2/src/examples/java/dream/examples/taskBoard/Monitor.java @@ -0,0 +1,92 @@ +/* + * Created by JFormDesigner on Fri May 20 23:52:44 CEST 2016 + */ + +package dream.examples.taskBoard; + +import java.awt.Container; + +import javax.swing.GroupLayout; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JTextArea; + +/** + * @author Min Yang + */ +public class Monitor extends JPanel { + public Monitor() { + initComponents(); + } + + public static void main(String[] args) { + + javax.swing.SwingUtilities.invokeLater(new Runnable() { + public void run() { + Monitor user = new Monitor(); + user.initComponents(); + } + }); + + } + + private void initComponents() { + // JFormDesigner - Component initialization - DO NOT MODIFY + // //GEN-BEGIN:initComponents + // Generated using JFormDesigner Evaluation license - Min Yang + frame1 = new JFrame(); + textArea1 = new JTextArea(); + label1 = new JLabel(); + label2 = new JLabel(); + textArea2 = new JTextArea(); + + // ======== frame1 ======== + { + Container frame1ContentPane = frame1.getContentPane(); + + // ---- label1 ---- + label1.setText("Current devs:"); + + // ---- label2 ---- + label2.setText("Current tests:"); + + GroupLayout frame1ContentPaneLayout = new GroupLayout(frame1ContentPane); + frame1ContentPane.setLayout(frame1ContentPaneLayout); + frame1ContentPaneLayout.setHorizontalGroup(frame1ContentPaneLayout.createParallelGroup() + .addGroup(frame1ContentPaneLayout.createSequentialGroup().addContainerGap() + .addGroup(frame1ContentPaneLayout.createParallelGroup().addComponent(label1).addComponent( + textArea2, GroupLayout.PREFERRED_SIZE, 570, GroupLayout.PREFERRED_SIZE)) + .addGap(30, 30, 30) + .addGroup(frame1ContentPaneLayout.createParallelGroup().addComponent(label2).addComponent( + textArea1, GroupLayout.PREFERRED_SIZE, 570, GroupLayout.PREFERRED_SIZE)) + .addContainerGap(30, Short.MAX_VALUE))); + frame1ContentPaneLayout.setVerticalGroup(frame1ContentPaneLayout.createParallelGroup() + .addGroup(frame1ContentPaneLayout.createSequentialGroup().addContainerGap() + .addGroup(frame1ContentPaneLayout.createParallelGroup(GroupLayout.Alignment.BASELINE) + .addComponent(label2).addComponent(label1)) + .addGap(13, 13, 13) + .addGroup(frame1ContentPaneLayout.createParallelGroup(GroupLayout.Alignment.LEADING, false) + .addComponent(textArea2, GroupLayout.DEFAULT_SIZE, 600, Short.MAX_VALUE) + .addComponent(textArea1, GroupLayout.DEFAULT_SIZE, 600, Short.MAX_VALUE)) + .addContainerGap(15, Short.MAX_VALUE))); + + frame1.pack(); + frame1.setLocationRelativeTo(frame1.getOwner()); + frame1.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + frame1.setVisible(true); + } + // JFormDesigner - End of component initialization + // //GEN-END:initComponents + } + + // JFormDesigner - Variables declaration - DO NOT MODIFY + // //GEN-BEGIN:variables + // Generated using JFormDesigner Evaluation license - Min Yang + private JFrame frame1; + private JTextArea textArea1; + private JLabel label1; + private JLabel label2; + private JTextArea textArea2; + // JFormDesigner - End of variables declaration //GEN-END:variables +} From 4557a9515c6fcc778eff1258855dbc9b47f2e4db Mon Sep 17 00:00:00 2001 From: "Mia.Min.Yang" Date: Sat, 21 May 2016 21:26:32 +0200 Subject: [PATCH 090/161] remove needless imperative style. --- .../dream/examples/taskBoard/InitApp.java | 24 ++++++ .../dream/examples/taskBoard/Monitor.java | 50 +++++++---- .../dream/examples/taskBoard/NewTaskGUI.java | 1 - .../dream/examples/taskBoard/ServerNode.java | 35 +++----- .../examples/taskBoard/TaskReviewer.java | 84 ++++++------------- .../java/dream/examples/taskBoard/Tasks.java | 5 +- 6 files changed, 97 insertions(+), 102 deletions(-) diff --git a/Dream2/src/examples/java/dream/examples/taskBoard/InitApp.java b/Dream2/src/examples/java/dream/examples/taskBoard/InitApp.java index f2652a8..097b9b5 100644 --- a/Dream2/src/examples/java/dream/examples/taskBoard/InitApp.java +++ b/Dream2/src/examples/java/dream/examples/taskBoard/InitApp.java @@ -12,4 +12,28 @@ */ // TODO run the whole package together public class InitApp { + // public static void main(String... args) { + // Process serverNode = null; + // Process gui = null; + // Process viewer = null; + // try { + // serverNode = new NewJvmHelper().startNewJVM(ServerNode.class); + // gui = new NewJvmHelper().startNewJVM(NewTaskGUI.class); + // viewer = new NewJvmHelper().startNewJVM(TaskReviewer.class); + // Thread.sleep(5000 * 5000); + // } catch (InterruptedException e) { + // // TODO Auto-generated catch block + // e.printStackTrace(); + // } finally { + // if (serverNode != null) { + // serverNode.destroyForcibly(); + // } + // if (viewer != null) { + // viewer.destroyForcibly(); + // } + // if (gui != null) { + // gui.destroyForcibly(); + // } + // } + // } } \ No newline at end of file diff --git a/Dream2/src/examples/java/dream/examples/taskBoard/Monitor.java b/Dream2/src/examples/java/dream/examples/taskBoard/Monitor.java index adf967e..70c7e8a 100644 --- a/Dream2/src/examples/java/dream/examples/taskBoard/Monitor.java +++ b/Dream2/src/examples/java/dream/examples/taskBoard/Monitor.java @@ -5,12 +5,17 @@ package dream.examples.taskBoard; import java.awt.Container; +import java.awt.event.ActionEvent; +import java.util.logging.Logger; import javax.swing.GroupLayout; +import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JLabel; +import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JTextArea; +import javax.swing.LayoutStyle; /** * @author Min Yang @@ -31,62 +36,79 @@ public void run() { } + private void button1ActionPerformed(ActionEvent e) { + if (e.getActionCommand() == "SHOW") { + TaskReviewer view = new TaskReviewer(); + } else { + JOptionPane.showMessageDialog(null, "Ops, currently no task, please try again later."); + log.info("Wrong input pattern of tasks"); + } + } + private void initComponents() { // JFormDesigner - Component initialization - DO NOT MODIFY // //GEN-BEGIN:initComponents - // Generated using JFormDesigner Evaluation license - Min Yang + // Generated using JFormDesigner Evaluation license - Ashleeeeee Yang frame1 = new JFrame(); textArea1 = new JTextArea(); label1 = new JLabel(); label2 = new JLabel(); textArea2 = new JTextArea(); + button1 = new JButton(); // ======== frame1 ======== { Container frame1ContentPane = frame1.getContentPane(); - // ---- label1 ---- label1.setText("Current devs:"); // ---- label2 ---- label2.setText("Current tests:"); + // ---- button1 ---- + button1.setText("Show me tasks"); + button1.addActionListener(e -> button1ActionPerformed(e)); + button1.setActionCommand("SHOW"); + GroupLayout frame1ContentPaneLayout = new GroupLayout(frame1ContentPane); frame1ContentPane.setLayout(frame1ContentPaneLayout); frame1ContentPaneLayout.setHorizontalGroup(frame1ContentPaneLayout.createParallelGroup() .addGroup(frame1ContentPaneLayout.createSequentialGroup().addContainerGap() .addGroup(frame1ContentPaneLayout.createParallelGroup().addComponent(label1).addComponent( - textArea2, GroupLayout.PREFERRED_SIZE, 570, GroupLayout.PREFERRED_SIZE)) + textArea2, GroupLayout.PREFERRED_SIZE, 57, GroupLayout.PREFERRED_SIZE)) .addGap(30, 30, 30) .addGroup(frame1ContentPaneLayout.createParallelGroup().addComponent(label2).addComponent( - textArea1, GroupLayout.PREFERRED_SIZE, 570, GroupLayout.PREFERRED_SIZE)) - .addContainerGap(30, Short.MAX_VALUE))); + textArea1, GroupLayout.PREFERRED_SIZE, 57, GroupLayout.PREFERRED_SIZE)) + .addContainerGap(9, Short.MAX_VALUE)) + .addGroup(GroupLayout.Alignment.TRAILING, frame1ContentPaneLayout.createSequentialGroup() + .addContainerGap(11, Short.MAX_VALUE).addComponent(button1).addGap(71, 71, 71))); frame1ContentPaneLayout.setVerticalGroup(frame1ContentPaneLayout.createParallelGroup() - .addGroup(frame1ContentPaneLayout.createSequentialGroup().addContainerGap() + .addGroup(frame1ContentPaneLayout.createSequentialGroup().addComponent(button1) + .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED) .addGroup(frame1ContentPaneLayout.createParallelGroup(GroupLayout.Alignment.BASELINE) .addComponent(label2).addComponent(label1)) .addGap(13, 13, 13) .addGroup(frame1ContentPaneLayout.createParallelGroup(GroupLayout.Alignment.LEADING, false) - .addComponent(textArea2, GroupLayout.DEFAULT_SIZE, 600, Short.MAX_VALUE) - .addComponent(textArea1, GroupLayout.DEFAULT_SIZE, 600, Short.MAX_VALUE)) - .addContainerGap(15, Short.MAX_VALUE))); - + .addComponent(textArea2, GroupLayout.DEFAULT_SIZE, 108, Short.MAX_VALUE) + .addComponent(textArea1, GroupLayout.DEFAULT_SIZE, 108, Short.MAX_VALUE)) + .addContainerGap(GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))); frame1.pack(); frame1.setLocationRelativeTo(frame1.getOwner()); frame1.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame1.setVisible(true); } - // JFormDesigner - End of component initialization // //GEN-END:initComponents } // JFormDesigner - Variables declaration - DO NOT MODIFY // //GEN-BEGIN:variables - // Generated using JFormDesigner Evaluation license - Min Yang + // Generated using JFormDesigner Evaluation license - Ashleeeeee Yang private JFrame frame1; - private JTextArea textArea1; + private static JTextArea textArea1; private JLabel label1; private JLabel label2; - private JTextArea textArea2; + private static JTextArea textArea2; + private JButton button1; + private Logger log = Logger.getLogger("Monitor"); // JFormDesigner - End of variables declaration //GEN-END:variables } diff --git a/Dream2/src/examples/java/dream/examples/taskBoard/NewTaskGUI.java b/Dream2/src/examples/java/dream/examples/taskBoard/NewTaskGUI.java index 3a0ef8a..faf8513 100644 --- a/Dream2/src/examples/java/dream/examples/taskBoard/NewTaskGUI.java +++ b/Dream2/src/examples/java/dream/examples/taskBoard/NewTaskGUI.java @@ -27,7 +27,6 @@ public static void main(String[] args) { public void run() { NewTaskGUI user = new NewTaskGUI(); user.initComponents(); - } }); diff --git a/Dream2/src/examples/java/dream/examples/taskBoard/ServerNode.java b/Dream2/src/examples/java/dream/examples/taskBoard/ServerNode.java index 5eed961..aa2db7c 100644 --- a/Dream2/src/examples/java/dream/examples/taskBoard/ServerNode.java +++ b/Dream2/src/examples/java/dream/examples/taskBoard/ServerNode.java @@ -21,10 +21,10 @@ * and test etc. */ public class ServerNode { - public static final String NAME = "ServerHost"; + public static final String NAME = "ServerNode"; private boolean serverStarted = false; private Var> myClients = null; - private final Logger log = Logger.getLogger("Server"); + private final Logger log = Logger.getLogger("ServerNode"); private Var devTask = null; private Var testTask = null; @@ -45,58 +45,45 @@ private void detectClients() { Set vars = DreamClient.instance.listVariables(); vars.stream().map(x -> new Pair(x.split("@")[1], x.split("@")[0])) .filter(x -> !myClients.get().contains(x.getSecond() + "@" + x.getFirst()) - && x.getSecond().equalsIgnoreCase("toServerVar")) + && x.getSecond().equalsIgnoreCase("FromTaskNode")) .forEach(x -> createClient(x.getFirst(), x.getSecond())); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } - log.info("inside ServerNode:" + vars.toString()); detectClients(); } private void createClient(String clientHost, String clientVar) { - log.info("inside"); RemoteVar rv = new RemoteVar(clientHost, clientVar); - Signal sig = new Signal("received_" + clientHost, () -> { + Signal sig = new Signal(clientHost, () -> { if (rv.get() != null) { - return rv.get(); } else { return ""; } }, rv); - // sig.change().addHandler((oldValue, newValue) -> - // createTaskLists(newValue)); + sig.change().addHandler((oldValue, newValue) -> { if (newValue != null) { String newDev = newValue.split(":")[0]; String newTest = newValue.split(":")[1]; - if (devTask == null) { + if (devTask == null) devTask = new Var("taskDevs", ""); - } - if (testTask == null) { + + if (testTask == null) testTask = new Var("taskTests", ""); - } + // Set vars for remote querying - devTask.set(devTask.get().toString() + newDev); - testTask.set(testTask.get().toString() + newTest); + devTask.set(devTask.get().toString() + newDev + ":"); + testTask.set(testTask.get().toString() + newTest + ":"); } }); myClients.modify((old) -> old.add(clientVar + "@" + clientHost)); } - /* - * private void createTaskLists(String newValue) { if (newValue != null) { - * String newDev = newValue.split(":")[0]; String newTest = - * newValue.split(":")[1]; if (devTask == null) { devTask = new - * Var("taskDevs", ""); } if (testTask == null) { testTask = new - * Var("taskTests", ""); } // Set vars for remote querying - * devTask.set(devTask.get().toString() + newDev); - * testTask.set(testTask.get().toString() + newTest); } } - */ private final void startServerIfNeeded() { if (!serverStarted) { ServerLauncher.start(); diff --git a/Dream2/src/examples/java/dream/examples/taskBoard/TaskReviewer.java b/Dream2/src/examples/java/dream/examples/taskBoard/TaskReviewer.java index c9127d1..14484fa 100644 --- a/Dream2/src/examples/java/dream/examples/taskBoard/TaskReviewer.java +++ b/Dream2/src/examples/java/dream/examples/taskBoard/TaskReviewer.java @@ -1,16 +1,13 @@ package dream.examples.taskBoard; import java.util.ArrayList; -import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; -import dream.client.DreamClient; import dream.client.RemoteVar; import dream.client.Signal; import dream.client.Var; import dream.common.Consts; -import dream.examples.util.Pair; /** * @@ -21,72 +18,39 @@ public class TaskReviewer { private final Logger log = Logger.getLogger("MagtClient"); private Var> myServer = null; - RemoteVar devTasks = null; - RemoteVar testTasks = null; + RemoteVar devs = null; + RemoteVar tests = null; + Signal sigDevs = null; + Signal sigTests = null; + + public static void main(String[] args) { + new TaskReviewer(); + } public TaskReviewer() { Consts.hostName = "QueryClient"; log.setLevel(Level.ALL); log.addHandler(Logger.getGlobal().getHandlers()[0]); - myServer = new Var>("from_server", new ArrayList()); - detectQueryResults(); - } - - private void detectQueryResults() { - log.info("In TaskReview"); - Set vars = DreamClient.instance.listVariables(); - vars.stream().map(x -> new Pair(x.split("@")[1], x.split("@")[0])) - .filter(x -> !myServer.get().contains(x.getSecond() + "@" + x.getFirst()) - && x.getSecond().equalsIgnoreCase("taskDevs")) - .forEach(x -> updateDevQuery(x.getFirst(), x.getSecond())); - - vars.stream().map(x -> new Pair(x.split("@")[1], x.split("@")[0])) - .filter(x -> !myServer.get().contains(x.getSecond() + "@" + x.getFirst()) - && x.getSecond().equalsIgnoreCase("taskTests")) - .forEach(x -> updateTestQuery(x.getFirst(), x.getSecond())); - - try { - Thread.sleep(500); - } catch (InterruptedException e) { - e.printStackTrace(); + if (devs == null) { + devs = new RemoteVar("ServerNode", "taskDevs"); + sigDevs = new Signal("sigDevs", () -> { + return devs.get(); + }, devs); } - detectQueryResults(); - } + if (tests == null) { - private void updateDevQuery(String host, String value) { - RemoteVar rv = new RemoteVar(host, value); - Signal sig = new Signal("received_" + host, () -> { - if (rv.get() != null) { - return rv.get(); - } else { - return ""; - } - }, rv); - sig.change().addHandler((oldValue, newValue) -> { - // TODO show current dev tasks on monitor - log.info("Current dev tasks are:" + newValue); + tests = new RemoteVar("ServerNode", "taskTests"); + sigTests = new Signal("sigTests", () -> { + return tests.get(); + }, tests); + } + // TODO show in monitor + sigDevs.change().addHandler((oldVa, newVal) -> { + System.out.println("newVal devs:" + newVal); }); - myServer.modify((old) -> old.add(value + "@" + host)); - } - private void updateTestQuery(String host, String value) { - RemoteVar rv = new RemoteVar(host, value); - Signal sig = new Signal("received_" + host, () -> { - if (rv.get() != null) { - return rv.get(); - } else { - return ""; - } - }, rv); - sig.change().addHandler((oldValue, newValue) -> { - // TODO show current dev tasks on monitor - log.info("Current test tasks are: " + newValue); + sigTests.change().addHandler((oldVa, newVal) -> { + System.out.println("newVal tests:" + newVal); }); - myServer.modify((old) -> old.add(value + "@" + host)); - } - - public static void main(String[] args) { - new TaskReviewer(); } - } diff --git a/Dream2/src/examples/java/dream/examples/taskBoard/Tasks.java b/Dream2/src/examples/java/dream/examples/taskBoard/Tasks.java index 60485be..f483b67 100644 --- a/Dream2/src/examples/java/dream/examples/taskBoard/Tasks.java +++ b/Dream2/src/examples/java/dream/examples/taskBoard/Tasks.java @@ -21,13 +21,12 @@ public class Tasks extends JFrame { private static final long serialVersionUID = 1L; public Tasks(String input) { - Consts.hostName = "host"; - Var v = new Var("toServerVar", input); + Consts.hostName = "TaskNode"; + Var v = new Var("FromTaskNode", input); for (int i = 0; i < 10; i++) { try { Thread.sleep(500); } catch (InterruptedException e) { - // TODO Auto-generated catch block e.printStackTrace(); } v.set(input); From 96178aca36501c68e201c3b7002b6d1aac5b9417 Mon Sep 17 00:00:00 2001 From: "Mia.Min.Yang" Date: Sat, 21 May 2016 22:03:39 +0200 Subject: [PATCH 091/161] Support multiple JVM. --- .../examples/java/dream/examples/taskBoard/NewTaskGUI.java | 4 +++- .../examples/java/dream/examples/taskBoard/TaskReviewer.java | 5 +---- Dream2/src/examples/java/dream/examples/taskBoard/Tasks.java | 5 ++++- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/Dream2/src/examples/java/dream/examples/taskBoard/NewTaskGUI.java b/Dream2/src/examples/java/dream/examples/taskBoard/NewTaskGUI.java index faf8513..5308385 100644 --- a/Dream2/src/examples/java/dream/examples/taskBoard/NewTaskGUI.java +++ b/Dream2/src/examples/java/dream/examples/taskBoard/NewTaskGUI.java @@ -19,7 +19,7 @@ */ public class NewTaskGUI { public static JTextField textField1; - static Logger log = Logger.getLogger("Monitor"); + static Logger log = Logger.getLogger("NewTaskGUI"); public static void main(String[] args) { @@ -57,12 +57,14 @@ void initComponents() { .addComponent(textField1, GroupLayout.PREFERRED_SIZE, 590, GroupLayout.PREFERRED_SIZE) .addGap(18, 18, 18).addComponent(button1).addContainerGap(34, Short.MAX_VALUE))); + frame1ContentPaneLayout.setVerticalGroup(frame1ContentPaneLayout.createParallelGroup() .addGroup(frame1ContentPaneLayout.createSequentialGroup().addGap(19, 19, 19) .addGroup(frame1ContentPaneLayout.createParallelGroup(GroupLayout.Alignment.BASELINE) .addComponent(button1).addComponent(textField1, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)) .addContainerGap(24, Short.MAX_VALUE))); + frame1.pack(); frame1.setLocationRelativeTo(frame1.getOwner()); frame1.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); diff --git a/Dream2/src/examples/java/dream/examples/taskBoard/TaskReviewer.java b/Dream2/src/examples/java/dream/examples/taskBoard/TaskReviewer.java index 14484fa..bd4b84b 100644 --- a/Dream2/src/examples/java/dream/examples/taskBoard/TaskReviewer.java +++ b/Dream2/src/examples/java/dream/examples/taskBoard/TaskReviewer.java @@ -1,12 +1,10 @@ package dream.examples.taskBoard; -import java.util.ArrayList; import java.util.logging.Level; import java.util.logging.Logger; import dream.client.RemoteVar; import dream.client.Signal; -import dream.client.Var; import dream.common.Consts; /** @@ -16,8 +14,7 @@ * @description Review the tasks. */ public class TaskReviewer { - private final Logger log = Logger.getLogger("MagtClient"); - private Var> myServer = null; + private final Logger log = Logger.getLogger("ViewNode"); RemoteVar devs = null; RemoteVar tests = null; Signal sigDevs = null; diff --git a/Dream2/src/examples/java/dream/examples/taskBoard/Tasks.java b/Dream2/src/examples/java/dream/examples/taskBoard/Tasks.java index f483b67..2365968 100644 --- a/Dream2/src/examples/java/dream/examples/taskBoard/Tasks.java +++ b/Dream2/src/examples/java/dream/examples/taskBoard/Tasks.java @@ -1,5 +1,6 @@ package dream.examples.taskBoard; +import java.util.Random; import java.util.logging.Logger; import javax.swing.JFrame; @@ -21,7 +22,9 @@ public class Tasks extends JFrame { private static final long serialVersionUID = 1L; public Tasks(String input) { - Consts.hostName = "TaskNode"; + Random rm = new Random(); + Consts.hostName = "TaskNode" + rm.nextInt(100); + System.out.println("HOST name:" + Consts.hostName); Var v = new Var("FromTaskNode", input); for (int i = 0; i < 10; i++) { try { From e3f88f23f3818c928e6c10298131488eb00da85d Mon Sep 17 00:00:00 2001 From: Tobias Becker Date: Mon, 23 May 2016 17:54:17 +0200 Subject: [PATCH 092/161] fixed chat starter --- Dream2/src/examples/java/dream/examples/chat/Starter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dream2/src/examples/java/dream/examples/chat/Starter.java b/Dream2/src/examples/java/dream/examples/chat/Starter.java index a99b327..9f7e82d 100644 --- a/Dream2/src/examples/java/dream/examples/chat/Starter.java +++ b/Dream2/src/examples/java/dream/examples/chat/Starter.java @@ -36,7 +36,7 @@ private void start() { int x = 0; int y = 0; for (int i = 0; i < CHAT_COUNT; i++) { - NewJvmHelper.startNewJVM(Chat.class, getName(i), Integer.toString(x), Integer.toString(y)); + processes.add(NewJvmHelper.startNewJVM(Chat.class, getName(i), Integer.toString(x), Integer.toString(y))); x += xStep; if (x >= 3 * xStep) { x = 0; From 3d9937e09c7e626edb3112c1a1ede3ae390edd2e Mon Sep 17 00:00:00 2001 From: Tobias Becker Date: Mon, 23 May 2016 17:58:16 +0200 Subject: [PATCH 093/161] starting tasks in own jvm --- .../dream/examples/taskBoard/Monitor.java | 6 +-- .../dream/examples/taskBoard/NewTaskGUI.java | 47 ++++++++++++++++++- .../dream/examples/taskBoard/ServerNode.java | 26 ++-------- .../examples/taskBoard/TaskReviewer.java | 18 +++---- .../java/dream/examples/taskBoard/Tasks.java | 22 +++++---- 5 files changed, 72 insertions(+), 47 deletions(-) diff --git a/Dream2/src/examples/java/dream/examples/taskBoard/Monitor.java b/Dream2/src/examples/java/dream/examples/taskBoard/Monitor.java index 70c7e8a..72bc5db 100644 --- a/Dream2/src/examples/java/dream/examples/taskBoard/Monitor.java +++ b/Dream2/src/examples/java/dream/examples/taskBoard/Monitor.java @@ -13,17 +13,13 @@ import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JOptionPane; -import javax.swing.JPanel; import javax.swing.JTextArea; import javax.swing.LayoutStyle; /** * @author Min Yang */ -public class Monitor extends JPanel { - public Monitor() { - initComponents(); - } +public class Monitor { public static void main(String[] args) { diff --git a/Dream2/src/examples/java/dream/examples/taskBoard/NewTaskGUI.java b/Dream2/src/examples/java/dream/examples/taskBoard/NewTaskGUI.java index 5308385..aac6b8b 100644 --- a/Dream2/src/examples/java/dream/examples/taskBoard/NewTaskGUI.java +++ b/Dream2/src/examples/java/dream/examples/taskBoard/NewTaskGUI.java @@ -3,6 +3,9 @@ import java.awt.Container; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import java.awt.event.WindowEvent; +import java.awt.event.WindowListener; +import java.util.ArrayList; import java.util.logging.Logger; import javax.swing.GroupLayout; @@ -11,6 +14,8 @@ import javax.swing.JOptionPane; import javax.swing.JTextField; +import dream.examples.util.NewJvmHelper; + /** * * @author Min Yang @@ -20,6 +25,7 @@ public class NewTaskGUI { public static JTextField textField1; static Logger log = Logger.getLogger("NewTaskGUI"); + private static ArrayList tasks = new ArrayList(); public static void main(String[] args) { @@ -37,6 +43,37 @@ void initComponents() { // //GEN-BEGIN:initComponents // Generated using JFormDesigner Evaluation license - Min Yang frame1 = new JFrame(); + frame1.addWindowListener(new WindowListener() { + @Override + public void windowOpened(WindowEvent e) { + } + + @Override + public void windowIconified(WindowEvent e) { + } + + @Override + public void windowDeiconified(WindowEvent e) { + } + + @Override + public void windowDeactivated(WindowEvent e) { + } + + @Override + public void windowClosing(WindowEvent e) { + onExit(); + } + + @Override + public void windowClosed(WindowEvent e) { + onExit(); + } + + @Override + public void windowActivated(WindowEvent e) { + } + }); textField1 = new JTextField(); button1 = new JButton(); button1.setActionCommand("ADD"); @@ -89,7 +126,7 @@ public void actionPerformed(ActionEvent paramActionEvent) { if (toTasks.contains(":") && toTasks.split(":")[0].matches("D\\d*") && toTasks.split(":")[1].matches("T\\d*")) { // TODO: think! to make it more user friendly - new Tasks(toTasks); + tasks.add(NewJvmHelper.startNewJVM(Tasks.class, toTasks)); } else { textField1.setText(""); JOptionPane.showMessageDialog(null, "Please input the right pattern of task."); @@ -98,4 +135,12 @@ public void actionPerformed(ActionEvent paramActionEvent) { } } } + + private void onExit() { + System.out.println("exit"); + for (Process p : tasks) { + p.destroyForcibly(); + System.out.println("Destroying " + p.toString()); + } + } } diff --git a/Dream2/src/examples/java/dream/examples/taskBoard/ServerNode.java b/Dream2/src/examples/java/dream/examples/taskBoard/ServerNode.java index aa2db7c..28daf39 100644 --- a/Dream2/src/examples/java/dream/examples/taskBoard/ServerNode.java +++ b/Dream2/src/examples/java/dream/examples/taskBoard/ServerNode.java @@ -2,16 +2,13 @@ import java.util.ArrayList; import java.util.Set; -import java.util.logging.Level; -import java.util.logging.Logger; import dream.client.DreamClient; import dream.client.RemoteVar; import dream.client.Signal; import dream.client.Var; -import dream.common.Consts; +import dream.examples.util.Client; import dream.examples.util.Pair; -import dream.server.ServerLauncher; /** * @@ -20,11 +17,9 @@ * @description run background tasks: read task, create task lists: development * and test etc. */ -public class ServerNode { +public class ServerNode extends Client { public static final String NAME = "ServerNode"; - private boolean serverStarted = false; private Var> myClients = null; - private final Logger log = Logger.getLogger("ServerNode"); private Var devTask = null; private Var testTask = null; @@ -33,10 +28,7 @@ public static void main(String... args) { } public ServerNode() { - startServerIfNeeded(); - log.setLevel(Level.ALL); - log.addHandler(Logger.getGlobal().getHandlers()[0]); - Consts.hostName = NAME; + super(NAME); myClients = new Var>("Server_registered_clients", new ArrayList()); detectClients(); } @@ -83,16 +75,4 @@ private void createClient(String clientHost, String clientVar) { }); myClients.modify((old) -> old.add(clientVar + "@" + clientHost)); } - - private final void startServerIfNeeded() { - if (!serverStarted) { - ServerLauncher.start(); - serverStarted = true; - } - try { - Thread.sleep(500); - } catch (InterruptedException e) { - log.log(Level.SEVERE, "Failed to wait for Server starting", e); - } - } } \ No newline at end of file diff --git a/Dream2/src/examples/java/dream/examples/taskBoard/TaskReviewer.java b/Dream2/src/examples/java/dream/examples/taskBoard/TaskReviewer.java index bd4b84b..f483cf0 100644 --- a/Dream2/src/examples/java/dream/examples/taskBoard/TaskReviewer.java +++ b/Dream2/src/examples/java/dream/examples/taskBoard/TaskReviewer.java @@ -1,11 +1,11 @@ package dream.examples.taskBoard; -import java.util.logging.Level; -import java.util.logging.Logger; +import java.util.Arrays; +import java.util.List; import dream.client.RemoteVar; import dream.client.Signal; -import dream.common.Consts; +import dream.examples.util.Client; /** * @@ -13,8 +13,7 @@ * @date May 13, 2016 * @description Review the tasks. */ -public class TaskReviewer { - private final Logger log = Logger.getLogger("ViewNode"); +public class TaskReviewer extends Client { RemoteVar devs = null; RemoteVar tests = null; Signal sigDevs = null; @@ -24,10 +23,13 @@ public static void main(String[] args) { new TaskReviewer(); } + @Override + protected List waitForVars() { + return Arrays.asList("taskDevs@ServerNode", "taskTests@ServerNode"); + } + public TaskReviewer() { - Consts.hostName = "QueryClient"; - log.setLevel(Level.ALL); - log.addHandler(Logger.getGlobal().getHandlers()[0]); + super("QueryClient"); if (devs == null) { devs = new RemoteVar("ServerNode", "taskDevs"); sigDevs = new Signal("sigDevs", () -> { diff --git a/Dream2/src/examples/java/dream/examples/taskBoard/Tasks.java b/Dream2/src/examples/java/dream/examples/taskBoard/Tasks.java index 2365968..4ba430e 100644 --- a/Dream2/src/examples/java/dream/examples/taskBoard/Tasks.java +++ b/Dream2/src/examples/java/dream/examples/taskBoard/Tasks.java @@ -1,12 +1,12 @@ package dream.examples.taskBoard; +import java.awt.EventQueue; import java.util.Random; import java.util.logging.Logger; -import javax.swing.JFrame; - import dream.client.Var; import dream.common.Consts; +import dream.examples.util.Client; /** * @@ -14,16 +14,10 @@ * @date May 13, 2016 * @description Creating tasks. */ -public class Tasks extends JFrame { - Logger log = Logger.getLogger("Tasks"); - /** - * Default UID - */ - private static final long serialVersionUID = 1L; +public class Tasks extends Client { public Tasks(String input) { - Random rm = new Random(); - Consts.hostName = "TaskNode" + rm.nextInt(100); + super("TaskNode" + new Random().nextInt(100)); System.out.println("HOST name:" + Consts.hostName); Var v = new Var("FromTaskNode", input); for (int i = 0; i < 10; i++) { @@ -35,4 +29,12 @@ public Tasks(String input) { v.set(input); } } + + public static void main(String[] args) { + if (args.length < 1) { + Logger.getGlobal().severe("developer/tasks missing"); + return; + } + EventQueue.invokeLater(() -> new Tasks(args[0])); + } } From 1d59be520d277a3069edc1f0e93f8e4e8cb2d6e2 Mon Sep 17 00:00:00 2001 From: Tobias Becker Date: Thu, 26 May 2016 14:01:45 +0200 Subject: [PATCH 094/161] extended form example --- .../java/dream/examples/form/Boss.java | 25 ++++- .../java/dream/examples/form/FormClient.java | 13 ++- .../java/dream/examples/form/FormGUI.java | 102 ++++++++++++------ .../java/dream/examples/form/FormServer.java | 12 ++- .../java/dream/examples/form/Secretary.java | 3 +- .../java/dream/examples/form/graph.png | Bin 67919 -> 72093 bytes 6 files changed, 107 insertions(+), 48 deletions(-) diff --git a/Dream2/src/examples/java/dream/examples/form/Boss.java b/Dream2/src/examples/java/dream/examples/form/Boss.java index 1d9f33f..84d9f08 100644 --- a/Dream2/src/examples/java/dream/examples/form/Boss.java +++ b/Dream2/src/examples/java/dream/examples/form/Boss.java @@ -5,21 +5,36 @@ public class Boss extends FormClient { private Var eph; + private Var rmh; public Boss() { - super("Boss", "Euro/Hour"); + super("Boss", "Euro/Hour", "Minimum Hours"); + setInitValues(Double.toString(8.5), Integer.toString(10)); } @Override protected void init() { eph = new Var<>("euro_per_hour", 8.5); + rmh = new Var<>("required_minimum_hours", 10); } @Override - public void typedText(String typedText) { - Double value = Double.valueOf(typedText); - eph.set(value); - logger.fine("Set Euro_Per_Hour to " + value); + public void typedText(int i, String typedText) { + switch (i) { + case 0: + Double value = Double.valueOf(typedText); + eph.set(value); + logger.fine("Set Euro_Per_Hour to " + value); + break; + case 1: + Integer value2 = Integer.valueOf(typedText); + rmh.set(value2); + logger.fine("Set Required_Minimum_Hours to " + value2); + break; + default: + break; + } + } public static void main(String[] args) { diff --git a/Dream2/src/examples/java/dream/examples/form/FormClient.java b/Dream2/src/examples/java/dream/examples/form/FormClient.java index 9ece458..84ab01d 100644 --- a/Dream2/src/examples/java/dream/examples/form/FormClient.java +++ b/Dream2/src/examples/java/dream/examples/form/FormClient.java @@ -16,9 +16,10 @@ public abstract class FormClient extends Client { private Signal remoteSettings; private FormGUI gui; - private String labelText; + private String[] labelText; + private String[] values; - public FormClient(String name, String labelText) { + public FormClient(String name, String... labelText) { super(name); this.labelText = labelText; } @@ -31,6 +32,8 @@ protected List waitForVars() { protected void start() { gui = new FormGUI(getHostName(), labelText); gui.setListener(this); + if (values != null) + gui.setInitValues(values); salary = new RemoteVar<>("FormServer", "salary"); settings = new RemoteVar<>("FormServer", "settingsOkay"); @@ -55,5 +58,9 @@ protected void start() { remoteSettings.change().addHandler((o, n) -> gui.setColor((n ? Color.green : Color.red))); } - public abstract void typedText(String typedText); + public abstract void typedText(int i, String typedText); + + public void setInitValues(String... values) { + this.values = values; + } } diff --git a/Dream2/src/examples/java/dream/examples/form/FormGUI.java b/Dream2/src/examples/java/dream/examples/form/FormGUI.java index 3167635..e5bb511 100644 --- a/Dream2/src/examples/java/dream/examples/form/FormGUI.java +++ b/Dream2/src/examples/java/dream/examples/form/FormGUI.java @@ -3,6 +3,9 @@ import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; +import java.awt.GridLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; @@ -15,52 +18,47 @@ public class FormGUI extends JFrame { private static final long serialVersionUID = 9205614575242281482L; - private JTextField sendText; + private JTextField[] sendText; private FormClient listener; private JLabel display; + private String[] labelText; - public FormGUI(String name, String labelText) { - initUI(labelText); + public FormGUI(String name, String... labelText) { setTitle(name); + this.labelText = labelText; + initUI(); } - private void initUI(String labelText) { - sendText = new JTextField(20); - sendText.addKeyListener(new KeyListener() { + private void initUI() { + getContentPane().setLayout(new BorderLayout()); + JPanel all = new JPanel(); + all.setLayout(new GridLayout(0, 2)); + getContentPane().add(all, BorderLayout.CENTER); - @Override - public void keyTyped(KeyEvent e) { - } + sendText = new JTextField[labelText.length]; - @Override - public void keyReleased(KeyEvent e) { - } + for (int i = 0; i < labelText.length; i++) { + sendText[i] = new JTextField(20); + sendText[i].addKeyListener(new MultiListener(i)); + JButton sendButton = new JButton("Set"); + sendButton.addActionListener(new MultiListener(i)); - @Override - public void keyPressed(KeyEvent e) { - if (e.getKeyCode() == KeyEvent.VK_ENTER) - sendText(); - } - }); - JButton sendButton = new JButton("Set"); - sendButton.addActionListener((e) -> sendText()); + JLabel label = new JLabel(labelText[i] + ": "); + + JPanel p = new JPanel(); + p.add(label); + p.add(sendText[i]); + + all.add(p); + all.add(sendButton); + } display = new JLabel(""); display.setPreferredSize(new Dimension(100, 30)); display.setMinimumSize(new Dimension(100, 30)); display.setOpaque(true); - - JLabel label = new JLabel(labelText + ": "); - - JPanel p = new JPanel(); - p.add(label); - p.add(sendText); - - BorderLayout lay = new BorderLayout(); - getContentPane().setLayout(lay); - getContentPane().add(p, BorderLayout.WEST); - getContentPane().add(sendButton, BorderLayout.EAST); getContentPane().add(display, BorderLayout.NORTH); + // setSize(300, 200); setLocationRelativeTo(null); setDefaultCloseOperation(EXIT_ON_CLOSE); @@ -69,16 +67,24 @@ public void keyPressed(KeyEvent e) { setVisible(true); } - private void sendText() { - listener.typedText(getTypedText()); + public void setInitValues(String... values) { + for (int i = 0; i < values.length; i++) { + if (sendText[i].getText() == null || sendText[i].getText().isEmpty()) { + sendText[i].setText(values[i]); + } + } + } + + private void sendText(int i) { + listener.typedText(i, getTypedText(i)); } public void setListener(FormClient c) { listener = c; } - public String getTypedText() { - return sendText.getText(); + public String getTypedText(int i) { + return sendText[i].getText(); } public void setText(String text) { @@ -89,4 +95,30 @@ public void setColor(Color bg) { display.setBackground(bg); } + class MultiListener implements KeyListener, ActionListener { + private int i; + + public MultiListener(int i) { + this.i = i; + } + + @Override + public void keyTyped(KeyEvent e) { + } + + @Override + public void keyReleased(KeyEvent e) { + } + + @Override + public void keyPressed(KeyEvent e) { + if (e.getKeyCode() == KeyEvent.VK_ENTER) + sendText(i); + } + + @Override + public void actionPerformed(ActionEvent e) { + sendText(i); + } + } } diff --git a/Dream2/src/examples/java/dream/examples/form/FormServer.java b/Dream2/src/examples/java/dream/examples/form/FormServer.java index 1fc4e08..7c315e2 100644 --- a/Dream2/src/examples/java/dream/examples/form/FormServer.java +++ b/Dream2/src/examples/java/dream/examples/form/FormServer.java @@ -13,6 +13,7 @@ public class FormServer extends Client { protected RemoteVar working_hours; protected RemoteVar euro_per_hour; + protected RemoteVar required_minimum_hours; public FormServer() { super("FormServer"); @@ -23,7 +24,7 @@ public FormServer() { * Look for new clients every 5 seconds */ private void detectNewSession() { - while (euro_per_hour == null || working_hours == null) { + while (euro_per_hour == null || working_hours == null || required_minimum_hours == null) { for (String str : DreamClient.instance.listVariables()) { String host = str.split("@")[1]; String var = str.split("@")[0]; @@ -33,6 +34,9 @@ private void detectNewSession() { } else if (euro_per_hour == null && var.equalsIgnoreCase("euro_per_hour")) { euro_per_hour = new RemoteVar<>(host, var); logger.fine("Found Boss"); + } else if (required_minimum_hours == null && var.equalsIgnoreCase("required_minimum_hours")) { + required_minimum_hours = new RemoteVar<>(host, "required_minimum_hours"); + logger.fine("Found Boss"); } } try { @@ -48,11 +52,11 @@ protected void createDependencies() { logger.fine("Building Dependencies"); final Signal minimumHours = new Signal<>("minimumHours", () -> { - if (working_hours.get() != null) - return working_hours.get() > 10; + if (working_hours.get() != null && required_minimum_hours.get() != null) + return working_hours.get() > required_minimum_hours.get(); else return false; - }, working_hours); + }, working_hours, required_minimum_hours); final Signal maximumHours = new Signal<>("maximumHours", () -> { if (working_hours.get() != null) diff --git a/Dream2/src/examples/java/dream/examples/form/Secretary.java b/Dream2/src/examples/java/dream/examples/form/Secretary.java index 9c2b9b3..789bf3b 100644 --- a/Dream2/src/examples/java/dream/examples/form/Secretary.java +++ b/Dream2/src/examples/java/dream/examples/form/Secretary.java @@ -8,6 +8,7 @@ public class Secretary extends FormClient { public Secretary() { super("Secretary", "Working Hours"); + setInitValues(Integer.toString(5)); } @Override @@ -16,7 +17,7 @@ protected void init() { } @Override - public void typedText(String typedText) { + public void typedText(int i, String typedText) { Integer value = Integer.valueOf(typedText); wh.set(value); logger.fine("Set Working_Hours to " + value); diff --git a/Dream2/src/examples/java/dream/examples/form/graph.png b/Dream2/src/examples/java/dream/examples/form/graph.png index c2275dc7f8d3d8c95123aba83607b1f0dbd3e423..195cccc0ef57c9e20019a7689b24ce808913b7f9 100644 GIT binary patch literal 72093 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 From e03462b37e751897f8f044fe18e887738e9e44e5 Mon Sep 17 00:00:00 2001 From: Tobias Becker Date: Fri, 27 May 2016 11:13:48 +0200 Subject: [PATCH 095/161] updated GlitchFreeFormServer accordingly --- ...reeFormServer.java => SingleGlitchFreeFormServer.java} | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) rename Dream2/src/examples/java/dream/examples/form/{GlitchFreeFormServer.java => SingleGlitchFreeFormServer.java} (90%) diff --git a/Dream2/src/examples/java/dream/examples/form/GlitchFreeFormServer.java b/Dream2/src/examples/java/dream/examples/form/SingleGlitchFreeFormServer.java similarity index 90% rename from Dream2/src/examples/java/dream/examples/form/GlitchFreeFormServer.java rename to Dream2/src/examples/java/dream/examples/form/SingleGlitchFreeFormServer.java index ed01918..33320a0 100644 --- a/Dream2/src/examples/java/dream/examples/form/GlitchFreeFormServer.java +++ b/Dream2/src/examples/java/dream/examples/form/SingleGlitchFreeFormServer.java @@ -5,7 +5,7 @@ import dream.client.Signal; import dream.examples.util.Pair; -public class GlitchFreeFormServer extends FormServer { +public class SingleGlitchFreeFormServer extends FormServer { @Override protected void createDependencies() { @@ -15,8 +15,8 @@ protected void createDependencies() { final UpdateCounter maximumCounter = new UpdateCounter(); final Signal> minimumHours = new Signal<>("minimumHours", () -> { - return new Pair<>(working_hours.get() > 10, minimumCounter.incAndGet()); - }, working_hours); + return new Pair<>(working_hours.get() > required_minimum_hours.get(), minimumCounter.incAndGet()); + }, working_hours, required_minimum_hours); final Signal> maximumHours = new Signal<>("maximumHours", () -> { return new Pair<>(working_hours.get() < 60, maximumCounter.incAndGet()); @@ -55,7 +55,7 @@ protected void createDependencies() { } public static void main(String[] args) { - new GlitchFreeFormServer(); + new SingleGlitchFreeFormServer(); } } From f31a62e36a8075abbd018a0b11550a1f24b419dd Mon Sep 17 00:00:00 2001 From: Tobias Becker Date: Thu, 2 Jun 2016 17:05:52 +0200 Subject: [PATCH 096/161] some cleanup --- .../form/CompleteGlitchFreeFormServer.java | 95 ++++++ .../dream/examples/taskBoard/InitApp.java | 81 +++-- .../dream/examples/taskBoard/Monitor.java | 110 ------- .../java/dream/examples/taskBoard/Pair.java | 28 -- .../dream/examples/taskBoard/ServerNode.java | 32 +- .../java/dream/examples/taskBoard/Task.java | 39 +++ .../{NewTaskGUI.java => TaskCreater.java} | 284 +++++++++--------- .../dream/examples/taskBoard/TaskMonitor.java | 144 +++++++++ .../examples/taskBoard/TaskReviewer.java | 55 ---- .../java/dream/examples/taskBoard/Tasks.java | 40 --- 10 files changed, 489 insertions(+), 419 deletions(-) create mode 100644 Dream2/src/examples/java/dream/examples/form/CompleteGlitchFreeFormServer.java delete mode 100644 Dream2/src/examples/java/dream/examples/taskBoard/Monitor.java delete mode 100644 Dream2/src/examples/java/dream/examples/taskBoard/Pair.java create mode 100644 Dream2/src/examples/java/dream/examples/taskBoard/Task.java rename Dream2/src/examples/java/dream/examples/taskBoard/{NewTaskGUI.java => TaskCreater.java} (64%) create mode 100644 Dream2/src/examples/java/dream/examples/taskBoard/TaskMonitor.java delete mode 100644 Dream2/src/examples/java/dream/examples/taskBoard/TaskReviewer.java delete mode 100644 Dream2/src/examples/java/dream/examples/taskBoard/Tasks.java diff --git a/Dream2/src/examples/java/dream/examples/form/CompleteGlitchFreeFormServer.java b/Dream2/src/examples/java/dream/examples/form/CompleteGlitchFreeFormServer.java new file mode 100644 index 0000000..9fb1dd3 --- /dev/null +++ b/Dream2/src/examples/java/dream/examples/form/CompleteGlitchFreeFormServer.java @@ -0,0 +1,95 @@ +package dream.examples.form; + +import java.util.LinkedList; + +import dream.client.Signal; +import dream.examples.util.Pair; + +public class CompleteGlitchFreeFormServer extends SingleGlitchFreeFormServer { + + @Override + protected void createDependencies() { + logger.fine("Building Dependencies"); + + final Value lock = new Value<>(false); + + final Value uc_working_hours_value = new Value<>(); + final Signal uc_working_hours = new Signal<>("uc_working_hours", () -> { + if (!lock.get()) { + lock.set(true); + uc_working_hours_value.set(working_hours.get()); + } else { + // TODO after releasing the lock update with this value (queue?) + } + return uc_working_hours_value.get(); + }, working_hours); + + final Value uc_euro_per_hour_value = new Value<>(); + final Signal uc_euro_per_hour = new Signal<>("uc_euro_per_hour", () -> { + if (!lock.get()) { + lock.set(true); + uc_euro_per_hour_value.set(euro_per_hour.get()); + } else { + // TODO after releasing the lock update with this value (queue?) + } + return uc_euro_per_hour_value.get(); + }, euro_per_hour); + + final Value uc_req_minimum_hours_value = new Value<>(); + final Signal uc_req_minimum_hours = new Signal<>("uc_required_minimum_hours", () -> { + if (!lock.get()) { + lock.set(true); + uc_req_minimum_hours_value.set(required_minimum_hours.get()); + } else { + // TODO after releasing the lock update with this value (queue?) + } + return uc_req_minimum_hours_value.get(); + }, required_minimum_hours); + + final UpdateCounter minimumCounter = new UpdateCounter(); + final UpdateCounter maximumCounter = new UpdateCounter(); + + final Signal> minimumHours = new Signal<>("minimumHours", () -> { + return new Pair<>(working_hours.get() > required_minimum_hours.get(), minimumCounter.incAndGet()); + }, working_hours, required_minimum_hours); + + final Signal> maximumHours = new Signal<>("maximumHours", () -> { + return new Pair<>(working_hours.get() < 60, maximumCounter.incAndGet()); + }, working_hours); + + final Signal minimumEuroPerHour = new Signal<>("minimumEuroPerHour", () -> { + return euro_per_hour.get() > 10; + }, euro_per_hour); + + final LinkedList> minimumQueue = new LinkedList<>(); + final LinkedList> maximumQueue = new LinkedList<>(); + final Value currentValue = new Value<>(false); + + new Signal<>("settingsOkay", () -> { + if (minimumHours.get() != null + && (minimumQueue.isEmpty() || minimumQueue.getLast().getSecond() < minimumHours.get().getSecond())) + minimumQueue.add(minimumHours.get()); + if (maximumHours.get() != null + && (maximumQueue.isEmpty() || maximumQueue.getLast().getSecond() < maximumHours.get().getSecond())) + maximumQueue.add(maximumHours.get()); + + if (minimumQueue.size() > 0 && maximumQueue.size() > 0 && minimumEuroPerHour.get() != null) + currentValue.set( + minimumQueue.pop().getFirst() && maximumQueue.pop().getFirst() && minimumEuroPerHour.get()); + return currentValue.get(); + }, minimumHours, maximumHours, minimumEuroPerHour); + + new Signal<>("salary", () -> { + if (working_hours.get() != null && euro_per_hour.get() != null) + return working_hours.get() * euro_per_hour.get(); + else + return 0.0; + }, working_hours, euro_per_hour); + + logger.fine("Finished building Dependencies"); + } + + public static void main(String[] args) { + new SingleGlitchFreeFormServer(); + } +} diff --git a/Dream2/src/examples/java/dream/examples/taskBoard/InitApp.java b/Dream2/src/examples/java/dream/examples/taskBoard/InitApp.java index 097b9b5..89d49af 100644 --- a/Dream2/src/examples/java/dream/examples/taskBoard/InitApp.java +++ b/Dream2/src/examples/java/dream/examples/taskBoard/InitApp.java @@ -1,5 +1,7 @@ package dream.examples.taskBoard; +import dream.examples.util.NewJvmHelper; + /** * * @author Min Yang @@ -12,28 +14,59 @@ */ // TODO run the whole package together public class InitApp { - // public static void main(String... args) { - // Process serverNode = null; - // Process gui = null; - // Process viewer = null; - // try { - // serverNode = new NewJvmHelper().startNewJVM(ServerNode.class); - // gui = new NewJvmHelper().startNewJVM(NewTaskGUI.class); - // viewer = new NewJvmHelper().startNewJVM(TaskReviewer.class); - // Thread.sleep(5000 * 5000); - // } catch (InterruptedException e) { - // // TODO Auto-generated catch block - // e.printStackTrace(); - // } finally { - // if (serverNode != null) { - // serverNode.destroyForcibly(); - // } - // if (viewer != null) { - // viewer.destroyForcibly(); - // } - // if (gui != null) { - // gui.destroyForcibly(); - // } - // } - // } + + private static Process serverNode; + private static Process viewer; + private static Process gui; + + public static void main(String... args) { + serverNode = NewJvmHelper.startNewJVM(ServerNode.class); + gui = NewJvmHelper.startNewJVM(TaskCreater.class); + viewer = NewJvmHelper.startNewJVM(TaskMonitor.class); + + sleep(-1); + } + + private static void sleep(int time) { + do { + try { + Thread.sleep(time == -1 ? 1000 : time); + checkExit(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } while (time == -1); + + } + + private static void checkExit() { + if (!serverNode.isAlive()) { + System.out.println("server closed ... exiting!"); + destr(); + System.exit(0); + } + if (!viewer.isAlive()) { + System.out.println("viewer window closed ... exiting!"); + destr(); + System.exit(0); + } + if (!gui.isAlive()) { + System.out.println("gui window closed ... exiting!"); + destr(); + System.exit(0); + } + + } + + private static void destr() { + if (serverNode != null) { + serverNode.destroyForcibly(); + } + if (viewer != null) { + viewer.destroyForcibly(); + } + if (gui != null) { + gui.destroyForcibly(); + } + } } \ No newline at end of file diff --git a/Dream2/src/examples/java/dream/examples/taskBoard/Monitor.java b/Dream2/src/examples/java/dream/examples/taskBoard/Monitor.java deleted file mode 100644 index 72bc5db..0000000 --- a/Dream2/src/examples/java/dream/examples/taskBoard/Monitor.java +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Created by JFormDesigner on Fri May 20 23:52:44 CEST 2016 - */ - -package dream.examples.taskBoard; - -import java.awt.Container; -import java.awt.event.ActionEvent; -import java.util.logging.Logger; - -import javax.swing.GroupLayout; -import javax.swing.JButton; -import javax.swing.JFrame; -import javax.swing.JLabel; -import javax.swing.JOptionPane; -import javax.swing.JTextArea; -import javax.swing.LayoutStyle; - -/** - * @author Min Yang - */ -public class Monitor { - - public static void main(String[] args) { - - javax.swing.SwingUtilities.invokeLater(new Runnable() { - public void run() { - Monitor user = new Monitor(); - user.initComponents(); - } - }); - - } - - private void button1ActionPerformed(ActionEvent e) { - if (e.getActionCommand() == "SHOW") { - TaskReviewer view = new TaskReviewer(); - } else { - JOptionPane.showMessageDialog(null, "Ops, currently no task, please try again later."); - log.info("Wrong input pattern of tasks"); - } - } - - private void initComponents() { - // JFormDesigner - Component initialization - DO NOT MODIFY - // //GEN-BEGIN:initComponents - // Generated using JFormDesigner Evaluation license - Ashleeeeee Yang - frame1 = new JFrame(); - textArea1 = new JTextArea(); - label1 = new JLabel(); - label2 = new JLabel(); - textArea2 = new JTextArea(); - button1 = new JButton(); - - // ======== frame1 ======== - { - Container frame1ContentPane = frame1.getContentPane(); - // ---- label1 ---- - label1.setText("Current devs:"); - - // ---- label2 ---- - label2.setText("Current tests:"); - - // ---- button1 ---- - button1.setText("Show me tasks"); - button1.addActionListener(e -> button1ActionPerformed(e)); - button1.setActionCommand("SHOW"); - - GroupLayout frame1ContentPaneLayout = new GroupLayout(frame1ContentPane); - frame1ContentPane.setLayout(frame1ContentPaneLayout); - frame1ContentPaneLayout.setHorizontalGroup(frame1ContentPaneLayout.createParallelGroup() - .addGroup(frame1ContentPaneLayout.createSequentialGroup().addContainerGap() - .addGroup(frame1ContentPaneLayout.createParallelGroup().addComponent(label1).addComponent( - textArea2, GroupLayout.PREFERRED_SIZE, 57, GroupLayout.PREFERRED_SIZE)) - .addGap(30, 30, 30) - .addGroup(frame1ContentPaneLayout.createParallelGroup().addComponent(label2).addComponent( - textArea1, GroupLayout.PREFERRED_SIZE, 57, GroupLayout.PREFERRED_SIZE)) - .addContainerGap(9, Short.MAX_VALUE)) - .addGroup(GroupLayout.Alignment.TRAILING, frame1ContentPaneLayout.createSequentialGroup() - .addContainerGap(11, Short.MAX_VALUE).addComponent(button1).addGap(71, 71, 71))); - frame1ContentPaneLayout.setVerticalGroup(frame1ContentPaneLayout.createParallelGroup() - .addGroup(frame1ContentPaneLayout.createSequentialGroup().addComponent(button1) - .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED) - .addGroup(frame1ContentPaneLayout.createParallelGroup(GroupLayout.Alignment.BASELINE) - .addComponent(label2).addComponent(label1)) - .addGap(13, 13, 13) - .addGroup(frame1ContentPaneLayout.createParallelGroup(GroupLayout.Alignment.LEADING, false) - .addComponent(textArea2, GroupLayout.DEFAULT_SIZE, 108, Short.MAX_VALUE) - .addComponent(textArea1, GroupLayout.DEFAULT_SIZE, 108, Short.MAX_VALUE)) - .addContainerGap(GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))); - frame1.pack(); - frame1.setLocationRelativeTo(frame1.getOwner()); - frame1.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - frame1.setVisible(true); - } - // //GEN-END:initComponents - } - - // JFormDesigner - Variables declaration - DO NOT MODIFY - // //GEN-BEGIN:variables - // Generated using JFormDesigner Evaluation license - Ashleeeeee Yang - private JFrame frame1; - private static JTextArea textArea1; - private JLabel label1; - private JLabel label2; - private static JTextArea textArea2; - private JButton button1; - private Logger log = Logger.getLogger("Monitor"); - // JFormDesigner - End of variables declaration //GEN-END:variables -} diff --git a/Dream2/src/examples/java/dream/examples/taskBoard/Pair.java b/Dream2/src/examples/java/dream/examples/taskBoard/Pair.java deleted file mode 100644 index f141aab..0000000 --- a/Dream2/src/examples/java/dream/examples/taskBoard/Pair.java +++ /dev/null @@ -1,28 +0,0 @@ -package dream.examples.taskBoard; - -/** - * @author Min Yang - * @date May 13, 2016 - * @description common class for host-var pair - */ -public class Pair { - String host; - String Var; - - public String getHost() { - return host; - } - - public void setHost(String host) { - this.host = host; - } - - public String getVar() { - return Var; - } - - public void setVar(String var) { - Var = var; - } - -} diff --git a/Dream2/src/examples/java/dream/examples/taskBoard/ServerNode.java b/Dream2/src/examples/java/dream/examples/taskBoard/ServerNode.java index 28daf39..b0f9da8 100644 --- a/Dream2/src/examples/java/dream/examples/taskBoard/ServerNode.java +++ b/Dream2/src/examples/java/dream/examples/taskBoard/ServerNode.java @@ -13,15 +13,15 @@ /** * * @author Min Yang - * @date May 13, 2016 + * @author Tobias Becker * @description run background tasks: read task, create task lists: development * and test etc. */ public class ServerNode extends Client { public static final String NAME = "ServerNode"; private Var> myClients = null; - private Var devTask = null; - private Var testTask = null; + private Var developers = null; + private Var tasks = null; public static void main(String... args) { new ServerNode(); @@ -29,6 +29,8 @@ public static void main(String... args) { public ServerNode() { super(NAME); + developers = new Var("developers", ""); + tasks = new Var("tasks", ""); myClients = new Var>("Server_registered_clients", new ArrayList()); detectClients(); } @@ -48,29 +50,27 @@ private void detectClients() { } private void createClient(String clientHost, String clientVar) { - RemoteVar rv = new RemoteVar(clientHost, clientVar); - Signal sig = new Signal(clientHost, () -> { + System.out.println("detected client " + clientHost); + RemoteVar rv = new RemoteVar(clientHost, clientVar); + Signal sig = new Signal(clientHost, () -> { if (rv.get() != null) { return rv.get(); } else { - - return ""; + return null; } }, rv); sig.change().addHandler((oldValue, newValue) -> { if (newValue != null) { - String newDev = newValue.split(":")[0]; - String newTest = newValue.split(":")[1]; - if (devTask == null) - devTask = new Var("taskDevs", ""); - - if (testTask == null) - testTask = new Var("taskTests", ""); + Integer newDev = newValue.getDeveloper(); + Integer newTask = newValue.getTask(); // Set vars for remote querying - devTask.set(devTask.get().toString() + newDev + ":"); - testTask.set(testTask.get().toString() + newTest + ":"); + String devValue = developers.get().length() == 0 ? newDev.toString() : developers.get() + ":" + newDev; + String taskValue = tasks.get().length() == 0 ? newTask.toString() : tasks.get() + ":" + newTask; + developers.set(devValue); + tasks.set(taskValue); + System.out.println("new value from " + clientHost); } }); myClients.modify((old) -> old.add(clientVar + "@" + clientHost)); diff --git a/Dream2/src/examples/java/dream/examples/taskBoard/Task.java b/Dream2/src/examples/java/dream/examples/taskBoard/Task.java new file mode 100644 index 0000000..de35586 --- /dev/null +++ b/Dream2/src/examples/java/dream/examples/taskBoard/Task.java @@ -0,0 +1,39 @@ +package dream.examples.taskBoard; + +import java.io.Serializable; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * @author Min Yang + * @author Tobias Becker + * @description a task + */ +public class Task implements Serializable { + private static final long serialVersionUID = 8329097603920137211L; + public static String pattern = "D(\\d*):T(\\d*)"; + private int developer; + private int task; + + public Task(String input) { + Matcher m = Pattern.compile(pattern).matcher(input); + if (m.matches()) { + developer = Integer.parseInt(m.group(1)); + task = Integer.parseInt(m.group(2)); + } else + throw new UnsupportedOperationException("Wrong Input"); + } + + public static boolean isValid(String input) { + return input.matches(pattern); + } + + public int getDeveloper() { + return developer; + } + + public int getTask() { + return task; + } + +} diff --git a/Dream2/src/examples/java/dream/examples/taskBoard/NewTaskGUI.java b/Dream2/src/examples/java/dream/examples/taskBoard/TaskCreater.java similarity index 64% rename from Dream2/src/examples/java/dream/examples/taskBoard/NewTaskGUI.java rename to Dream2/src/examples/java/dream/examples/taskBoard/TaskCreater.java index aac6b8b..d01a596 100644 --- a/Dream2/src/examples/java/dream/examples/taskBoard/NewTaskGUI.java +++ b/Dream2/src/examples/java/dream/examples/taskBoard/TaskCreater.java @@ -1,146 +1,138 @@ -package dream.examples.taskBoard; - -import java.awt.Container; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.WindowEvent; -import java.awt.event.WindowListener; -import java.util.ArrayList; -import java.util.logging.Logger; - -import javax.swing.GroupLayout; -import javax.swing.JButton; -import javax.swing.JFrame; -import javax.swing.JOptionPane; -import javax.swing.JTextField; - -import dream.examples.util.NewJvmHelper; - -/** - * - * @author Min Yang - * @date May 15, 2016 - * @description - */ -public class NewTaskGUI { - public static JTextField textField1; - static Logger log = Logger.getLogger("NewTaskGUI"); - private static ArrayList tasks = new ArrayList(); - - public static void main(String[] args) { - - javax.swing.SwingUtilities.invokeLater(new Runnable() { - public void run() { - NewTaskGUI user = new NewTaskGUI(); - user.initComponents(); - } - }); - - } - - void initComponents() { - // JFormDesigner - Component initialization - DO NOT MODIFY - // //GEN-BEGIN:initComponents - // Generated using JFormDesigner Evaluation license - Min Yang - frame1 = new JFrame(); - frame1.addWindowListener(new WindowListener() { - @Override - public void windowOpened(WindowEvent e) { - } - - @Override - public void windowIconified(WindowEvent e) { - } - - @Override - public void windowDeiconified(WindowEvent e) { - } - - @Override - public void windowDeactivated(WindowEvent e) { - } - - @Override - public void windowClosing(WindowEvent e) { - onExit(); - } - - @Override - public void windowClosed(WindowEvent e) { - onExit(); - } - - @Override - public void windowActivated(WindowEvent e) { - } - }); - textField1 = new JTextField(); - button1 = new JButton(); - button1.setActionCommand("ADD"); - - // ======== frame1 ======== - { - Container frame1ContentPane = frame1.getContentPane(); - - // ---- button1 ---- - button1.setText("New Task"); - button1.addActionListener(new ButtonListener()); - - GroupLayout frame1ContentPaneLayout = new GroupLayout(frame1ContentPane); - frame1ContentPane.setLayout(frame1ContentPaneLayout); - frame1ContentPaneLayout - .setHorizontalGroup(frame1ContentPaneLayout.createParallelGroup() - .addGroup(frame1ContentPaneLayout.createSequentialGroup().addGap(20, 20, 20) - .addComponent(textField1, GroupLayout.PREFERRED_SIZE, 590, - GroupLayout.PREFERRED_SIZE) - .addGap(18, 18, 18).addComponent(button1).addContainerGap(34, Short.MAX_VALUE))); - - frame1ContentPaneLayout.setVerticalGroup(frame1ContentPaneLayout.createParallelGroup() - .addGroup(frame1ContentPaneLayout.createSequentialGroup().addGap(19, 19, 19) - .addGroup(frame1ContentPaneLayout.createParallelGroup(GroupLayout.Alignment.BASELINE) - .addComponent(button1).addComponent(textField1, GroupLayout.PREFERRED_SIZE, - GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)) - .addContainerGap(24, Short.MAX_VALUE))); - - frame1.pack(); - frame1.setLocationRelativeTo(frame1.getOwner()); - frame1.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - frame1.setVisible(true); - } - // //GEN-END:initComponents - - } - - // JFormDesigner - Variables declaration - DO NOT MODIFY - // //GEN-BEGIN:variables - // Generated using JFormDesigner Evaluation license - Min Yang - private JFrame frame1; - private JButton button1; - - // JFormDesigner - End of variables declaration //GEN-END:variables - static class ButtonListener implements ActionListener { - @Override - public void actionPerformed(ActionEvent paramActionEvent) { - if (paramActionEvent.getActionCommand() == "ADD") { - String toTasks = textField1.getText(); - if (toTasks.contains(":") && toTasks.split(":")[0].matches("D\\d*") - && toTasks.split(":")[1].matches("T\\d*")) { - // TODO: think! to make it more user friendly - tasks.add(NewJvmHelper.startNewJVM(Tasks.class, toTasks)); - } else { - textField1.setText(""); - JOptionPane.showMessageDialog(null, "Please input the right pattern of task."); - log.info("Wrong input pattern of tasks"); - } - } - } - } - - private void onExit() { - System.out.println("exit"); - for (Process p : tasks) { - p.destroyForcibly(); - System.out.println("Destroying " + p.toString()); - } - } -} +package dream.examples.taskBoard; + +import java.awt.Container; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.WindowEvent; +import java.awt.event.WindowListener; +import java.util.logging.Logger; + +import javax.swing.GroupLayout; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JOptionPane; +import javax.swing.JTextField; + +import dream.client.Var; +import dream.examples.util.Client; + +/** + * @author Min Yang + * @author Tobias Becker + */ +public class TaskCreater extends Client { + private Var var; + + public TaskCreater() { + super("TaskCreater"); + var = new Var("FromTaskNode", null); + new TaskCreaterGUI(this); + } + + public static void main(String[] args) { + new TaskCreater(); + } + + public Logger getLogger() { + return logger; + } + + public void addTask(String toTasks) { + var.set(new Task(toTasks)); + } +} + +class TaskCreaterGUI { + private JTextField textField1; + private JFrame frame1; + private JButton button1; + private TaskCreater taskCreater; + + public TaskCreaterGUI(TaskCreater t) { + this.taskCreater = t; + initComponents(); + } + + void initComponents() { + frame1 = new JFrame(); + frame1.addWindowListener(new WindowListener() { + @Override + public void windowOpened(WindowEvent e) { + } + + @Override + public void windowIconified(WindowEvent e) { + } + + @Override + public void windowDeiconified(WindowEvent e) { + } + + @Override + public void windowDeactivated(WindowEvent e) { + } + + @Override + public void windowClosing(WindowEvent e) { + } + + @Override + public void windowClosed(WindowEvent e) { + } + + @Override + public void windowActivated(WindowEvent e) { + } + }); + textField1 = new JTextField(); + button1 = new JButton(); + button1.setActionCommand("ADD"); + + // ======== frame1 ======== + { + Container frame1ContentPane = frame1.getContentPane(); + + // ---- button1 ---- + button1.setText("New Task"); + button1.addActionListener(new ButtonListener()); + + GroupLayout frame1ContentPaneLayout = new GroupLayout(frame1ContentPane); + frame1ContentPane.setLayout(frame1ContentPaneLayout); + frame1ContentPaneLayout + .setHorizontalGroup(frame1ContentPaneLayout.createParallelGroup() + .addGroup(frame1ContentPaneLayout.createSequentialGroup().addGap(20, 20, 20) + .addComponent(textField1, GroupLayout.PREFERRED_SIZE, 590, + GroupLayout.PREFERRED_SIZE) + .addGap(18, 18, 18).addComponent(button1).addContainerGap(34, Short.MAX_VALUE))); + + frame1ContentPaneLayout.setVerticalGroup(frame1ContentPaneLayout.createParallelGroup() + .addGroup(frame1ContentPaneLayout.createSequentialGroup().addGap(19, 19, 19) + .addGroup(frame1ContentPaneLayout.createParallelGroup(GroupLayout.Alignment.BASELINE) + .addComponent(button1).addComponent(textField1, GroupLayout.PREFERRED_SIZE, + GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)) + .addContainerGap(24, Short.MAX_VALUE))); + + frame1.pack(); + frame1.setLocationRelativeTo(frame1.getOwner()); + frame1.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + frame1.setVisible(true); + } + } + + class ButtonListener implements ActionListener { + @Override + public void actionPerformed(ActionEvent paramActionEvent) { + if (paramActionEvent.getActionCommand() == "ADD") { + String toTasks = textField1.getText(); + if (Task.isValid(toTasks)) { + taskCreater.addTask(toTasks); + textField1.setText(""); + } else { + textField1.setText(""); + JOptionPane.showMessageDialog(null, "Please input the right pattern of task. (D:T)"); + taskCreater.getLogger().info("Wrong input pattern of tasks"); + } + } + } + } +} diff --git a/Dream2/src/examples/java/dream/examples/taskBoard/TaskMonitor.java b/Dream2/src/examples/java/dream/examples/taskBoard/TaskMonitor.java new file mode 100644 index 0000000..0734ba9 --- /dev/null +++ b/Dream2/src/examples/java/dream/examples/taskBoard/TaskMonitor.java @@ -0,0 +1,144 @@ +package dream.examples.taskBoard; + +import java.awt.Container; +import java.awt.event.ActionEvent; +import java.util.Arrays; +import java.util.List; + +import javax.swing.GroupLayout; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JTextArea; +import javax.swing.LayoutStyle; + +import dream.client.RemoteVar; +import dream.client.Signal; +import dream.examples.util.Client; + +/** + * @author Min Yang + * @author Tobias Becker + * @description Review the tasks. + */ +public class TaskMonitor extends Client { + + private final MonitorGUI gui; + private final RemoteVar devs; + private final RemoteVar tasks; + private final Signal sigDevs; + private final Signal sigTasks; + + @Override + protected List waitForVars() { + return Arrays.asList("developers@ServerNode", "tasks@ServerNode"); + } + + public TaskMonitor() { + super("TaskMonitor"); + gui = new MonitorGUI(this); + + devs = new RemoteVar("ServerNode", "developers"); + sigDevs = new Signal("sigDevs", () -> { + return devs.get(); + }, devs); + + tasks = new RemoteVar("ServerNode", "tasks"); + sigTasks = new Signal("sigTests", () -> { + return tasks.get(); + }, tasks); + + // TODO show in monitor + sigDevs.change().addHandler((oldVa, newVal) -> { + System.out.println("newVal devs:" + newVal); + gui.setDevs(newVal); + }); + + sigTasks.change().addHandler((oldVa, newVal) -> { + System.out.println("newVal tasks:" + newVal); + gui.setTasks(newVal); + }); + } + + public void clickbutton1() { + // TODO: + } + + public static void main(String[] args) { + new TaskMonitor(); + } +} + +class MonitorGUI { + private JFrame frame1; + private JTextArea textAreaTasks; + private JLabel label1; + private JLabel label2; + private JTextArea textAreaDevs; + private JButton button1; + private TaskMonitor monitor; + + public MonitorGUI(TaskMonitor m) { + this.monitor = m; + frame1 = new JFrame(); + textAreaTasks = new JTextArea(); + label1 = new JLabel(); + label2 = new JLabel(); + textAreaDevs = new JTextArea(); + button1 = new JButton(); + + // ======== frame1 ======== + { + Container frame1ContentPane = frame1.getContentPane(); + // ---- label1 ---- + label1.setText("Current devs:"); + + // ---- label2 ---- + label2.setText("Current tasks:"); + + // ---- button1 ---- + button1.setText("Show me tasks"); + button1.addActionListener(e -> button1ActionPerformed(e)); + + GroupLayout frame1ContentPaneLayout = new GroupLayout(frame1ContentPane); + frame1ContentPane.setLayout(frame1ContentPaneLayout); + frame1ContentPaneLayout.setHorizontalGroup(frame1ContentPaneLayout.createParallelGroup() + .addGroup(frame1ContentPaneLayout.createSequentialGroup().addContainerGap() + .addGroup(frame1ContentPaneLayout.createParallelGroup().addComponent(label1).addComponent( + textAreaDevs, GroupLayout.PREFERRED_SIZE, 57, GroupLayout.PREFERRED_SIZE)) + .addGap(30, 30, 30) + .addGroup(frame1ContentPaneLayout.createParallelGroup().addComponent(label2).addComponent( + textAreaTasks, GroupLayout.PREFERRED_SIZE, 57, GroupLayout.PREFERRED_SIZE)) + .addContainerGap(9, Short.MAX_VALUE)) + .addGroup(GroupLayout.Alignment.TRAILING, + frame1ContentPaneLayout.createSequentialGroup().addContainerGap(11, Short.MAX_VALUE)));// .addComponent(button1).addGap(71, + frame1ContentPaneLayout.setVerticalGroup(frame1ContentPaneLayout.createParallelGroup() + .addGroup(frame1ContentPaneLayout.createSequentialGroup() // .addComponent(button1) + .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED) + .addGroup(frame1ContentPaneLayout.createParallelGroup(GroupLayout.Alignment.BASELINE) + .addComponent(label2).addComponent(label1)) + .addGap(13, 13, 13) + .addGroup(frame1ContentPaneLayout.createParallelGroup(GroupLayout.Alignment.LEADING, false) + .addComponent(textAreaDevs, GroupLayout.DEFAULT_SIZE, 108, + Short.MAX_VALUE) + .addComponent(textAreaTasks, GroupLayout.DEFAULT_SIZE, 108, Short.MAX_VALUE)) + .addContainerGap(GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))); + frame1.pack(); + frame1.setLocationRelativeTo(frame1.getOwner()); + frame1.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + frame1.setVisible(true); + } + } + + public void setDevs(String value) { + textAreaDevs.setText(value.replace(":", "\n")); + } + + public void setTasks(String value) { + textAreaTasks.setText(value.replace(":", "\n")); + } + + private void button1ActionPerformed(ActionEvent e) { + monitor.clickbutton1(); + } +} \ No newline at end of file diff --git a/Dream2/src/examples/java/dream/examples/taskBoard/TaskReviewer.java b/Dream2/src/examples/java/dream/examples/taskBoard/TaskReviewer.java deleted file mode 100644 index f483cf0..0000000 --- a/Dream2/src/examples/java/dream/examples/taskBoard/TaskReviewer.java +++ /dev/null @@ -1,55 +0,0 @@ -package dream.examples.taskBoard; - -import java.util.Arrays; -import java.util.List; - -import dream.client.RemoteVar; -import dream.client.Signal; -import dream.examples.util.Client; - -/** - * - * @author Min Yang - * @date May 13, 2016 - * @description Review the tasks. - */ -public class TaskReviewer extends Client { - RemoteVar devs = null; - RemoteVar tests = null; - Signal sigDevs = null; - Signal sigTests = null; - - public static void main(String[] args) { - new TaskReviewer(); - } - - @Override - protected List waitForVars() { - return Arrays.asList("taskDevs@ServerNode", "taskTests@ServerNode"); - } - - public TaskReviewer() { - super("QueryClient"); - if (devs == null) { - devs = new RemoteVar("ServerNode", "taskDevs"); - sigDevs = new Signal("sigDevs", () -> { - return devs.get(); - }, devs); - } - if (tests == null) { - - tests = new RemoteVar("ServerNode", "taskTests"); - sigTests = new Signal("sigTests", () -> { - return tests.get(); - }, tests); - } - // TODO show in monitor - sigDevs.change().addHandler((oldVa, newVal) -> { - System.out.println("newVal devs:" + newVal); - }); - - sigTests.change().addHandler((oldVa, newVal) -> { - System.out.println("newVal tests:" + newVal); - }); - } -} diff --git a/Dream2/src/examples/java/dream/examples/taskBoard/Tasks.java b/Dream2/src/examples/java/dream/examples/taskBoard/Tasks.java deleted file mode 100644 index 4ba430e..0000000 --- a/Dream2/src/examples/java/dream/examples/taskBoard/Tasks.java +++ /dev/null @@ -1,40 +0,0 @@ -package dream.examples.taskBoard; - -import java.awt.EventQueue; -import java.util.Random; -import java.util.logging.Logger; - -import dream.client.Var; -import dream.common.Consts; -import dream.examples.util.Client; - -/** - * - * @author Min Yang - * @date May 13, 2016 - * @description Creating tasks. - */ -public class Tasks extends Client { - - public Tasks(String input) { - super("TaskNode" + new Random().nextInt(100)); - System.out.println("HOST name:" + Consts.hostName); - Var v = new Var("FromTaskNode", input); - for (int i = 0; i < 10; i++) { - try { - Thread.sleep(500); - } catch (InterruptedException e) { - e.printStackTrace(); - } - v.set(input); - } - } - - public static void main(String[] args) { - if (args.length < 1) { - Logger.getGlobal().severe("developer/tasks missing"); - return; - } - EventQueue.invokeLater(() -> new Tasks(args[0])); - } -} From 3cd22573e4e00b6c88e3fcadf7efcd23086d7882 Mon Sep 17 00:00:00 2001 From: Tobias Becker Date: Thu, 2 Jun 2016 17:22:14 +0200 Subject: [PATCH 097/161] added descriptions to all classes --- .../dream/examples/taskBoard/InitApp.java | 17 ++++++----- .../dream/examples/taskBoard/ServerNode.java | 5 ++-- .../java/dream/examples/taskBoard/Task.java | 3 +- .../dream/examples/taskBoard/TaskCreater.java | 5 +++- .../dream/examples/taskBoard/TaskMonitor.java | 29 ++++--------------- 5 files changed, 23 insertions(+), 36 deletions(-) diff --git a/Dream2/src/examples/java/dream/examples/taskBoard/InitApp.java b/Dream2/src/examples/java/dream/examples/taskBoard/InitApp.java index 89d49af..b2605bf 100644 --- a/Dream2/src/examples/java/dream/examples/taskBoard/InitApp.java +++ b/Dream2/src/examples/java/dream/examples/taskBoard/InitApp.java @@ -3,18 +3,19 @@ import dream.examples.util.NewJvmHelper; /** + * To start this example either:
+ * - run {@link ServerNode}, {@link TaskCreater} and {@link TaskMonitor} in any + * order
+ * - or run this class.
+ * This class will start all three classes each in a seperate instance of the + * JVM. It will also stop all classes if one of them is stopped normally.
+ * WARNING: If they are destroyed forcefully via Eclipse (or any other + * way) there may continue to run and will have to be exited manually. * * @author Min Yang - * @date May 13, 2016 - * @description User can first run - * ServerNode.java(..examples.taskBoard.ServerNode.java), then run - * NewTaskGUI.java (..examples.taskBoard.NewTaskGUI.java), then run - * TaskReviewer.java (..examples.taskBoard.TaskReviewer.java) - * + * @author Tobias Becker */ -// TODO run the whole package together public class InitApp { - private static Process serverNode; private static Process viewer; private static Process gui; diff --git a/Dream2/src/examples/java/dream/examples/taskBoard/ServerNode.java b/Dream2/src/examples/java/dream/examples/taskBoard/ServerNode.java index b0f9da8..c8fec67 100644 --- a/Dream2/src/examples/java/dream/examples/taskBoard/ServerNode.java +++ b/Dream2/src/examples/java/dream/examples/taskBoard/ServerNode.java @@ -11,11 +11,12 @@ import dream.examples.util.Pair; /** + * Holds a list of tasks and a list of developers each indicated by a simple + * integer. Searches for new clients (TaskCreater) and registers to their + * "task creation channels" * * @author Min Yang * @author Tobias Becker - * @description run background tasks: read task, create task lists: development - * and test etc. */ public class ServerNode extends Client { public static final String NAME = "ServerNode"; diff --git a/Dream2/src/examples/java/dream/examples/taskBoard/Task.java b/Dream2/src/examples/java/dream/examples/taskBoard/Task.java index de35586..0a3bd7e 100644 --- a/Dream2/src/examples/java/dream/examples/taskBoard/Task.java +++ b/Dream2/src/examples/java/dream/examples/taskBoard/Task.java @@ -5,9 +5,10 @@ import java.util.regex.Pattern; /** + * Small serializable class that represents a task assigned to a developer. + * * @author Min Yang * @author Tobias Becker - * @description a task */ public class Task implements Serializable { private static final long serialVersionUID = 8329097603920137211L; diff --git a/Dream2/src/examples/java/dream/examples/taskBoard/TaskCreater.java b/Dream2/src/examples/java/dream/examples/taskBoard/TaskCreater.java index d01a596..c2a5ca4 100644 --- a/Dream2/src/examples/java/dream/examples/taskBoard/TaskCreater.java +++ b/Dream2/src/examples/java/dream/examples/taskBoard/TaskCreater.java @@ -5,6 +5,7 @@ import java.awt.event.ActionListener; import java.awt.event.WindowEvent; import java.awt.event.WindowListener; +import java.util.Random; import java.util.logging.Logger; import javax.swing.GroupLayout; @@ -17,6 +18,8 @@ import dream.examples.util.Client; /** + * Interface to create new Tasks. May be started multiple times! + * * @author Min Yang * @author Tobias Becker */ @@ -24,7 +27,7 @@ public class TaskCreater extends Client { private Var var; public TaskCreater() { - super("TaskCreater"); + super("TaskCreater" + new Random().nextInt(1000)); var = new Var("FromTaskNode", null); new TaskCreaterGUI(this); } diff --git a/Dream2/src/examples/java/dream/examples/taskBoard/TaskMonitor.java b/Dream2/src/examples/java/dream/examples/taskBoard/TaskMonitor.java index 0734ba9..2665923 100644 --- a/Dream2/src/examples/java/dream/examples/taskBoard/TaskMonitor.java +++ b/Dream2/src/examples/java/dream/examples/taskBoard/TaskMonitor.java @@ -1,12 +1,10 @@ package dream.examples.taskBoard; import java.awt.Container; -import java.awt.event.ActionEvent; import java.util.Arrays; import java.util.List; import javax.swing.GroupLayout; -import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JTextArea; @@ -36,7 +34,7 @@ protected List waitForVars() { public TaskMonitor() { super("TaskMonitor"); - gui = new MonitorGUI(this); + gui = new MonitorGUI(); devs = new RemoteVar("ServerNode", "developers"); sigDevs = new Signal("sigDevs", () -> { @@ -60,10 +58,6 @@ public TaskMonitor() { }); } - public void clickbutton1() { - // TODO: - } - public static void main(String[] args) { new TaskMonitor(); } @@ -75,17 +69,13 @@ class MonitorGUI { private JLabel label1; private JLabel label2; private JTextArea textAreaDevs; - private JButton button1; - private TaskMonitor monitor; - public MonitorGUI(TaskMonitor m) { - this.monitor = m; + public MonitorGUI() { frame1 = new JFrame(); textAreaTasks = new JTextArea(); label1 = new JLabel(); label2 = new JLabel(); textAreaDevs = new JTextArea(); - button1 = new JButton(); // ======== frame1 ======== { @@ -96,10 +86,6 @@ public MonitorGUI(TaskMonitor m) { // ---- label2 ---- label2.setText("Current tasks:"); - // ---- button1 ---- - button1.setText("Show me tasks"); - button1.addActionListener(e -> button1ActionPerformed(e)); - GroupLayout frame1ContentPaneLayout = new GroupLayout(frame1ContentPane); frame1ContentPane.setLayout(frame1ContentPaneLayout); frame1ContentPaneLayout.setHorizontalGroup(frame1ContentPaneLayout.createParallelGroup() @@ -111,16 +97,15 @@ public MonitorGUI(TaskMonitor m) { textAreaTasks, GroupLayout.PREFERRED_SIZE, 57, GroupLayout.PREFERRED_SIZE)) .addContainerGap(9, Short.MAX_VALUE)) .addGroup(GroupLayout.Alignment.TRAILING, - frame1ContentPaneLayout.createSequentialGroup().addContainerGap(11, Short.MAX_VALUE)));// .addComponent(button1).addGap(71, + frame1ContentPaneLayout.createSequentialGroup().addContainerGap(11, Short.MAX_VALUE))); frame1ContentPaneLayout.setVerticalGroup(frame1ContentPaneLayout.createParallelGroup() - .addGroup(frame1ContentPaneLayout.createSequentialGroup() // .addComponent(button1) + .addGroup(frame1ContentPaneLayout.createSequentialGroup() .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED) .addGroup(frame1ContentPaneLayout.createParallelGroup(GroupLayout.Alignment.BASELINE) .addComponent(label2).addComponent(label1)) .addGap(13, 13, 13) .addGroup(frame1ContentPaneLayout.createParallelGroup(GroupLayout.Alignment.LEADING, false) - .addComponent(textAreaDevs, GroupLayout.DEFAULT_SIZE, 108, - Short.MAX_VALUE) + .addComponent(textAreaDevs, GroupLayout.DEFAULT_SIZE, 108, Short.MAX_VALUE) .addComponent(textAreaTasks, GroupLayout.DEFAULT_SIZE, 108, Short.MAX_VALUE)) .addContainerGap(GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))); frame1.pack(); @@ -137,8 +122,4 @@ public void setDevs(String value) { public void setTasks(String value) { textAreaTasks.setText(value.replace(":", "\n")); } - - private void button1ActionPerformed(ActionEvent e) { - monitor.clickbutton1(); - } } \ No newline at end of file From 3c6c090401e15287d5a52cba053e36ac1dcbf3e8 Mon Sep 17 00:00:00 2001 From: Tobias Becker Date: Thu, 2 Jun 2016 17:29:16 +0200 Subject: [PATCH 098/161] Some fixes --- .../dream/examples/taskBoard/InitApp.java | 46 +++++++------------ .../dream/examples/taskBoard/TaskMonitor.java | 7 ++- 2 files changed, 22 insertions(+), 31 deletions(-) diff --git a/Dream2/src/examples/java/dream/examples/taskBoard/InitApp.java b/Dream2/src/examples/java/dream/examples/taskBoard/InitApp.java index b2605bf..2e83ecc 100644 --- a/Dream2/src/examples/java/dream/examples/taskBoard/InitApp.java +++ b/Dream2/src/examples/java/dream/examples/taskBoard/InitApp.java @@ -1,5 +1,8 @@ package dream.examples.taskBoard; +import java.util.ArrayList; +import java.util.List; + import dream.examples.util.NewJvmHelper; /** @@ -16,14 +19,14 @@ * @author Tobias Becker */ public class InitApp { - private static Process serverNode; - private static Process viewer; - private static Process gui; + private static List processes; public static void main(String... args) { - serverNode = NewJvmHelper.startNewJVM(ServerNode.class); - gui = NewJvmHelper.startNewJVM(TaskCreater.class); - viewer = NewJvmHelper.startNewJVM(TaskMonitor.class); + processes = new ArrayList<>(); + processes.add(NewJvmHelper.startNewJVM(ServerNode.class)); + processes.add(NewJvmHelper.startNewJVM(TaskCreater.class)); + processes.add(NewJvmHelper.startNewJVM(TaskCreater.class)); + processes.add(NewJvmHelper.startNewJVM(TaskMonitor.class)); sleep(-1); } @@ -41,33 +44,18 @@ private static void sleep(int time) { } private static void checkExit() { - if (!serverNode.isAlive()) { - System.out.println("server closed ... exiting!"); - destr(); - System.exit(0); - } - if (!viewer.isAlive()) { - System.out.println("viewer window closed ... exiting!"); - destr(); - System.exit(0); - } - if (!gui.isAlive()) { - System.out.println("gui window closed ... exiting!"); - destr(); - System.exit(0); + for (Process p : processes) { + if (!p.isAlive()) { + System.out.println(p.getClass().getSimpleName() + " closed ... exiting!"); + destr(); + System.exit(0); + } } - } private static void destr() { - if (serverNode != null) { - serverNode.destroyForcibly(); - } - if (viewer != null) { - viewer.destroyForcibly(); - } - if (gui != null) { - gui.destroyForcibly(); + for (Process p : processes) { + p.destroyForcibly(); } } } \ No newline at end of file diff --git a/Dream2/src/examples/java/dream/examples/taskBoard/TaskMonitor.java b/Dream2/src/examples/java/dream/examples/taskBoard/TaskMonitor.java index 2665923..c531c44 100644 --- a/Dream2/src/examples/java/dream/examples/taskBoard/TaskMonitor.java +++ b/Dream2/src/examples/java/dream/examples/taskBoard/TaskMonitor.java @@ -15,9 +15,10 @@ import dream.examples.util.Client; /** + * Displays both lists, the developers and the tasks. + * * @author Min Yang * @author Tobias Becker - * @description Review the tasks. */ public class TaskMonitor extends Client { @@ -72,10 +73,12 @@ class MonitorGUI { public MonitorGUI() { frame1 = new JFrame(); - textAreaTasks = new JTextArea(); label1 = new JLabel(); label2 = new JLabel(); + textAreaTasks = new JTextArea(); textAreaDevs = new JTextArea(); + textAreaDevs.setEditable(false); + textAreaTasks.setEditable(false); // ======== frame1 ======== { From 52777e6c45e356fdf26c98e204da417566204c4d Mon Sep 17 00:00:00 2001 From: Tobias Becker Date: Thu, 2 Jun 2016 17:42:53 +0200 Subject: [PATCH 099/161] refactored to use two channels between creater and server --- .../dream/examples/taskBoard/ServerNode.java | 23 +++++++++---------- .../java/dream/examples/taskBoard/Task.java | 8 +++++++ .../dream/examples/taskBoard/TaskCreater.java | 13 +++++++---- 3 files changed, 27 insertions(+), 17 deletions(-) diff --git a/Dream2/src/examples/java/dream/examples/taskBoard/ServerNode.java b/Dream2/src/examples/java/dream/examples/taskBoard/ServerNode.java index c8fec67..a677dbe 100644 --- a/Dream2/src/examples/java/dream/examples/taskBoard/ServerNode.java +++ b/Dream2/src/examples/java/dream/examples/taskBoard/ServerNode.java @@ -40,7 +40,7 @@ private void detectClients() { Set vars = DreamClient.instance.listVariables(); vars.stream().map(x -> new Pair(x.split("@")[1], x.split("@")[0])) .filter(x -> !myClients.get().contains(x.getSecond() + "@" + x.getFirst()) - && x.getSecond().equalsIgnoreCase("FromTaskNode")) + && (x.getSecond().equalsIgnoreCase("newTask") || x.getSecond().equalsIgnoreCase("newDev"))) .forEach(x -> createClient(x.getFirst(), x.getSecond())); try { Thread.sleep(500); @@ -51,9 +51,9 @@ private void detectClients() { } private void createClient(String clientHost, String clientVar) { - System.out.println("detected client " + clientHost); - RemoteVar rv = new RemoteVar(clientHost, clientVar); - Signal sig = new Signal(clientHost, () -> { + System.out.println("detected client " + clientHost + " " + clientVar); + RemoteVar rv = new RemoteVar<>(clientHost, clientVar); + Signal sig = new Signal<>(clientHost, () -> { if (rv.get() != null) { return rv.get(); } else { @@ -63,15 +63,14 @@ private void createClient(String clientHost, String clientVar) { sig.change().addHandler((oldValue, newValue) -> { if (newValue != null) { - Integer newDev = newValue.getDeveloper(); - Integer newTask = newValue.getTask(); - // Set vars for remote querying - String devValue = developers.get().length() == 0 ? newDev.toString() : developers.get() + ":" + newDev; - String taskValue = tasks.get().length() == 0 ? newTask.toString() : tasks.get() + ":" + newTask; - developers.set(devValue); - tasks.set(taskValue); - System.out.println("new value from " + clientHost); + if (clientVar.equalsIgnoreCase("newDev")) { + developers.set(developers.get().length() == 0 ? newValue : developers.get() + ":" + newValue); + } + if (clientVar.equalsIgnoreCase("newTask")) { + tasks.set(tasks.get().length() == 0 ? newValue : tasks.get() + ":" + newValue); + } + System.out.println("new value from " + clientHost + "@" + clientVar); } }); myClients.modify((old) -> old.add(clientVar + "@" + clientHost)); diff --git a/Dream2/src/examples/java/dream/examples/taskBoard/Task.java b/Dream2/src/examples/java/dream/examples/taskBoard/Task.java index 0a3bd7e..d1811af 100644 --- a/Dream2/src/examples/java/dream/examples/taskBoard/Task.java +++ b/Dream2/src/examples/java/dream/examples/taskBoard/Task.java @@ -37,4 +37,12 @@ public int getTask() { return task; } + public String getDevString() { + return Integer.toString(developer); + } + + public String getTaskString() { + return Integer.toString(task); + } + } diff --git a/Dream2/src/examples/java/dream/examples/taskBoard/TaskCreater.java b/Dream2/src/examples/java/dream/examples/taskBoard/TaskCreater.java index c2a5ca4..9b9dc04 100644 --- a/Dream2/src/examples/java/dream/examples/taskBoard/TaskCreater.java +++ b/Dream2/src/examples/java/dream/examples/taskBoard/TaskCreater.java @@ -24,11 +24,13 @@ * @author Tobias Becker */ public class TaskCreater extends Client { - private Var var; + private Var taskCreater; + private Var devCreater; public TaskCreater() { super("TaskCreater" + new Random().nextInt(1000)); - var = new Var("FromTaskNode", null); + taskCreater = new Var<>("newTask", null); + devCreater = new Var<>("newDev", null); new TaskCreaterGUI(this); } @@ -40,8 +42,9 @@ public Logger getLogger() { return logger; } - public void addTask(String toTasks) { - var.set(new Task(toTasks)); + public void addTask(Task t) { + taskCreater.set(t.getTaskString()); + devCreater.set(t.getDevString()); } } @@ -128,7 +131,7 @@ public void actionPerformed(ActionEvent paramActionEvent) { if (paramActionEvent.getActionCommand() == "ADD") { String toTasks = textField1.getText(); if (Task.isValid(toTasks)) { - taskCreater.addTask(toTasks); + taskCreater.addTask(new Task(toTasks)); textField1.setText(""); } else { textField1.setText(""); From 91a8cf2430a84f2781603e61ea51ef17738ca0d2 Mon Sep 17 00:00:00 2001 From: Tobias Becker Date: Thu, 2 Jun 2016 17:45:52 +0200 Subject: [PATCH 100/161] added some usability --- .../dream/examples/taskBoard/TaskCreater.java | 37 +++++++++++++------ 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/Dream2/src/examples/java/dream/examples/taskBoard/TaskCreater.java b/Dream2/src/examples/java/dream/examples/taskBoard/TaskCreater.java index 9b9dc04..9594767 100644 --- a/Dream2/src/examples/java/dream/examples/taskBoard/TaskCreater.java +++ b/Dream2/src/examples/java/dream/examples/taskBoard/TaskCreater.java @@ -3,6 +3,8 @@ import java.awt.Container; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import java.awt.event.KeyEvent; +import java.awt.event.KeyListener; import java.awt.event.WindowEvent; import java.awt.event.WindowListener; import java.util.Random; @@ -91,8 +93,23 @@ public void windowActivated(WindowEvent e) { } }); textField1 = new JTextField(); + textField1.addKeyListener(new KeyListener() { + + @Override + public void keyTyped(KeyEvent e) { + } + + @Override + public void keyReleased(KeyEvent e) { + } + + @Override + public void keyPressed(KeyEvent e) { + if (e.getKeyCode() == KeyEvent.VK_ENTER) + button1.doClick(); + } + }); button1 = new JButton(); - button1.setActionCommand("ADD"); // ======== frame1 ======== { @@ -128,16 +145,14 @@ public void windowActivated(WindowEvent e) { class ButtonListener implements ActionListener { @Override public void actionPerformed(ActionEvent paramActionEvent) { - if (paramActionEvent.getActionCommand() == "ADD") { - String toTasks = textField1.getText(); - if (Task.isValid(toTasks)) { - taskCreater.addTask(new Task(toTasks)); - textField1.setText(""); - } else { - textField1.setText(""); - JOptionPane.showMessageDialog(null, "Please input the right pattern of task. (D:T)"); - taskCreater.getLogger().info("Wrong input pattern of tasks"); - } + String toTasks = textField1.getText(); + if (Task.isValid(toTasks)) { + taskCreater.addTask(new Task(toTasks)); + textField1.setText(""); + } else { + textField1.setText(""); + JOptionPane.showMessageDialog(null, "Please input the right pattern of task. (D:T)"); + taskCreater.getLogger().info("Wrong input pattern of tasks"); } } } From 8152f816a644f67654ddca3e43619501febe6dac Mon Sep 17 00:00:00 2001 From: Tobias Becker Date: Fri, 3 Jun 2016 11:38:25 +0200 Subject: [PATCH 101/161] variable names to constants --- .../java/dream/examples/form/Boss.java | 25 ++++-- .../form/CompleteGlitchFreeFormServer.java | 82 +----------------- .../java/dream/examples/form/FormServer.java | 42 +++++---- .../java/dream/examples/form/Secretary.java | 7 +- .../form/SingleGlitchFreeFormServer.java | 14 +-- .../form/complete_glitchfree_graph.png | Bin 0 -> 77390 bytes .../java/dream/examples/form/graph.png | Bin 72093 -> 67919 bytes .../java/dream/examples/form/graph2.png | Bin 0 -> 72093 bytes 8 files changed, 54 insertions(+), 116 deletions(-) create mode 100644 Dream2/src/examples/java/dream/examples/form/complete_glitchfree_graph.png create mode 100644 Dream2/src/examples/java/dream/examples/form/graph2.png diff --git a/Dream2/src/examples/java/dream/examples/form/Boss.java b/Dream2/src/examples/java/dream/examples/form/Boss.java index 84d9f08..977fb38 100644 --- a/Dream2/src/examples/java/dream/examples/form/Boss.java +++ b/Dream2/src/examples/java/dream/examples/form/Boss.java @@ -1,21 +1,26 @@ package dream.examples.form; import dream.client.Var; +import dream.examples.util.Pair; public class Boss extends FormClient { + public static final String NAME = "Boss"; + public static final String EuroPerHour = "euro_per_hour"; + public static final String RequiredHours = "required_hours"; + private Var eph; - private Var rmh; + private Var> rh; public Boss() { - super("Boss", "Euro/Hour", "Minimum Hours"); - setInitValues(Double.toString(8.5), Integer.toString(10)); + super(NAME, "Euro/Hour", "Minimum Hours", "Maximum Hours"); + setInitValues(Double.toString(8.5), Integer.toString(10), Integer.toString(60)); } @Override protected void init() { - eph = new Var<>("euro_per_hour", 8.5); - rmh = new Var<>("required_minimum_hours", 10); + eph = new Var<>(EuroPerHour, 8.5); + rh = new Var<>(RequiredHours, new Pair<>(10, 60)); } @Override @@ -28,8 +33,13 @@ public void typedText(int i, String typedText) { break; case 1: Integer value2 = Integer.valueOf(typedText); - rmh.set(value2); - logger.fine("Set Required_Minimum_Hours to " + value2); + rh.set(new Pair<>(value2, rh.get().getSecond())); + logger.fine("Set minimum @ Required_Hours to " + value2); + break; + case 2: + Integer value3 = Integer.valueOf(typedText); + rh.set(new Pair<>(rh.get().getSecond(), value3)); + logger.fine("Set maximum @ Required_Hours to " + value3); break; default: break; @@ -41,5 +51,4 @@ public static void main(String[] args) { Boss b = new Boss(); b.start(); } - } diff --git a/Dream2/src/examples/java/dream/examples/form/CompleteGlitchFreeFormServer.java b/Dream2/src/examples/java/dream/examples/form/CompleteGlitchFreeFormServer.java index 9fb1dd3..9ab48f6 100644 --- a/Dream2/src/examples/java/dream/examples/form/CompleteGlitchFreeFormServer.java +++ b/Dream2/src/examples/java/dream/examples/form/CompleteGlitchFreeFormServer.java @@ -1,95 +1,15 @@ package dream.examples.form; -import java.util.LinkedList; - -import dream.client.Signal; -import dream.examples.util.Pair; - public class CompleteGlitchFreeFormServer extends SingleGlitchFreeFormServer { @Override protected void createDependencies() { logger.fine("Building Dependencies"); - final Value lock = new Value<>(false); - - final Value uc_working_hours_value = new Value<>(); - final Signal uc_working_hours = new Signal<>("uc_working_hours", () -> { - if (!lock.get()) { - lock.set(true); - uc_working_hours_value.set(working_hours.get()); - } else { - // TODO after releasing the lock update with this value (queue?) - } - return uc_working_hours_value.get(); - }, working_hours); - - final Value uc_euro_per_hour_value = new Value<>(); - final Signal uc_euro_per_hour = new Signal<>("uc_euro_per_hour", () -> { - if (!lock.get()) { - lock.set(true); - uc_euro_per_hour_value.set(euro_per_hour.get()); - } else { - // TODO after releasing the lock update with this value (queue?) - } - return uc_euro_per_hour_value.get(); - }, euro_per_hour); - - final Value uc_req_minimum_hours_value = new Value<>(); - final Signal uc_req_minimum_hours = new Signal<>("uc_required_minimum_hours", () -> { - if (!lock.get()) { - lock.set(true); - uc_req_minimum_hours_value.set(required_minimum_hours.get()); - } else { - // TODO after releasing the lock update with this value (queue?) - } - return uc_req_minimum_hours_value.get(); - }, required_minimum_hours); - - final UpdateCounter minimumCounter = new UpdateCounter(); - final UpdateCounter maximumCounter = new UpdateCounter(); - - final Signal> minimumHours = new Signal<>("minimumHours", () -> { - return new Pair<>(working_hours.get() > required_minimum_hours.get(), minimumCounter.incAndGet()); - }, working_hours, required_minimum_hours); - - final Signal> maximumHours = new Signal<>("maximumHours", () -> { - return new Pair<>(working_hours.get() < 60, maximumCounter.incAndGet()); - }, working_hours); - - final Signal minimumEuroPerHour = new Signal<>("minimumEuroPerHour", () -> { - return euro_per_hour.get() > 10; - }, euro_per_hour); - - final LinkedList> minimumQueue = new LinkedList<>(); - final LinkedList> maximumQueue = new LinkedList<>(); - final Value currentValue = new Value<>(false); - - new Signal<>("settingsOkay", () -> { - if (minimumHours.get() != null - && (minimumQueue.isEmpty() || minimumQueue.getLast().getSecond() < minimumHours.get().getSecond())) - minimumQueue.add(minimumHours.get()); - if (maximumHours.get() != null - && (maximumQueue.isEmpty() || maximumQueue.getLast().getSecond() < maximumHours.get().getSecond())) - maximumQueue.add(maximumHours.get()); - - if (minimumQueue.size() > 0 && maximumQueue.size() > 0 && minimumEuroPerHour.get() != null) - currentValue.set( - minimumQueue.pop().getFirst() && maximumQueue.pop().getFirst() && minimumEuroPerHour.get()); - return currentValue.get(); - }, minimumHours, maximumHours, minimumEuroPerHour); - - new Signal<>("salary", () -> { - if (working_hours.get() != null && euro_per_hour.get() != null) - return working_hours.get() * euro_per_hour.get(); - else - return 0.0; - }, working_hours, euro_per_hour); - logger.fine("Finished building Dependencies"); } public static void main(String[] args) { - new SingleGlitchFreeFormServer(); + new CompleteGlitchFreeFormServer(); } } diff --git a/Dream2/src/examples/java/dream/examples/form/FormServer.java b/Dream2/src/examples/java/dream/examples/form/FormServer.java index 7c315e2..4d1bb09 100644 --- a/Dream2/src/examples/java/dream/examples/form/FormServer.java +++ b/Dream2/src/examples/java/dream/examples/form/FormServer.java @@ -6,17 +6,23 @@ import dream.client.RemoteVar; import dream.client.Signal; import dream.examples.util.Client; +import dream.examples.util.Pair; public class FormServer extends Client { public static final String NAME = "FormServer"; + public static final String MinimumHours = "minimumHours"; + public static final String MaximumHours = "maximumHours"; + public static final String MinimumEuroPerHour = "minimumEuroPerHour"; + public static final String SettingsOkay = "settingsOkay"; + public static final String Salary = "salary"; protected RemoteVar working_hours; protected RemoteVar euro_per_hour; - protected RemoteVar required_minimum_hours; + protected RemoteVar> required_hours; public FormServer() { - super("FormServer"); + super(NAME); detectNewSession(); } @@ -24,18 +30,18 @@ public FormServer() { * Look for new clients every 5 seconds */ private void detectNewSession() { - while (euro_per_hour == null || working_hours == null || required_minimum_hours == null) { + while (euro_per_hour == null || working_hours == null || required_hours == null) { for (String str : DreamClient.instance.listVariables()) { String host = str.split("@")[1]; String var = str.split("@")[0]; - if (working_hours == null && var.equalsIgnoreCase("working_hours")) { + if (working_hours == null && var.equalsIgnoreCase(Secretary.WorkingHours)) { working_hours = new RemoteVar<>(host, var); logger.fine("Found Secretary"); - } else if (euro_per_hour == null && var.equalsIgnoreCase("euro_per_hour")) { + } else if (euro_per_hour == null && var.equalsIgnoreCase(Boss.EuroPerHour)) { euro_per_hour = new RemoteVar<>(host, var); logger.fine("Found Boss"); - } else if (required_minimum_hours == null && var.equalsIgnoreCase("required_minimum_hours")) { - required_minimum_hours = new RemoteVar<>(host, "required_minimum_hours"); + } else if (required_hours == null && var.equalsIgnoreCase(Boss.RequiredHours)) { + required_hours = new RemoteVar<>(host, Boss.RequiredHours); logger.fine("Found Boss"); } } @@ -51,35 +57,35 @@ private void detectNewSession() { protected void createDependencies() { logger.fine("Building Dependencies"); - final Signal minimumHours = new Signal<>("minimumHours", () -> { - if (working_hours.get() != null && required_minimum_hours.get() != null) - return working_hours.get() > required_minimum_hours.get(); + final Signal minimumHours = new Signal<>(MinimumHours, () -> { + if (working_hours.get() != null && required_hours.get() != null) + return working_hours.get() > required_hours.get().getFirst(); else return false; - }, working_hours, required_minimum_hours); + }, working_hours, required_hours); - final Signal maximumHours = new Signal<>("maximumHours", () -> { - if (working_hours.get() != null) - return working_hours.get() < 60; + final Signal maximumHours = new Signal<>(MaximumHours, () -> { + if (working_hours.get() != null && required_hours.get() != null) + return working_hours.get() < required_hours.get().getSecond(); else return false; - }, working_hours); + }, working_hours, required_hours); - final Signal minimumEuroPerHour = new Signal<>("minimumEuroPerHour", () -> { + final Signal minimumEuroPerHour = new Signal<>(MinimumEuroPerHour, () -> { if (euro_per_hour.get() != null) return euro_per_hour.get() > 10; else return false; }, euro_per_hour); - new Signal<>("settingsOkay", () -> { + new Signal<>(SettingsOkay, () -> { if (minimumHours.get() != null && maximumHours.get() != null && minimumEuroPerHour.get() != null) return minimumHours.get() && maximumHours.get() && minimumEuroPerHour.get(); else return false; }, minimumHours, maximumHours, minimumEuroPerHour); - new Signal<>("salary", () -> { + new Signal<>(Salary, () -> { if (working_hours.get() != null && euro_per_hour.get() != null) return working_hours.get() * euro_per_hour.get(); else diff --git a/Dream2/src/examples/java/dream/examples/form/Secretary.java b/Dream2/src/examples/java/dream/examples/form/Secretary.java index 789bf3b..ca607be 100644 --- a/Dream2/src/examples/java/dream/examples/form/Secretary.java +++ b/Dream2/src/examples/java/dream/examples/form/Secretary.java @@ -4,16 +4,19 @@ public class Secretary extends FormClient { + public static final String NAME = "Secretary"; + public static final String WorkingHours = "working_hours"; + private Var wh; public Secretary() { - super("Secretary", "Working Hours"); + super(NAME, "Working Hours"); setInitValues(Integer.toString(5)); } @Override protected void init() { - wh = new Var<>("working_hours", 5); + wh = new Var<>(WorkingHours, 5); } @Override diff --git a/Dream2/src/examples/java/dream/examples/form/SingleGlitchFreeFormServer.java b/Dream2/src/examples/java/dream/examples/form/SingleGlitchFreeFormServer.java index 33320a0..438672d 100644 --- a/Dream2/src/examples/java/dream/examples/form/SingleGlitchFreeFormServer.java +++ b/Dream2/src/examples/java/dream/examples/form/SingleGlitchFreeFormServer.java @@ -14,15 +14,15 @@ protected void createDependencies() { final UpdateCounter minimumCounter = new UpdateCounter(); final UpdateCounter maximumCounter = new UpdateCounter(); - final Signal> minimumHours = new Signal<>("minimumHours", () -> { - return new Pair<>(working_hours.get() > required_minimum_hours.get(), minimumCounter.incAndGet()); - }, working_hours, required_minimum_hours); + final Signal> minimumHours = new Signal<>(MinimumHours, () -> { + return new Pair<>(working_hours.get() > 10, minimumCounter.incAndGet()); + }, working_hours); - final Signal> maximumHours = new Signal<>("maximumHours", () -> { + final Signal> maximumHours = new Signal<>(MaximumHours, () -> { return new Pair<>(working_hours.get() < 60, maximumCounter.incAndGet()); }, working_hours); - final Signal minimumEuroPerHour = new Signal<>("minimumEuroPerHour", () -> { + final Signal minimumEuroPerHour = new Signal<>(MinimumEuroPerHour, () -> { return euro_per_hour.get() > 10; }, euro_per_hour); @@ -30,7 +30,7 @@ protected void createDependencies() { final LinkedList> maximumQueue = new LinkedList<>(); final Value currentValue = new Value<>(false); - new Signal<>("settingsOkay", () -> { + new Signal<>(SettingsOkay, () -> { if (minimumHours.get() != null && (minimumQueue.isEmpty() || minimumQueue.getLast().getSecond() < minimumHours.get().getSecond())) minimumQueue.add(minimumHours.get()); @@ -44,7 +44,7 @@ protected void createDependencies() { return currentValue.get(); }, minimumHours, maximumHours, minimumEuroPerHour); - new Signal<>("salary", () -> { + new Signal<>(Salary, () -> { if (working_hours.get() != null && euro_per_hour.get() != null) return working_hours.get() * euro_per_hour.get(); else diff --git a/Dream2/src/examples/java/dream/examples/form/complete_glitchfree_graph.png b/Dream2/src/examples/java/dream/examples/form/complete_glitchfree_graph.png new file mode 100644 index 0000000000000000000000000000000000000000..2381fa5c3ebceb9e4ac3c1b2067ad1b14f1a257c GIT binary patch literal 77390 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

NwR`OVqfPrMBH{X32vnYE)jd~D;&O{`HoLn2av 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 O2s~Z=T-G@yGywqE%0wgp literal 0 HcmV?d00001 diff --git a/Dream2/src/examples/java/dream/examples/form/graph.png b/Dream2/src/examples/java/dream/examples/form/graph.png index 195cccc0ef57c9e20019a7689b24ce808913b7f9..c2275dc7f8d3d8c95123aba83607b1f0dbd3e423 100644 GIT binary patch literal 67919 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 literal 72093 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 Date: Fri, 3 Jun 2016 11:39:43 +0200 Subject: [PATCH 102/161] adding info to exceptions during signal updates --- Dream2/src/main/java/dream/client/Signal.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Dream2/src/main/java/dream/client/Signal.java b/Dream2/src/main/java/dream/client/Signal.java index 4862298..75e1c38 100755 --- a/Dream2/src/main/java/dream/client/Signal.java +++ b/Dream2/src/main/java/dream/client/Signal.java @@ -10,6 +10,7 @@ import java.util.Queue; import java.util.Set; import java.util.function.Supplier; +import java.util.logging.Level; import java.util.logging.Logger; import java.util.stream.Collectors; @@ -87,8 +88,9 @@ private final void processUpdate(EventProducerPair update) { val = evaluate(); logger.finest("New value computed for the reactive object: " + val); } catch (final Exception e) { - logger.info( - "Exception during the evaluation of the expression. Acknowledging the producers, releasing the locks, and returning."); + logger.log(Level.INFO, + "Exception during the evaluation of the expression. Acknowledging the producers, releasing the locks, and returning.", + e); pairs.forEach(pair -> pair.getUpdateProducer().notifyUpdateFinished()); // Release locks, if needed if ((Consts.consistencyType == ConsistencyType.COMPLETE_GLITCH_FREE || // From 1bf47e1b40c9f0fcab51142e5929e5cbb3d51a3a Mon Sep 17 00:00:00 2001 From: Tobias Becker Date: Fri, 3 Jun 2016 11:40:00 +0200 Subject: [PATCH 103/161] removed todo --- .../src/examples/java/dream/examples/taskBoard/TaskMonitor.java | 1 - 1 file changed, 1 deletion(-) diff --git a/Dream2/src/examples/java/dream/examples/taskBoard/TaskMonitor.java b/Dream2/src/examples/java/dream/examples/taskBoard/TaskMonitor.java index c531c44..e2ac57f 100644 --- a/Dream2/src/examples/java/dream/examples/taskBoard/TaskMonitor.java +++ b/Dream2/src/examples/java/dream/examples/taskBoard/TaskMonitor.java @@ -47,7 +47,6 @@ public TaskMonitor() { return tasks.get(); }, tasks); - // TODO show in monitor sigDevs.change().addHandler((oldVa, newVal) -> { System.out.println("newVal devs:" + newVal); gui.setDevs(newVal); From 96ef8821313e775886866db2779216bbe9a3b1ac Mon Sep 17 00:00:00 2001 From: Tobias Becker Date: Fri, 3 Jun 2016 11:45:12 +0200 Subject: [PATCH 104/161] moved single and complete glitchfreedom to seperate packages --- .../form/{ => complete_glitchfree}/Boss.java | 3 +- .../CompleteGlitchFreeFormServer.java | 6 ++- .../complete_glitchfree_graph.png | Bin .../java/dream/examples/form/core/Boss.java | 40 ++++++++++++++++++ .../examples/form/{ => core}/FormClient.java | 2 +- .../examples/form/{ => core}/FormGUI.java | 2 +- .../examples/form/{ => core}/FormServer.java | 21 ++++----- .../GlitchFreeFormServer.java} | 6 +-- .../examples/form/{ => core}/Secretary.java | 2 +- .../dream/examples/form/{ => core}/graph.png | Bin 10 files changed, 60 insertions(+), 22 deletions(-) rename Dream2/src/examples/java/dream/examples/form/{ => complete_glitchfree}/Boss.java (93%) rename Dream2/src/examples/java/dream/examples/form/{ => complete_glitchfree}/CompleteGlitchFreeFormServer.java (56%) rename Dream2/src/examples/java/dream/examples/form/{ => complete_glitchfree}/complete_glitchfree_graph.png (100%) create mode 100644 Dream2/src/examples/java/dream/examples/form/core/Boss.java rename Dream2/src/examples/java/dream/examples/form/{ => core}/FormClient.java (97%) rename Dream2/src/examples/java/dream/examples/form/{ => core}/FormGUI.java (98%) rename Dream2/src/examples/java/dream/examples/form/{ => core}/FormServer.java (77%) rename Dream2/src/examples/java/dream/examples/form/{SingleGlitchFreeFormServer.java => core/GlitchFreeFormServer.java} (95%) rename Dream2/src/examples/java/dream/examples/form/{ => core}/Secretary.java (95%) rename Dream2/src/examples/java/dream/examples/form/{ => core}/graph.png (100%) diff --git a/Dream2/src/examples/java/dream/examples/form/Boss.java b/Dream2/src/examples/java/dream/examples/form/complete_glitchfree/Boss.java similarity index 93% rename from Dream2/src/examples/java/dream/examples/form/Boss.java rename to Dream2/src/examples/java/dream/examples/form/complete_glitchfree/Boss.java index 977fb38..9a2b616 100644 --- a/Dream2/src/examples/java/dream/examples/form/Boss.java +++ b/Dream2/src/examples/java/dream/examples/form/complete_glitchfree/Boss.java @@ -1,6 +1,7 @@ -package dream.examples.form; +package dream.examples.form.complete_glitchfree; import dream.client.Var; +import dream.examples.form.core.FormClient; import dream.examples.util.Pair; public class Boss extends FormClient { diff --git a/Dream2/src/examples/java/dream/examples/form/CompleteGlitchFreeFormServer.java b/Dream2/src/examples/java/dream/examples/form/complete_glitchfree/CompleteGlitchFreeFormServer.java similarity index 56% rename from Dream2/src/examples/java/dream/examples/form/CompleteGlitchFreeFormServer.java rename to Dream2/src/examples/java/dream/examples/form/complete_glitchfree/CompleteGlitchFreeFormServer.java index 9ab48f6..dfc57a3 100644 --- a/Dream2/src/examples/java/dream/examples/form/CompleteGlitchFreeFormServer.java +++ b/Dream2/src/examples/java/dream/examples/form/complete_glitchfree/CompleteGlitchFreeFormServer.java @@ -1,6 +1,8 @@ -package dream.examples.form; +package dream.examples.form.complete_glitchfree; -public class CompleteGlitchFreeFormServer extends SingleGlitchFreeFormServer { +import dream.examples.form.core.GlitchFreeFormServer; + +public class CompleteGlitchFreeFormServer extends GlitchFreeFormServer { @Override protected void createDependencies() { diff --git a/Dream2/src/examples/java/dream/examples/form/complete_glitchfree_graph.png b/Dream2/src/examples/java/dream/examples/form/complete_glitchfree/complete_glitchfree_graph.png similarity index 100% rename from Dream2/src/examples/java/dream/examples/form/complete_glitchfree_graph.png rename to Dream2/src/examples/java/dream/examples/form/complete_glitchfree/complete_glitchfree_graph.png diff --git a/Dream2/src/examples/java/dream/examples/form/core/Boss.java b/Dream2/src/examples/java/dream/examples/form/core/Boss.java new file mode 100644 index 0000000..2a3f6bf --- /dev/null +++ b/Dream2/src/examples/java/dream/examples/form/core/Boss.java @@ -0,0 +1,40 @@ +package dream.examples.form.core; + +import dream.client.Var; + +public class Boss extends FormClient { + + public static final String NAME = "Boss"; + public static final String EuroPerHour = "euro_per_hour"; + + protected Var eph; + + public Boss() { + super(NAME, "Euro/Hour"); + setInitValues(Double.toString(8.5)); + } + + @Override + protected void init() { + eph = new Var<>(EuroPerHour, 8.5); + } + + @Override + public void typedText(int i, String typedText) { + switch (i) { + case 0: + Double value = Double.valueOf(typedText); + eph.set(value); + logger.fine("Set Euro_Per_Hour to " + value); + break; + default: + break; + } + + } + + public static void main(String[] args) { + Boss b = new Boss(); + b.start(); + } +} diff --git a/Dream2/src/examples/java/dream/examples/form/FormClient.java b/Dream2/src/examples/java/dream/examples/form/core/FormClient.java similarity index 97% rename from Dream2/src/examples/java/dream/examples/form/FormClient.java rename to Dream2/src/examples/java/dream/examples/form/core/FormClient.java index 84ab01d..786f5f1 100644 --- a/Dream2/src/examples/java/dream/examples/form/FormClient.java +++ b/Dream2/src/examples/java/dream/examples/form/core/FormClient.java @@ -1,4 +1,4 @@ -package dream.examples.form; +package dream.examples.form.core; import java.awt.Color; import java.util.Arrays; diff --git a/Dream2/src/examples/java/dream/examples/form/FormGUI.java b/Dream2/src/examples/java/dream/examples/form/core/FormGUI.java similarity index 98% rename from Dream2/src/examples/java/dream/examples/form/FormGUI.java rename to Dream2/src/examples/java/dream/examples/form/core/FormGUI.java index e5bb511..3e91cc7 100644 --- a/Dream2/src/examples/java/dream/examples/form/FormGUI.java +++ b/Dream2/src/examples/java/dream/examples/form/core/FormGUI.java @@ -1,4 +1,4 @@ -package dream.examples.form; +package dream.examples.form.core; import java.awt.BorderLayout; import java.awt.Color; diff --git a/Dream2/src/examples/java/dream/examples/form/FormServer.java b/Dream2/src/examples/java/dream/examples/form/core/FormServer.java similarity index 77% rename from Dream2/src/examples/java/dream/examples/form/FormServer.java rename to Dream2/src/examples/java/dream/examples/form/core/FormServer.java index 4d1bb09..da84853 100644 --- a/Dream2/src/examples/java/dream/examples/form/FormServer.java +++ b/Dream2/src/examples/java/dream/examples/form/core/FormServer.java @@ -1,4 +1,4 @@ -package dream.examples.form; +package dream.examples.form.core; import java.util.logging.Level; @@ -6,7 +6,6 @@ import dream.client.RemoteVar; import dream.client.Signal; import dream.examples.util.Client; -import dream.examples.util.Pair; public class FormServer extends Client { @@ -19,7 +18,6 @@ public class FormServer extends Client { protected RemoteVar working_hours; protected RemoteVar euro_per_hour; - protected RemoteVar> required_hours; public FormServer() { super(NAME); @@ -30,7 +28,7 @@ public FormServer() { * Look for new clients every 5 seconds */ private void detectNewSession() { - while (euro_per_hour == null || working_hours == null || required_hours == null) { + while (euro_per_hour == null || working_hours == null) { for (String str : DreamClient.instance.listVariables()) { String host = str.split("@")[1]; String var = str.split("@")[0]; @@ -40,9 +38,6 @@ private void detectNewSession() { } else if (euro_per_hour == null && var.equalsIgnoreCase(Boss.EuroPerHour)) { euro_per_hour = new RemoteVar<>(host, var); logger.fine("Found Boss"); - } else if (required_hours == null && var.equalsIgnoreCase(Boss.RequiredHours)) { - required_hours = new RemoteVar<>(host, Boss.RequiredHours); - logger.fine("Found Boss"); } } try { @@ -58,18 +53,18 @@ protected void createDependencies() { logger.fine("Building Dependencies"); final Signal minimumHours = new Signal<>(MinimumHours, () -> { - if (working_hours.get() != null && required_hours.get() != null) - return working_hours.get() > required_hours.get().getFirst(); + if (working_hours.get() != null) + return working_hours.get() > 10; else return false; - }, working_hours, required_hours); + }, working_hours); final Signal maximumHours = new Signal<>(MaximumHours, () -> { - if (working_hours.get() != null && required_hours.get() != null) - return working_hours.get() < required_hours.get().getSecond(); + if (working_hours.get() != null) + return working_hours.get() < 60; else return false; - }, working_hours, required_hours); + }, working_hours); final Signal minimumEuroPerHour = new Signal<>(MinimumEuroPerHour, () -> { if (euro_per_hour.get() != null) diff --git a/Dream2/src/examples/java/dream/examples/form/SingleGlitchFreeFormServer.java b/Dream2/src/examples/java/dream/examples/form/core/GlitchFreeFormServer.java similarity index 95% rename from Dream2/src/examples/java/dream/examples/form/SingleGlitchFreeFormServer.java rename to Dream2/src/examples/java/dream/examples/form/core/GlitchFreeFormServer.java index 438672d..ec957d2 100644 --- a/Dream2/src/examples/java/dream/examples/form/SingleGlitchFreeFormServer.java +++ b/Dream2/src/examples/java/dream/examples/form/core/GlitchFreeFormServer.java @@ -1,11 +1,11 @@ -package dream.examples.form; +package dream.examples.form.core; import java.util.LinkedList; import dream.client.Signal; import dream.examples.util.Pair; -public class SingleGlitchFreeFormServer extends FormServer { +public class GlitchFreeFormServer extends FormServer { @Override protected void createDependencies() { @@ -55,7 +55,7 @@ protected void createDependencies() { } public static void main(String[] args) { - new SingleGlitchFreeFormServer(); + new GlitchFreeFormServer(); } } diff --git a/Dream2/src/examples/java/dream/examples/form/Secretary.java b/Dream2/src/examples/java/dream/examples/form/core/Secretary.java similarity index 95% rename from Dream2/src/examples/java/dream/examples/form/Secretary.java rename to Dream2/src/examples/java/dream/examples/form/core/Secretary.java index ca607be..c97255b 100644 --- a/Dream2/src/examples/java/dream/examples/form/Secretary.java +++ b/Dream2/src/examples/java/dream/examples/form/core/Secretary.java @@ -1,4 +1,4 @@ -package dream.examples.form; +package dream.examples.form.core; import dream.client.Var; diff --git a/Dream2/src/examples/java/dream/examples/form/graph.png b/Dream2/src/examples/java/dream/examples/form/core/graph.png similarity index 100% rename from Dream2/src/examples/java/dream/examples/form/graph.png rename to Dream2/src/examples/java/dream/examples/form/core/graph.png From ee419545ca7c2eacccf5dbc7ce4b69ba955fc541 Mon Sep 17 00:00:00 2001 From: Tobias Becker Date: Fri, 3 Jun 2016 12:02:52 +0200 Subject: [PATCH 105/161] added a description for the form example --- .../CompleteGlitchFreeFormServer.java | 4 +- .../form/complete_glitchfree/FormServer.java | 101 ++++++++++++++++++ .../form/complete_glitchfree/Secretary.java | 10 ++ .../dream/examples/form/package-info.java | 20 ++++ .../examples/form/{core => simple}/Boss.java | 3 +- .../form/{core => simple}/FormServer.java | 2 +- .../GlitchFreeFormServer.java | 2 +- .../form/{core => simple}/Secretary.java | 3 +- .../examples/form/{core => simple}/graph.png | Bin 9 files changed, 138 insertions(+), 7 deletions(-) create mode 100644 Dream2/src/examples/java/dream/examples/form/complete_glitchfree/FormServer.java create mode 100644 Dream2/src/examples/java/dream/examples/form/complete_glitchfree/Secretary.java create mode 100644 Dream2/src/examples/java/dream/examples/form/package-info.java rename Dream2/src/examples/java/dream/examples/form/{core => simple}/Boss.java (89%) rename Dream2/src/examples/java/dream/examples/form/{core => simple}/FormServer.java (98%) rename Dream2/src/examples/java/dream/examples/form/{core => simple}/GlitchFreeFormServer.java (98%) rename Dream2/src/examples/java/dream/examples/form/{core => simple}/Secretary.java (89%) rename Dream2/src/examples/java/dream/examples/form/{core => simple}/graph.png (100%) diff --git a/Dream2/src/examples/java/dream/examples/form/complete_glitchfree/CompleteGlitchFreeFormServer.java b/Dream2/src/examples/java/dream/examples/form/complete_glitchfree/CompleteGlitchFreeFormServer.java index dfc57a3..1d08521 100644 --- a/Dream2/src/examples/java/dream/examples/form/complete_glitchfree/CompleteGlitchFreeFormServer.java +++ b/Dream2/src/examples/java/dream/examples/form/complete_glitchfree/CompleteGlitchFreeFormServer.java @@ -1,8 +1,6 @@ package dream.examples.form.complete_glitchfree; -import dream.examples.form.core.GlitchFreeFormServer; - -public class CompleteGlitchFreeFormServer extends GlitchFreeFormServer { +public class CompleteGlitchFreeFormServer extends FormServer { @Override protected void createDependencies() { diff --git a/Dream2/src/examples/java/dream/examples/form/complete_glitchfree/FormServer.java b/Dream2/src/examples/java/dream/examples/form/complete_glitchfree/FormServer.java new file mode 100644 index 0000000..f1e3c6f --- /dev/null +++ b/Dream2/src/examples/java/dream/examples/form/complete_glitchfree/FormServer.java @@ -0,0 +1,101 @@ +package dream.examples.form.complete_glitchfree; + +import java.util.logging.Level; + +import dream.client.DreamClient; +import dream.client.RemoteVar; +import dream.client.Signal; +import dream.examples.util.Client; +import dream.examples.util.Pair; + +public class FormServer extends Client { + + public static final String NAME = "FormServer"; + public static final String MinimumHours = "minimumHours"; + public static final String MaximumHours = "maximumHours"; + public static final String MinimumEuroPerHour = "minimumEuroPerHour"; + public static final String SettingsOkay = "settingsOkay"; + public static final String Salary = "salary"; + + protected RemoteVar working_hours; + protected RemoteVar euro_per_hour; + protected RemoteVar> required_hours; + + public FormServer() { + super(NAME); + detectNewSession(); + } + + /** + * Look for new clients every 5 seconds + */ + private void detectNewSession() { + while (euro_per_hour == null || working_hours == null || required_hours == null) { + for (String str : DreamClient.instance.listVariables()) { + String host = str.split("@")[1]; + String var = str.split("@")[0]; + if (working_hours == null && var.equalsIgnoreCase(Secretary.WorkingHours)) { + working_hours = new RemoteVar<>(host, var); + logger.fine("Found Secretary"); + } else if (euro_per_hour == null && var.equalsIgnoreCase(Boss.EuroPerHour)) { + euro_per_hour = new RemoteVar<>(host, var); + logger.fine("Found Boss"); + } else if (required_hours == null && var.equalsIgnoreCase(Boss.RequiredHours)) { + required_hours = new RemoteVar<>(host, var); + logger.fine("Found Boss"); + } + } + try { + Thread.sleep(500); + } catch (InterruptedException e) { + logger.log(Level.SEVERE, "Failed to sleep for 0.5 seconds", e); + } + } + createDependencies(); + } + + protected void createDependencies() { + logger.fine("Building Dependencies"); + + final Signal minimumHours = new Signal<>(MinimumHours, () -> { + if (working_hours.get() != null && required_hours.get() != null) + return working_hours.get() > required_hours.get().getFirst(); + else + return false; + }, working_hours, required_hours); + + final Signal maximumHours = new Signal<>(MaximumHours, () -> { + if (working_hours.get() != null && required_hours.get() != null) + return working_hours.get() < required_hours.get().getSecond(); + else + return false; + }, working_hours, required_hours); + + final Signal minimumEuroPerHour = new Signal<>(MinimumEuroPerHour, () -> { + if (euro_per_hour.get() != null) + return euro_per_hour.get() > 10; + else + return false; + }, euro_per_hour); + + new Signal<>(SettingsOkay, () -> { + if (minimumHours.get() != null && maximumHours.get() != null && minimumEuroPerHour.get() != null) + return minimumHours.get() && maximumHours.get() && minimumEuroPerHour.get(); + else + return false; + }, minimumHours, maximumHours, minimumEuroPerHour); + + new Signal<>(Salary, () -> { + if (working_hours.get() != null && euro_per_hour.get() != null) + return working_hours.get() * euro_per_hour.get(); + else + return 0.0; + }, working_hours, euro_per_hour); + + logger.fine("Finished building Dependencies"); + } + + public static void main(String[] args) { + new FormServer(); + } +} diff --git a/Dream2/src/examples/java/dream/examples/form/complete_glitchfree/Secretary.java b/Dream2/src/examples/java/dream/examples/form/complete_glitchfree/Secretary.java new file mode 100644 index 0000000..d007cb4 --- /dev/null +++ b/Dream2/src/examples/java/dream/examples/form/complete_glitchfree/Secretary.java @@ -0,0 +1,10 @@ +package dream.examples.form.complete_glitchfree; + +public class Secretary extends dream.examples.form.simple.Secretary { + + public static void main(String[] args) { + Secretary s = new Secretary(); + s.start(); + } + +} diff --git a/Dream2/src/examples/java/dream/examples/form/package-info.java b/Dream2/src/examples/java/dream/examples/form/package-info.java new file mode 100644 index 0000000..1382e45 --- /dev/null +++ b/Dream2/src/examples/java/dream/examples/form/package-info.java @@ -0,0 +1,20 @@ +/** + * This packages contains two examples:
+ * a) {@link dream.examples.form.simple simple}: uses a simple graph with a + * probable glitch. There are to different solutions to this glitch:
+ * {@link dream.examples.form.simple.FormServer FormServer} depends on Dream's + * single_glitch_free consistency to solve the glitch and
+ * {@link dream.examples.form.simple.GlitchFreeFormServer GlitchFreeFormServer}, + * which ensures itself that no glitch can occur.
+ *
+ * b) {@link dream.examples.form.complete_glitchfree complete}: uses a different + * graph, which requires a locking mechanism. + * + *
+ *
+ * Both examples contain an image of their used dependency graph + * + * @author Tobias Becker + */ +// TODO finish description +package dream.examples.form; \ No newline at end of file diff --git a/Dream2/src/examples/java/dream/examples/form/core/Boss.java b/Dream2/src/examples/java/dream/examples/form/simple/Boss.java similarity index 89% rename from Dream2/src/examples/java/dream/examples/form/core/Boss.java rename to Dream2/src/examples/java/dream/examples/form/simple/Boss.java index 2a3f6bf..c21fd78 100644 --- a/Dream2/src/examples/java/dream/examples/form/core/Boss.java +++ b/Dream2/src/examples/java/dream/examples/form/simple/Boss.java @@ -1,6 +1,7 @@ -package dream.examples.form.core; +package dream.examples.form.simple; import dream.client.Var; +import dream.examples.form.core.FormClient; public class Boss extends FormClient { diff --git a/Dream2/src/examples/java/dream/examples/form/core/FormServer.java b/Dream2/src/examples/java/dream/examples/form/simple/FormServer.java similarity index 98% rename from Dream2/src/examples/java/dream/examples/form/core/FormServer.java rename to Dream2/src/examples/java/dream/examples/form/simple/FormServer.java index da84853..70a3cc0 100644 --- a/Dream2/src/examples/java/dream/examples/form/core/FormServer.java +++ b/Dream2/src/examples/java/dream/examples/form/simple/FormServer.java @@ -1,4 +1,4 @@ -package dream.examples.form.core; +package dream.examples.form.simple; import java.util.logging.Level; diff --git a/Dream2/src/examples/java/dream/examples/form/core/GlitchFreeFormServer.java b/Dream2/src/examples/java/dream/examples/form/simple/GlitchFreeFormServer.java similarity index 98% rename from Dream2/src/examples/java/dream/examples/form/core/GlitchFreeFormServer.java rename to Dream2/src/examples/java/dream/examples/form/simple/GlitchFreeFormServer.java index ec957d2..4622572 100644 --- a/Dream2/src/examples/java/dream/examples/form/core/GlitchFreeFormServer.java +++ b/Dream2/src/examples/java/dream/examples/form/simple/GlitchFreeFormServer.java @@ -1,4 +1,4 @@ -package dream.examples.form.core; +package dream.examples.form.simple; import java.util.LinkedList; diff --git a/Dream2/src/examples/java/dream/examples/form/core/Secretary.java b/Dream2/src/examples/java/dream/examples/form/simple/Secretary.java similarity index 89% rename from Dream2/src/examples/java/dream/examples/form/core/Secretary.java rename to Dream2/src/examples/java/dream/examples/form/simple/Secretary.java index c97255b..b10e312 100644 --- a/Dream2/src/examples/java/dream/examples/form/core/Secretary.java +++ b/Dream2/src/examples/java/dream/examples/form/simple/Secretary.java @@ -1,6 +1,7 @@ -package dream.examples.form.core; +package dream.examples.form.simple; import dream.client.Var; +import dream.examples.form.core.FormClient; public class Secretary extends FormClient { diff --git a/Dream2/src/examples/java/dream/examples/form/core/graph.png b/Dream2/src/examples/java/dream/examples/form/simple/graph.png similarity index 100% rename from Dream2/src/examples/java/dream/examples/form/core/graph.png rename to Dream2/src/examples/java/dream/examples/form/simple/graph.png From e475ed2ac4ff7211a9225d4997f16b20c1538ef8 Mon Sep 17 00:00:00 2001 From: Tobias Becker Date: Fri, 3 Jun 2016 12:14:51 +0200 Subject: [PATCH 106/161] added superclass for formserver --- .../form/complete_glitchfree/FormServer.java | 11 +------- .../dream/examples/form/core/FormClient.java | 7 +++--- .../dream/examples/form/core/FormServer.java | 25 +++++++++++++++++++ .../examples/form/simple/FormServer.java | 11 +------- .../java/dream/examples/util/Client.java | 4 +++ 5 files changed, 35 insertions(+), 23 deletions(-) create mode 100644 Dream2/src/examples/java/dream/examples/form/core/FormServer.java diff --git a/Dream2/src/examples/java/dream/examples/form/complete_glitchfree/FormServer.java b/Dream2/src/examples/java/dream/examples/form/complete_glitchfree/FormServer.java index f1e3c6f..b617642 100644 --- a/Dream2/src/examples/java/dream/examples/form/complete_glitchfree/FormServer.java +++ b/Dream2/src/examples/java/dream/examples/form/complete_glitchfree/FormServer.java @@ -5,24 +5,15 @@ import dream.client.DreamClient; import dream.client.RemoteVar; import dream.client.Signal; -import dream.examples.util.Client; import dream.examples.util.Pair; -public class FormServer extends Client { - - public static final String NAME = "FormServer"; - public static final String MinimumHours = "minimumHours"; - public static final String MaximumHours = "maximumHours"; - public static final String MinimumEuroPerHour = "minimumEuroPerHour"; - public static final String SettingsOkay = "settingsOkay"; - public static final String Salary = "salary"; +public class FormServer extends dream.examples.form.core.FormServer { protected RemoteVar working_hours; protected RemoteVar euro_per_hour; protected RemoteVar> required_hours; public FormServer() { - super(NAME); detectNewSession(); } diff --git a/Dream2/src/examples/java/dream/examples/form/core/FormClient.java b/Dream2/src/examples/java/dream/examples/form/core/FormClient.java index 786f5f1..dd77f4d 100644 --- a/Dream2/src/examples/java/dream/examples/form/core/FormClient.java +++ b/Dream2/src/examples/java/dream/examples/form/core/FormClient.java @@ -26,7 +26,8 @@ public FormClient(String name, String... labelText) { @Override protected List waitForVars() { - return Arrays.asList("salary@FormServer", "settingsOkay@FormServer"); + return Arrays.asList(toVar(FormServer.NAME, FormServer.Salary), + toVar(FormServer.NAME, FormServer.SettingsOkay)); } protected void start() { @@ -35,8 +36,8 @@ protected void start() { if (values != null) gui.setInitValues(values); - salary = new RemoteVar<>("FormServer", "salary"); - settings = new RemoteVar<>("FormServer", "settingsOkay"); + salary = new RemoteVar<>(FormServer.NAME, FormServer.Salary); + settings = new RemoteVar<>(FormServer.NAME, FormServer.SettingsOkay); remoteSalary = new Signal<>("remoteSalary", () -> { if (salary.get() != null) diff --git a/Dream2/src/examples/java/dream/examples/form/core/FormServer.java b/Dream2/src/examples/java/dream/examples/form/core/FormServer.java new file mode 100644 index 0000000..ea9795f --- /dev/null +++ b/Dream2/src/examples/java/dream/examples/form/core/FormServer.java @@ -0,0 +1,25 @@ +package dream.examples.form.core; + +import dream.client.RemoteVar; +import dream.examples.util.Client; + +public class FormServer extends Client { + + public static final String NAME = "FormServer"; + public static final String MinimumHours = "minimumHours"; + public static final String MaximumHours = "maximumHours"; + public static final String MinimumEuroPerHour = "minimumEuroPerHour"; + public static final String SettingsOkay = "settingsOkay"; + public static final String Salary = "salary"; + + protected RemoteVar working_hours; + protected RemoteVar euro_per_hour; + + public FormServer() { + super(NAME); + } + + public static void main(String[] args) { + new FormServer(); + } +} diff --git a/Dream2/src/examples/java/dream/examples/form/simple/FormServer.java b/Dream2/src/examples/java/dream/examples/form/simple/FormServer.java index 70a3cc0..3a56c02 100644 --- a/Dream2/src/examples/java/dream/examples/form/simple/FormServer.java +++ b/Dream2/src/examples/java/dream/examples/form/simple/FormServer.java @@ -5,22 +5,13 @@ import dream.client.DreamClient; import dream.client.RemoteVar; import dream.client.Signal; -import dream.examples.util.Client; -public class FormServer extends Client { - - public static final String NAME = "FormServer"; - public static final String MinimumHours = "minimumHours"; - public static final String MaximumHours = "maximumHours"; - public static final String MinimumEuroPerHour = "minimumEuroPerHour"; - public static final String SettingsOkay = "settingsOkay"; - public static final String Salary = "salary"; +public class FormServer extends dream.examples.form.core.FormServer { protected RemoteVar working_hours; protected RemoteVar euro_per_hour; public FormServer() { - super(NAME); detectNewSession(); } diff --git a/Dream2/src/examples/java/dream/examples/util/Client.java b/Dream2/src/examples/java/dream/examples/util/Client.java index eda07c3..fc2d50c 100644 --- a/Dream2/src/examples/java/dream/examples/util/Client.java +++ b/Dream2/src/examples/java/dream/examples/util/Client.java @@ -129,4 +129,8 @@ public String getHostName() { return Consts.hostName; } + public String toVar(String host, String var) { + return var + "@" + host; + } + } From ad1bb38b300f2e4402577153f46eb1175949334d Mon Sep 17 00:00:00 2001 From: Tobias Becker Date: Fri, 3 Jun 2016 13:49:28 +0200 Subject: [PATCH 107/161] fixed bug, extended description --- .../form/complete_glitchfree/Boss.java | 2 +- .../CompleteGlitchFreeFormServer.java | 37 +++++++++++++++++++ .../dream/examples/form/package-info.java | 17 ++++++--- 3 files changed, 49 insertions(+), 7 deletions(-) diff --git a/Dream2/src/examples/java/dream/examples/form/complete_glitchfree/Boss.java b/Dream2/src/examples/java/dream/examples/form/complete_glitchfree/Boss.java index 9a2b616..0e21d88 100644 --- a/Dream2/src/examples/java/dream/examples/form/complete_glitchfree/Boss.java +++ b/Dream2/src/examples/java/dream/examples/form/complete_glitchfree/Boss.java @@ -39,7 +39,7 @@ public void typedText(int i, String typedText) { break; case 2: Integer value3 = Integer.valueOf(typedText); - rh.set(new Pair<>(rh.get().getSecond(), value3)); + rh.set(new Pair<>(rh.get().getFirst(), value3)); logger.fine("Set maximum @ Required_Hours to " + value3); break; default: diff --git a/Dream2/src/examples/java/dream/examples/form/complete_glitchfree/CompleteGlitchFreeFormServer.java b/Dream2/src/examples/java/dream/examples/form/complete_glitchfree/CompleteGlitchFreeFormServer.java index 1d08521..df02d5b 100644 --- a/Dream2/src/examples/java/dream/examples/form/complete_glitchfree/CompleteGlitchFreeFormServer.java +++ b/Dream2/src/examples/java/dream/examples/form/complete_glitchfree/CompleteGlitchFreeFormServer.java @@ -1,10 +1,47 @@ package dream.examples.form.complete_glitchfree; +import dream.client.Signal; + public class CompleteGlitchFreeFormServer extends FormServer { @Override protected void createDependencies() { logger.fine("Building Dependencies"); + // TODO implement glitch freedom + final Signal minimumHours = new Signal<>(MinimumHours, () -> { + if (working_hours.get() != null && required_hours.get() != null) + return working_hours.get() > required_hours.get().getFirst(); + else + return false; + }, working_hours, required_hours); + + final Signal maximumHours = new Signal<>(MaximumHours, () -> { + if (working_hours.get() != null && required_hours.get() != null) + return working_hours.get() < required_hours.get().getSecond(); + else + return false; + }, working_hours, required_hours); + + final Signal minimumEuroPerHour = new Signal<>(MinimumEuroPerHour, () -> { + if (euro_per_hour.get() != null) + return euro_per_hour.get() > 10; + else + return false; + }, euro_per_hour); + + new Signal<>(SettingsOkay, () -> { + if (minimumHours.get() != null && maximumHours.get() != null && minimumEuroPerHour.get() != null) + return minimumHours.get() && maximumHours.get() && minimumEuroPerHour.get(); + else + return false; + }, minimumHours, maximumHours, minimumEuroPerHour); + + new Signal<>(Salary, () -> { + if (working_hours.get() != null && euro_per_hour.get() != null) + return working_hours.get() * euro_per_hour.get(); + else + return 0.0; + }, working_hours, euro_per_hour); logger.fine("Finished building Dependencies"); } diff --git a/Dream2/src/examples/java/dream/examples/form/package-info.java b/Dream2/src/examples/java/dream/examples/form/package-info.java index 1382e45..2ab6845 100644 --- a/Dream2/src/examples/java/dream/examples/form/package-info.java +++ b/Dream2/src/examples/java/dream/examples/form/package-info.java @@ -1,20 +1,25 @@ /** * This packages contains two examples:
* a) {@link dream.examples.form.simple simple}: uses a simple graph with a - * probable glitch. There are to different solutions to this glitch:
- * {@link dream.examples.form.simple.FormServer FormServer} depends on Dream's + * probable glitch. There are two different solutions to this glitch:
+ * {@link dream.examples.form.simple.FormServer FormServer} depending on Dream's * single_glitch_free consistency to solve the glitch and
* {@link dream.examples.form.simple.GlitchFreeFormServer GlitchFreeFormServer}, * which ensures itself that no glitch can occur.
*
- * b) {@link dream.examples.form.complete_glitchfree complete}: uses a different - * graph, which requires a locking mechanism. - * + * b) {@link dream.examples.form.complete_glitchfree complete} uses a different + * graph, which requires a locking mechanism. Again there are two different + * solutions:
+ * {@link dream.examples.form.complete_glitchfree.FormServer FormServer} + * depending on Dream's complete_glitch_free consistency to solve the + * glitch and
+ * {@link dream.examples.form.complete_glitchfree.CompleteGlitchFreeFormServer + * CompleteGlitchFreeFormServer}, which ensures itself with a own locking + * mechanism that no glitch can occur.
*
*
* Both examples contain an image of their used dependency graph * * @author Tobias Becker */ -// TODO finish description package dream.examples.form; \ No newline at end of file From 6d00ca9c257cbf20415306813475ec49a2f753ea Mon Sep 17 00:00:00 2001 From: Tobias Becker Date: Fri, 3 Jun 2016 13:50:36 +0200 Subject: [PATCH 108/161] taskBoard: refactored to constant variable names --- .../dream/examples/taskBoard/ServerNode.java | 25 +++++++++++-------- .../dream/examples/taskBoard/TaskCreater.java | 8 ++++-- .../dream/examples/taskBoard/TaskMonitor.java | 11 +++++--- .../java/dream/examples/util/Client.java | 4 +++ 4 files changed, 31 insertions(+), 17 deletions(-) diff --git a/Dream2/src/examples/java/dream/examples/taskBoard/ServerNode.java b/Dream2/src/examples/java/dream/examples/taskBoard/ServerNode.java index a677dbe..cc11bf9 100644 --- a/Dream2/src/examples/java/dream/examples/taskBoard/ServerNode.java +++ b/Dream2/src/examples/java/dream/examples/taskBoard/ServerNode.java @@ -20,9 +20,12 @@ */ public class ServerNode extends Client { public static final String NAME = "ServerNode"; - private Var> myClients = null; - private Var developers = null; - private Var tasks = null; + public static final String VAR_developers = "developers"; + public static final String VAR_tasks = "tasks"; + + private final ArrayList myClients; + private final Var developers; + private final Var tasks; public static void main(String... args) { new ServerNode(); @@ -30,17 +33,17 @@ public static void main(String... args) { public ServerNode() { super(NAME); - developers = new Var("developers", ""); - tasks = new Var("tasks", ""); - myClients = new Var>("Server_registered_clients", new ArrayList()); + developers = new Var(VAR_developers, ""); + tasks = new Var(VAR_tasks, ""); + myClients = new ArrayList(); detectClients(); } private void detectClients() { Set vars = DreamClient.instance.listVariables(); vars.stream().map(x -> new Pair(x.split("@")[1], x.split("@")[0])) - .filter(x -> !myClients.get().contains(x.getSecond() + "@" + x.getFirst()) - && (x.getSecond().equalsIgnoreCase("newTask") || x.getSecond().equalsIgnoreCase("newDev"))) + .filter(x -> !myClients.contains(toVar(x)) && (x.getSecond().equalsIgnoreCase(TaskCreater.VAR_newTask) + || x.getSecond().equalsIgnoreCase(TaskCreater.VAR_newDev))) .forEach(x -> createClient(x.getFirst(), x.getSecond())); try { Thread.sleep(500); @@ -64,15 +67,15 @@ private void createClient(String clientHost, String clientVar) { sig.change().addHandler((oldValue, newValue) -> { if (newValue != null) { // Set vars for remote querying - if (clientVar.equalsIgnoreCase("newDev")) { + if (clientVar.equalsIgnoreCase(TaskCreater.VAR_newDev)) { developers.set(developers.get().length() == 0 ? newValue : developers.get() + ":" + newValue); } - if (clientVar.equalsIgnoreCase("newTask")) { + if (clientVar.equalsIgnoreCase(TaskCreater.VAR_newTask)) { tasks.set(tasks.get().length() == 0 ? newValue : tasks.get() + ":" + newValue); } System.out.println("new value from " + clientHost + "@" + clientVar); } }); - myClients.modify((old) -> old.add(clientVar + "@" + clientHost)); + myClients.add(clientVar + "@" + clientHost); } } \ No newline at end of file diff --git a/Dream2/src/examples/java/dream/examples/taskBoard/TaskCreater.java b/Dream2/src/examples/java/dream/examples/taskBoard/TaskCreater.java index 9594767..000b1cb 100644 --- a/Dream2/src/examples/java/dream/examples/taskBoard/TaskCreater.java +++ b/Dream2/src/examples/java/dream/examples/taskBoard/TaskCreater.java @@ -26,13 +26,17 @@ * @author Tobias Becker */ public class TaskCreater extends Client { + + public static final String VAR_newDev = "newDev"; + public static final String VAR_newTask = "newTask"; + private Var taskCreater; private Var devCreater; public TaskCreater() { super("TaskCreater" + new Random().nextInt(1000)); - taskCreater = new Var<>("newTask", null); - devCreater = new Var<>("newDev", null); + taskCreater = new Var<>(VAR_newTask, null); + devCreater = new Var<>(VAR_newDev, null); new TaskCreaterGUI(this); } diff --git a/Dream2/src/examples/java/dream/examples/taskBoard/TaskMonitor.java b/Dream2/src/examples/java/dream/examples/taskBoard/TaskMonitor.java index e2ac57f..6118394 100644 --- a/Dream2/src/examples/java/dream/examples/taskBoard/TaskMonitor.java +++ b/Dream2/src/examples/java/dream/examples/taskBoard/TaskMonitor.java @@ -22,6 +22,8 @@ */ public class TaskMonitor extends Client { + public static final String NAME = "TaskMonitor"; + private final MonitorGUI gui; private final RemoteVar devs; private final RemoteVar tasks; @@ -30,19 +32,20 @@ public class TaskMonitor extends Client { @Override protected List waitForVars() { - return Arrays.asList("developers@ServerNode", "tasks@ServerNode"); + return Arrays.asList(toVar(ServerNode.NAME, ServerNode.VAR_developers), + toVar(ServerNode.NAME, ServerNode.VAR_tasks)); } public TaskMonitor() { - super("TaskMonitor"); + super(NAME); gui = new MonitorGUI(); - devs = new RemoteVar("ServerNode", "developers"); + devs = new RemoteVar(ServerNode.NAME, ServerNode.VAR_developers); sigDevs = new Signal("sigDevs", () -> { return devs.get(); }, devs); - tasks = new RemoteVar("ServerNode", "tasks"); + tasks = new RemoteVar(ServerNode.NAME, ServerNode.VAR_tasks); sigTasks = new Signal("sigTests", () -> { return tasks.get(); }, tasks); diff --git a/Dream2/src/examples/java/dream/examples/util/Client.java b/Dream2/src/examples/java/dream/examples/util/Client.java index fc2d50c..3a283a0 100644 --- a/Dream2/src/examples/java/dream/examples/util/Client.java +++ b/Dream2/src/examples/java/dream/examples/util/Client.java @@ -133,4 +133,8 @@ public String toVar(String host, String var) { return var + "@" + host; } + public String toVar(Pair var) { + return toVar(var.getFirst(), var.getSecond()); + } + } From dcac9aafc4b28fef266bf88fba2da23b90b5fb8e Mon Sep 17 00:00:00 2001 From: Tobias Becker Date: Fri, 3 Jun 2016 14:10:49 +0200 Subject: [PATCH 109/161] added locking methods to utility class --- .../java/dream/examples/util/Client.java | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/Dream2/src/examples/java/dream/examples/util/Client.java b/Dream2/src/examples/java/dream/examples/util/Client.java index 3a283a0..4dfcb4d 100644 --- a/Dream2/src/examples/java/dream/examples/util/Client.java +++ b/Dream2/src/examples/java/dream/examples/util/Client.java @@ -2,11 +2,16 @@ import java.io.IOException; import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.LinkedList; import java.util.List; +import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; import dream.client.DreamClient; +import dream.client.LockToken; import dream.common.Consts; import dream.locking.LockManagerLauncher; import dream.server.ServerLauncher; @@ -129,6 +134,24 @@ public String getHostName() { return Consts.hostName; } + private LinkedList lockQueue = new LinkedList<>(); + + public LockToken readLock(String... vars) { + Set temp = new HashSet<>(); + Collections.addAll(temp, vars); + lockQueue.addLast(DreamClient.instance.readLock(temp)); + return lockQueue.getLast(); + } + + public void unlock(LockToken lock) { + lockQueue.remove(lock); + DreamClient.instance.unlock(lock); + } + + public void unlock() { + DreamClient.instance.unlock(lockQueue.poll()); + } + public String toVar(String host, String var) { return var + "@" + host; } From 0edfe4cdfb3ed547464524a27905b9643bbc563d Mon Sep 17 00:00:00 2001 From: Tobias Becker Date: Fri, 3 Jun 2016 14:11:11 +0200 Subject: [PATCH 110/161] taskBoard: added lock while writing --- .../src/examples/java/dream/examples/taskBoard/ServerNode.java | 2 +- .../src/examples/java/dream/examples/taskBoard/TaskCreater.java | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Dream2/src/examples/java/dream/examples/taskBoard/ServerNode.java b/Dream2/src/examples/java/dream/examples/taskBoard/ServerNode.java index cc11bf9..8477da8 100644 --- a/Dream2/src/examples/java/dream/examples/taskBoard/ServerNode.java +++ b/Dream2/src/examples/java/dream/examples/taskBoard/ServerNode.java @@ -54,7 +54,7 @@ private void detectClients() { } private void createClient(String clientHost, String clientVar) { - System.out.println("detected client " + clientHost + " " + clientVar); + logger.info("detected client " + clientHost + " " + clientVar); RemoteVar rv = new RemoteVar<>(clientHost, clientVar); Signal sig = new Signal<>(clientHost, () -> { if (rv.get() != null) { diff --git a/Dream2/src/examples/java/dream/examples/taskBoard/TaskCreater.java b/Dream2/src/examples/java/dream/examples/taskBoard/TaskCreater.java index 000b1cb..b32a894 100644 --- a/Dream2/src/examples/java/dream/examples/taskBoard/TaskCreater.java +++ b/Dream2/src/examples/java/dream/examples/taskBoard/TaskCreater.java @@ -49,8 +49,10 @@ public Logger getLogger() { } public void addTask(Task t) { + readLock(toVar(getHostName(), VAR_newDev), toVar(getHostName(), VAR_newTask)); taskCreater.set(t.getTaskString()); devCreater.set(t.getDevString()); + unlock(); } } From d3ece9727c5778cd4fd26ace62cef5bdadd36793 Mon Sep 17 00:00:00 2001 From: Tobias Becker Date: Sat, 4 Jun 2016 11:20:25 +0200 Subject: [PATCH 111/161] added locking and utility class to readme --- README.md | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/README.md b/README.md index 3e1f952..ade1b82 100644 --- a/README.md +++ b/README.md @@ -50,6 +50,54 @@ Signal n = }); ``` +## Locks +When using Atomic_Consistency it is required to lock a Var before changing its value. +``` + LockToken lock = DreamClient.instance.readLock(new HashSet<>("exVar@Host1")); + myVar.set("CCC"); + DreamClient.instance.unlock(lock); +``` + +## Utility Class +In the dream.examples.util-package you can find the class Client which does all the needed setup for Dream. +It starts the DreamServer if it is not already running, sets the Client Name and connects to the Dependency Graph. +It also provides some additional utilities. + +``` +public class MyClass extends Client { + + public MyClass() { + super("ClientName"); + + // Here you can add you Vars/Signals + Var myVar = new Var<>("myVar", "value"); + } +} +``` + +It can also wait for defined Vars to be available: + +``` +public class OtherClass extends Client { + + public OtherClass() { + super("OtherName"); + // The super class blocks until myVar@ClientName is available + RemoteVar theirVar = new RemoteVar<>("ClientName", "myVar"); + } + + @Override + protected List waitForVars() { + return Arrays.asList("myVar@ClientName"); + } + + @Override + protected void init() { + // The code here is executed before the the super class blocks + } +} +``` + ## Contributors Alessandro Margara From 21485ffa0fa961e30c7e11ee9e283eb107843dba Mon Sep 17 00:00:00 2001 From: Tobias Becker Date: Mon, 6 Jun 2016 09:45:39 +0200 Subject: [PATCH 112/161] refactored to more appropriate names --- .../taskBoard/{Task.java => Assignment.java} | 4 +- .../{TaskCreater.java => Creator.java} | 34 ++-- .../dream/examples/taskBoard/InitApp.java | 10 +- .../{TaskMonitor.java => Monitor.java} | 14 +- .../{ServerNode.java => Server.java} | 160 +++++++++--------- 5 files changed, 111 insertions(+), 111 deletions(-) rename Dream2/src/examples/java/dream/examples/taskBoard/{Task.java => Assignment.java} (92%) rename Dream2/src/examples/java/dream/examples/taskBoard/{TaskCreater.java => Creator.java} (85%) rename Dream2/src/examples/java/dream/examples/taskBoard/{TaskMonitor.java => Monitor.java} (91%) rename Dream2/src/examples/java/dream/examples/taskBoard/{ServerNode.java => Server.java} (84%) diff --git a/Dream2/src/examples/java/dream/examples/taskBoard/Task.java b/Dream2/src/examples/java/dream/examples/taskBoard/Assignment.java similarity index 92% rename from Dream2/src/examples/java/dream/examples/taskBoard/Task.java rename to Dream2/src/examples/java/dream/examples/taskBoard/Assignment.java index d1811af..b0442c5 100644 --- a/Dream2/src/examples/java/dream/examples/taskBoard/Task.java +++ b/Dream2/src/examples/java/dream/examples/taskBoard/Assignment.java @@ -10,13 +10,13 @@ * @author Min Yang * @author Tobias Becker */ -public class Task implements Serializable { +public class Assignment implements Serializable { private static final long serialVersionUID = 8329097603920137211L; public static String pattern = "D(\\d*):T(\\d*)"; private int developer; private int task; - public Task(String input) { + public Assignment(String input) { Matcher m = Pattern.compile(pattern).matcher(input); if (m.matches()) { developer = Integer.parseInt(m.group(1)); diff --git a/Dream2/src/examples/java/dream/examples/taskBoard/TaskCreater.java b/Dream2/src/examples/java/dream/examples/taskBoard/Creator.java similarity index 85% rename from Dream2/src/examples/java/dream/examples/taskBoard/TaskCreater.java rename to Dream2/src/examples/java/dream/examples/taskBoard/Creator.java index b32a894..f17f780 100644 --- a/Dream2/src/examples/java/dream/examples/taskBoard/TaskCreater.java +++ b/Dream2/src/examples/java/dream/examples/taskBoard/Creator.java @@ -25,33 +25,33 @@ * @author Min Yang * @author Tobias Becker */ -public class TaskCreater extends Client { +public class Creator extends Client { public static final String VAR_newDev = "newDev"; public static final String VAR_newTask = "newTask"; - private Var taskCreater; - private Var devCreater; + private Var taskCreator; + private Var devCreator; - public TaskCreater() { - super("TaskCreater" + new Random().nextInt(1000)); - taskCreater = new Var<>(VAR_newTask, null); - devCreater = new Var<>(VAR_newDev, null); + public Creator() { + super("Creator" + new Random().nextInt(1000)); + taskCreator = new Var<>(VAR_newTask, null); + devCreator = new Var<>(VAR_newDev, null); new TaskCreaterGUI(this); } public static void main(String[] args) { - new TaskCreater(); + new Creator(); } public Logger getLogger() { return logger; } - public void addTask(Task t) { + public void addTask(Assignment t) { readLock(toVar(getHostName(), VAR_newDev), toVar(getHostName(), VAR_newTask)); - taskCreater.set(t.getTaskString()); - devCreater.set(t.getDevString()); + taskCreator.set(t.getTaskString()); + devCreator.set(t.getDevString()); unlock(); } } @@ -60,10 +60,10 @@ class TaskCreaterGUI { private JTextField textField1; private JFrame frame1; private JButton button1; - private TaskCreater taskCreater; + private Creator creator; - public TaskCreaterGUI(TaskCreater t) { - this.taskCreater = t; + public TaskCreaterGUI(Creator t) { + this.creator = t; initComponents(); } @@ -152,13 +152,13 @@ class ButtonListener implements ActionListener { @Override public void actionPerformed(ActionEvent paramActionEvent) { String toTasks = textField1.getText(); - if (Task.isValid(toTasks)) { - taskCreater.addTask(new Task(toTasks)); + if (Assignment.isValid(toTasks)) { + creator.addTask(new Assignment(toTasks)); textField1.setText(""); } else { textField1.setText(""); JOptionPane.showMessageDialog(null, "Please input the right pattern of task. (D:T)"); - taskCreater.getLogger().info("Wrong input pattern of tasks"); + creator.getLogger().info("Wrong input pattern of tasks"); } } } diff --git a/Dream2/src/examples/java/dream/examples/taskBoard/InitApp.java b/Dream2/src/examples/java/dream/examples/taskBoard/InitApp.java index 2e83ecc..7711074 100644 --- a/Dream2/src/examples/java/dream/examples/taskBoard/InitApp.java +++ b/Dream2/src/examples/java/dream/examples/taskBoard/InitApp.java @@ -7,7 +7,7 @@ /** * To start this example either:
- * - run {@link ServerNode}, {@link TaskCreater} and {@link TaskMonitor} in any + * - run {@link Server}, {@link Creator} and {@link Monitor} in any * order
* - or run this class.
* This class will start all three classes each in a seperate instance of the @@ -23,10 +23,10 @@ public class InitApp { public static void main(String... args) { processes = new ArrayList<>(); - processes.add(NewJvmHelper.startNewJVM(ServerNode.class)); - processes.add(NewJvmHelper.startNewJVM(TaskCreater.class)); - processes.add(NewJvmHelper.startNewJVM(TaskCreater.class)); - processes.add(NewJvmHelper.startNewJVM(TaskMonitor.class)); + processes.add(NewJvmHelper.startNewJVM(Server.class)); + processes.add(NewJvmHelper.startNewJVM(Creator.class)); + processes.add(NewJvmHelper.startNewJVM(Creator.class)); + processes.add(NewJvmHelper.startNewJVM(Monitor.class)); sleep(-1); } diff --git a/Dream2/src/examples/java/dream/examples/taskBoard/TaskMonitor.java b/Dream2/src/examples/java/dream/examples/taskBoard/Monitor.java similarity index 91% rename from Dream2/src/examples/java/dream/examples/taskBoard/TaskMonitor.java rename to Dream2/src/examples/java/dream/examples/taskBoard/Monitor.java index 6118394..b80ebea 100644 --- a/Dream2/src/examples/java/dream/examples/taskBoard/TaskMonitor.java +++ b/Dream2/src/examples/java/dream/examples/taskBoard/Monitor.java @@ -20,7 +20,7 @@ * @author Min Yang * @author Tobias Becker */ -public class TaskMonitor extends Client { +public class Monitor extends Client { public static final String NAME = "TaskMonitor"; @@ -32,20 +32,20 @@ public class TaskMonitor extends Client { @Override protected List waitForVars() { - return Arrays.asList(toVar(ServerNode.NAME, ServerNode.VAR_developers), - toVar(ServerNode.NAME, ServerNode.VAR_tasks)); + return Arrays.asList(toVar(Server.NAME, Server.VAR_developers), + toVar(Server.NAME, Server.VAR_tasks)); } - public TaskMonitor() { + public Monitor() { super(NAME); gui = new MonitorGUI(); - devs = new RemoteVar(ServerNode.NAME, ServerNode.VAR_developers); + devs = new RemoteVar(Server.NAME, Server.VAR_developers); sigDevs = new Signal("sigDevs", () -> { return devs.get(); }, devs); - tasks = new RemoteVar(ServerNode.NAME, ServerNode.VAR_tasks); + tasks = new RemoteVar(Server.NAME, Server.VAR_tasks); sigTasks = new Signal("sigTests", () -> { return tasks.get(); }, tasks); @@ -62,7 +62,7 @@ public TaskMonitor() { } public static void main(String[] args) { - new TaskMonitor(); + new Monitor(); } } diff --git a/Dream2/src/examples/java/dream/examples/taskBoard/ServerNode.java b/Dream2/src/examples/java/dream/examples/taskBoard/Server.java similarity index 84% rename from Dream2/src/examples/java/dream/examples/taskBoard/ServerNode.java rename to Dream2/src/examples/java/dream/examples/taskBoard/Server.java index 8477da8..cf05762 100644 --- a/Dream2/src/examples/java/dream/examples/taskBoard/ServerNode.java +++ b/Dream2/src/examples/java/dream/examples/taskBoard/Server.java @@ -1,81 +1,81 @@ -package dream.examples.taskBoard; - -import java.util.ArrayList; -import java.util.Set; - -import dream.client.DreamClient; -import dream.client.RemoteVar; -import dream.client.Signal; -import dream.client.Var; -import dream.examples.util.Client; -import dream.examples.util.Pair; - -/** - * Holds a list of tasks and a list of developers each indicated by a simple - * integer. Searches for new clients (TaskCreater) and registers to their - * "task creation channels" - * - * @author Min Yang - * @author Tobias Becker - */ -public class ServerNode extends Client { - public static final String NAME = "ServerNode"; - public static final String VAR_developers = "developers"; - public static final String VAR_tasks = "tasks"; - - private final ArrayList myClients; - private final Var developers; - private final Var tasks; - - public static void main(String... args) { - new ServerNode(); - } - - public ServerNode() { - super(NAME); - developers = new Var(VAR_developers, ""); - tasks = new Var(VAR_tasks, ""); - myClients = new ArrayList(); - detectClients(); - } - - private void detectClients() { - Set vars = DreamClient.instance.listVariables(); - vars.stream().map(x -> new Pair(x.split("@")[1], x.split("@")[0])) - .filter(x -> !myClients.contains(toVar(x)) && (x.getSecond().equalsIgnoreCase(TaskCreater.VAR_newTask) - || x.getSecond().equalsIgnoreCase(TaskCreater.VAR_newDev))) - .forEach(x -> createClient(x.getFirst(), x.getSecond())); - try { - Thread.sleep(500); - } catch (InterruptedException e) { - e.printStackTrace(); - } - detectClients(); - } - - private void createClient(String clientHost, String clientVar) { - logger.info("detected client " + clientHost + " " + clientVar); - RemoteVar rv = new RemoteVar<>(clientHost, clientVar); - Signal sig = new Signal<>(clientHost, () -> { - if (rv.get() != null) { - return rv.get(); - } else { - return null; - } - }, rv); - - sig.change().addHandler((oldValue, newValue) -> { - if (newValue != null) { - // Set vars for remote querying - if (clientVar.equalsIgnoreCase(TaskCreater.VAR_newDev)) { - developers.set(developers.get().length() == 0 ? newValue : developers.get() + ":" + newValue); - } - if (clientVar.equalsIgnoreCase(TaskCreater.VAR_newTask)) { - tasks.set(tasks.get().length() == 0 ? newValue : tasks.get() + ":" + newValue); - } - System.out.println("new value from " + clientHost + "@" + clientVar); - } - }); - myClients.add(clientVar + "@" + clientHost); - } +package dream.examples.taskBoard; + +import java.util.ArrayList; +import java.util.Set; + +import dream.client.DreamClient; +import dream.client.RemoteVar; +import dream.client.Signal; +import dream.client.Var; +import dream.examples.util.Client; +import dream.examples.util.Pair; + +/** + * Holds a list of tasks and a list of developers each indicated by a simple + * integer. Searches for new clients (TaskCreater) and registers to their + * "task creation channels" + * + * @author Min Yang + * @author Tobias Becker + */ +public class Server extends Client { + public static final String NAME = "ServerNode"; + public static final String VAR_developers = "developers"; + public static final String VAR_tasks = "tasks"; + + private final ArrayList myClients; + private final Var developers; + private final Var tasks; + + public static void main(String... args) { + new Server(); + } + + public Server() { + super(NAME); + developers = new Var(VAR_developers, ""); + tasks = new Var(VAR_tasks, ""); + myClients = new ArrayList(); + detectClients(); + } + + private void detectClients() { + Set vars = DreamClient.instance.listVariables(); + vars.stream().map(x -> new Pair(x.split("@")[1], x.split("@")[0])) + .filter(x -> !myClients.contains(toVar(x)) && (x.getSecond().equalsIgnoreCase(Creator.VAR_newTask) + || x.getSecond().equalsIgnoreCase(Creator.VAR_newDev))) + .forEach(x -> createClient(x.getFirst(), x.getSecond())); + try { + Thread.sleep(500); + } catch (InterruptedException e) { + e.printStackTrace(); + } + detectClients(); + } + + private void createClient(String clientHost, String clientVar) { + logger.info("detected client " + clientHost + " " + clientVar); + RemoteVar rv = new RemoteVar<>(clientHost, clientVar); + Signal sig = new Signal<>(clientHost, () -> { + if (rv.get() != null) { + return rv.get(); + } else { + return null; + } + }, rv); + + sig.change().addHandler((oldValue, newValue) -> { + if (newValue != null) { + // Set vars for remote querying + if (clientVar.equalsIgnoreCase(Creator.VAR_newDev)) { + developers.set(developers.get().length() == 0 ? newValue : developers.get() + ":" + newValue); + } + if (clientVar.equalsIgnoreCase(Creator.VAR_newTask)) { + tasks.set(tasks.get().length() == 0 ? newValue : tasks.get() + ":" + newValue); + } + System.out.println("new value from " + clientHost + "@" + clientVar); + } + }); + myClients.add(clientVar + "@" + clientHost); + } } \ No newline at end of file From 835c667e1897664224d8701e171575e153347928 Mon Sep 17 00:00:00 2001 From: Tobias Becker Date: Mon, 6 Jun 2016 09:52:34 +0200 Subject: [PATCH 113/161] fix bug when there is no lock to release --- Dream2/src/main/java/dream/locking/LockManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dream2/src/main/java/dream/locking/LockManager.java b/Dream2/src/main/java/dream/locking/LockManager.java index f89b767..d998838 100644 --- a/Dream2/src/main/java/dream/locking/LockManager.java +++ b/Dream2/src/main/java/dream/locking/LockManager.java @@ -55,7 +55,7 @@ final Set processLockRelease(LockReleasePacket release) { final LockRequestPacket reqPkt = activeLocks.get(lockID); final Set result = new HashSet<>(); - if (release(reqPkt.getLockID(), reqPkt.getLockNodes(), reqPkt.getType())) { + if (reqPkt != null && release(reqPkt.getLockID(), reqPkt.getLockNodes(), reqPkt.getType())) { final Iterator it = pendingRequests.iterator(); while (it.hasNext()) { final LockRequestPacket request = it.next(); From 68ad676873b3fc120c47cb3983bf8409900ce078 Mon Sep 17 00:00:00 2001 From: Tobias Becker Date: Mon, 6 Jun 2016 14:05:11 +0200 Subject: [PATCH 114/161] taskBoard: fix bug with locking --- .../src/examples/java/dream/examples/taskBoard/Creator.java | 2 -- .../src/examples/java/dream/examples/taskBoard/Monitor.java | 5 ++--- .../src/examples/java/dream/examples/taskBoard/Server.java | 2 +- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/Dream2/src/examples/java/dream/examples/taskBoard/Creator.java b/Dream2/src/examples/java/dream/examples/taskBoard/Creator.java index f17f780..7ca6899 100644 --- a/Dream2/src/examples/java/dream/examples/taskBoard/Creator.java +++ b/Dream2/src/examples/java/dream/examples/taskBoard/Creator.java @@ -49,10 +49,8 @@ public Logger getLogger() { } public void addTask(Assignment t) { - readLock(toVar(getHostName(), VAR_newDev), toVar(getHostName(), VAR_newTask)); taskCreator.set(t.getTaskString()); devCreator.set(t.getDevString()); - unlock(); } } diff --git a/Dream2/src/examples/java/dream/examples/taskBoard/Monitor.java b/Dream2/src/examples/java/dream/examples/taskBoard/Monitor.java index b80ebea..d0a9e46 100644 --- a/Dream2/src/examples/java/dream/examples/taskBoard/Monitor.java +++ b/Dream2/src/examples/java/dream/examples/taskBoard/Monitor.java @@ -22,7 +22,7 @@ */ public class Monitor extends Client { - public static final String NAME = "TaskMonitor"; + public static final String NAME = "Monitor"; private final MonitorGUI gui; private final RemoteVar devs; @@ -32,8 +32,7 @@ public class Monitor extends Client { @Override protected List waitForVars() { - return Arrays.asList(toVar(Server.NAME, Server.VAR_developers), - toVar(Server.NAME, Server.VAR_tasks)); + return Arrays.asList(toVar(Server.NAME, Server.VAR_developers), toVar(Server.NAME, Server.VAR_tasks)); } public Monitor() { diff --git a/Dream2/src/examples/java/dream/examples/taskBoard/Server.java b/Dream2/src/examples/java/dream/examples/taskBoard/Server.java index cf05762..3c3ecc5 100644 --- a/Dream2/src/examples/java/dream/examples/taskBoard/Server.java +++ b/Dream2/src/examples/java/dream/examples/taskBoard/Server.java @@ -56,7 +56,7 @@ private void detectClients() { private void createClient(String clientHost, String clientVar) { logger.info("detected client " + clientHost + " " + clientVar); RemoteVar rv = new RemoteVar<>(clientHost, clientVar); - Signal sig = new Signal<>(clientHost, () -> { + Signal sig = new Signal<>(clientHost + "-" + clientVar, () -> { if (rv.get() != null) { return rv.get(); } else { From d1b9f878836e4b4527c02196363eb194a051207c Mon Sep 17 00:00:00 2001 From: Tobias Becker Date: Mon, 6 Jun 2016 15:58:39 +0200 Subject: [PATCH 115/161] taskBoard: re-added button and changed display functionality --- .../dream/examples/taskBoard/Monitor.java | 35 +++++++++++++++---- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/Dream2/src/examples/java/dream/examples/taskBoard/Monitor.java b/Dream2/src/examples/java/dream/examples/taskBoard/Monitor.java index d0a9e46..77a085a 100644 --- a/Dream2/src/examples/java/dream/examples/taskBoard/Monitor.java +++ b/Dream2/src/examples/java/dream/examples/taskBoard/Monitor.java @@ -1,10 +1,12 @@ package dream.examples.taskBoard; import java.awt.Container; +import java.awt.event.ActionEvent; import java.util.Arrays; import java.util.List; import javax.swing.GroupLayout; +import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JTextArea; @@ -37,7 +39,7 @@ protected List waitForVars() { public Monitor() { super(NAME); - gui = new MonitorGUI(); + gui = new MonitorGUI(this); devs = new RemoteVar(Server.NAME, Server.VAR_developers); sigDevs = new Signal("sigDevs", () -> { @@ -51,18 +53,25 @@ public Monitor() { sigDevs.change().addHandler((oldVa, newVal) -> { System.out.println("newVal devs:" + newVal); - gui.setDevs(newVal); + // gui.setDevs(newVal); }); sigTasks.change().addHandler((oldVa, newVal) -> { System.out.println("newVal tasks:" + newVal); - gui.setTasks(newVal); + // gui.setTasks(newVal); }); } public static void main(String[] args) { new Monitor(); } + + public void clickButton1() { + readLock(toVar(Server.NAME, Server.VAR_tasks), toVar(Server.NAME, Server.VAR_developers)); + gui.setTasks(tasks.get()); + gui.setDevs(devs.get()); + unlock(); + } } class MonitorGUI { @@ -71,11 +80,15 @@ class MonitorGUI { private JLabel label1; private JLabel label2; private JTextArea textAreaDevs; + private JButton button1; + private Monitor monitor; - public MonitorGUI() { + public MonitorGUI(Monitor m) { + this.monitor = m; frame1 = new JFrame(); label1 = new JLabel(); label2 = new JLabel(); + button1 = new JButton(); textAreaTasks = new JTextArea(); textAreaDevs = new JTextArea(); textAreaDevs.setEditable(false); @@ -90,6 +103,10 @@ public MonitorGUI() { // ---- label2 ---- label2.setText("Current tasks:"); + // ---- button1 ---- + button1.setText("Show me tasks"); + button1.addActionListener(e -> button1ActionPerformed(e)); + GroupLayout frame1ContentPaneLayout = new GroupLayout(frame1ContentPane); frame1ContentPane.setLayout(frame1ContentPaneLayout); frame1ContentPaneLayout.setHorizontalGroup(frame1ContentPaneLayout.createParallelGroup() @@ -100,10 +117,10 @@ public MonitorGUI() { .addGroup(frame1ContentPaneLayout.createParallelGroup().addComponent(label2).addComponent( textAreaTasks, GroupLayout.PREFERRED_SIZE, 57, GroupLayout.PREFERRED_SIZE)) .addContainerGap(9, Short.MAX_VALUE)) - .addGroup(GroupLayout.Alignment.TRAILING, - frame1ContentPaneLayout.createSequentialGroup().addContainerGap(11, Short.MAX_VALUE))); + .addGroup(GroupLayout.Alignment.TRAILING, frame1ContentPaneLayout.createSequentialGroup() + .addContainerGap(11, Short.MAX_VALUE).addComponent(button1).addGap(71, 71, 71))); frame1ContentPaneLayout.setVerticalGroup(frame1ContentPaneLayout.createParallelGroup() - .addGroup(frame1ContentPaneLayout.createSequentialGroup() + .addGroup(frame1ContentPaneLayout.createSequentialGroup().addComponent(button1) .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED) .addGroup(frame1ContentPaneLayout.createParallelGroup(GroupLayout.Alignment.BASELINE) .addComponent(label2).addComponent(label1)) @@ -119,6 +136,10 @@ public MonitorGUI() { } } + private void button1ActionPerformed(ActionEvent e) { + monitor.clickButton1(); + } + public void setDevs(String value) { textAreaDevs.setText(value.replace(":", "\n")); } From 96f0a121ef4a9a573432e7fdbd9aefeb511ccc31 Mon Sep 17 00:00:00 2001 From: rkamath3 Date: Mon, 6 Jun 2016 16:06:00 +0200 Subject: [PATCH 116/161] Added Task example with vector clock --- .../dream/examples/tasks/DeligatProcess.java | 14 +- .../dream/examples/tasks/MasterProcess.java | 40 ++- .../java/dream/examples/tasks/Message.java | 7 +- .../examples/tasks/UiUpdatesListner.java | 5 +- .../dream/examples/tasks/WorkerHelper.java | 76 ++---- .../dream/examples/tasks/WorkerProcess.java | 257 ++++++++++++------ .../java/dream/examples/util/VectorClock.java | 124 +++++++++ 7 files changed, 376 insertions(+), 147 deletions(-) create mode 100644 Dream2/src/examples/java/dream/examples/util/VectorClock.java diff --git a/Dream2/src/examples/java/dream/examples/tasks/DeligatProcess.java b/Dream2/src/examples/java/dream/examples/tasks/DeligatProcess.java index e3c2cd0..d56d0d0 100644 --- a/Dream2/src/examples/java/dream/examples/tasks/DeligatProcess.java +++ b/Dream2/src/examples/java/dream/examples/tasks/DeligatProcess.java @@ -3,10 +3,13 @@ */ package dream.examples.tasks; +import java.util.HashMap; + import dream.client.RemoteVar; import dream.client.Signal; import dream.client.Var; import dream.common.Consts; +import dream.examples.util.VectorClock; /** * @author Ram @@ -16,6 +19,7 @@ public class DeligatProcess { static int i; int clock; + VectorClock localClock = new VectorClock("p2"); /** * @param args */ @@ -55,7 +59,12 @@ private void init() { // Register a handler which will be executed upon receiving the signal s.change().addHandler((oldVal, val) -> { - clock++; + + localClock.updateClock(); + HashMap messageClock = val.getClock(); + if (localClock.isNew(messageClock)) { + localClock.updateClock(messageClock); + } try { Thread.sleep(1000); } catch (Exception e) { @@ -63,7 +72,8 @@ private void init() { e.printStackTrace(); } val.getTask().setAssignee(i++ % 10 + ""); - val.setClock(val.getClock() + "@p2:" + clock); + localClock.updateClock(); + val.setClock(localClock.getLocalClock()); myVar.set(val); }); diff --git a/Dream2/src/examples/java/dream/examples/tasks/MasterProcess.java b/Dream2/src/examples/java/dream/examples/tasks/MasterProcess.java index 685c0bf..ef2ad4d 100644 --- a/Dream2/src/examples/java/dream/examples/tasks/MasterProcess.java +++ b/Dream2/src/examples/java/dream/examples/tasks/MasterProcess.java @@ -5,6 +5,7 @@ import dream.client.Var; import dream.common.Consts; +import dream.examples.util.VectorClock; /** * @author Ram @@ -14,26 +15,41 @@ public class MasterProcess { private void init() { Consts.hostName = "Host1"; - int clock = 0; + + VectorClock vectorClock = new VectorClock("p1"); Var initTask = new Var("TASK", null); - Var initTask1 = new Var("TASK1", null); + Var initTask2 = new Var("TASK2", null); try { int i = 0; while (true) { - Message m = new Message(); + // Create a message to be distributed + Message message = new Message(); Thread.sleep(5000); - Task t = new Task("Task" + i); - t.setId(1000 + i); - m.setTask(t); - clock++; - m.setClock("@p1:" + clock); - initTask.set(m); - t.setClock(clock); - t.setDescription("This is " + i + "th task"); + // Add task to a message + Task task = new Task("Task" + i); + + // Set id for the task + task.setId(1000 + i); + + message.setTask(task); + // increment local clock + vectorClock.updateClock(); + + // set clock for the task + message.setClock(vectorClock.getLocalClock()); - initTask1.set(m); + // Add description to task + task.setDescription("This is " + i + "th task"); + + // Send task to Deligator + initTask.set(message); + + // link latency + Thread.sleep(5000); + // Send task to worker + initTask2.set(message); i++; } } catch (Exception e) { diff --git a/Dream2/src/examples/java/dream/examples/tasks/Message.java b/Dream2/src/examples/java/dream/examples/tasks/Message.java index f8dbf04..200da3e 100644 --- a/Dream2/src/examples/java/dream/examples/tasks/Message.java +++ b/Dream2/src/examples/java/dream/examples/tasks/Message.java @@ -4,6 +4,7 @@ package dream.examples.tasks; import java.io.Serializable; +import java.util.HashMap; /** * Message format used for communication between different nodes @@ -15,7 +16,7 @@ public class Message implements Serializable { private static final long serialVersionUID = -9119849212879479791L; Task task; - String clock; + HashMap clock; /** * @return the task @@ -35,7 +36,7 @@ public void setTask(Task task) { /** * @return the clock */ - public String getClock() { + public HashMap getClock() { return clock; } @@ -43,7 +44,7 @@ public String getClock() { * @param clock * the clock to set */ - public void setClock(String clock) { + public void setClock(HashMap clock) { this.clock = clock; } diff --git a/Dream2/src/examples/java/dream/examples/tasks/UiUpdatesListner.java b/Dream2/src/examples/java/dream/examples/tasks/UiUpdatesListner.java index 32df328..c5ee2d0 100644 --- a/Dream2/src/examples/java/dream/examples/tasks/UiUpdatesListner.java +++ b/Dream2/src/examples/java/dream/examples/tasks/UiUpdatesListner.java @@ -8,5 +8,8 @@ * */ public interface UiUpdatesListner { - public void updateTasks(String task); + + void updateTasks(String task, boolean accepted); + + void updateClockinUi(String string); } diff --git a/Dream2/src/examples/java/dream/examples/tasks/WorkerHelper.java b/Dream2/src/examples/java/dream/examples/tasks/WorkerHelper.java index d58e5e3..b9408b7 100644 --- a/Dream2/src/examples/java/dream/examples/tasks/WorkerHelper.java +++ b/Dream2/src/examples/java/dream/examples/tasks/WorkerHelper.java @@ -3,13 +3,11 @@ */ package dream.examples.tasks; -import java.util.ArrayList; -import java.util.HashMap; - import dream.client.RemoteVar; import dream.client.Signal; import dream.client.Var; import dream.common.Consts; +import dream.examples.util.VectorClock; /** * @author Ram @@ -17,12 +15,8 @@ */ public class WorkerHelper implements Runnable { static int i = 0; - int localClock = 0; UiUpdatesListner listner; - ArrayList> disClock = new ArrayList<>(); - HashMap p1 = new HashMap(); - HashMap p2 = new HashMap(); - HashMap p3 = new HashMap(); + VectorClock localClock = new VectorClock("p3"); Var complexEvent = new Var("COMPEVENT", null); /** * @param args @@ -52,28 +46,34 @@ public WorkerHelper() { // TODO Auto-generated constructor stub } - public void isEvent() { - boolean COMPLEX_EVENT = false; - for (Integer i : p1.keySet()) { - for (Integer j : p2.keySet()) { - COMPLEX_EVENT = (p2.get(j).getId() == p1.get(i).getId() ? true : false); - if (COMPLEX_EVENT) { - Task task = p1.get(i); - task.setAssignee(p2.get(j).getAssignee()); - p1.remove(i); - p2.remove(j); - complexEvent.set(task); - break; - } - } + public void isEvent(Message val) { + // update clock on receiving new message + localClock.updateClock(); + // check if the received message is new + if (localClock.isNew(val.getClock())) { + // update clock with the help of new message + localClock.updateClock(val.getClock()); + Task task = val.getTask(); + listner.updateTasks("\nTask Name: " + task.getName() + "\n" + "Task Discription: " + task.getDescription() + + "\n" + "Assignee ID: " + task.getAssignee(), true); + System.out.println("New Message and Accepted \n" + "Task Name: " + task.getName() + "\n" + + "Task Discription: " + task.getDescription() + "\n" + "Assignee ID: " + task.getAssignee()); + + } else { + Task task = val.getTask(); + listner.updateTasks("\nTask Name: " + task.getName() + "\n" + "Task Discription: " + task.getDescription() + + "\n" + "Assignee ID: " + task.getAssignee(), false); + System.out.println("New Message and Rejected \n" + "Task Name: " + task.getName() + "\n" + + "Task Discription: " + task.getDescription() + "\n" + "Assignee ID: " + task.getAssignee()); } + listner.updateClockinUi(localClock.getLocalClock().toString()); } public void run() { Consts.hostName = "Host3"; - RemoteVar task = new RemoteVar("Host1", "TASK1"); + RemoteVar task = new RemoteVar("Host1", "TASK2"); RemoteVar taskDeligated = new RemoteVar("Host2", "TASK_ASSIGNED"); Signal signalFromMaster = new Signal("s", () -> { @@ -83,42 +83,22 @@ public void run() { return taskDeligated.get(); } , taskDeligated); - Signal signalForComplexEvent = new Signal("s2", () -> { - return complexEvent.get(); - } , complexEvent); - // Register a handler which will be executed upon receiving the signal // from master process signalFromMaster.change().addHandler((oldVal, val) -> { - Task t = (Task) val.getTask(); - p1.put(t.getClock(), t); - if (t != null) { - System.out.println("[MASTER]\nAssignee:" + t.getAssignee() + "\nTaskName: " + t.getName() - + "\nDescription " + t.getDescription() + " \nAnnotated clock: " + val.getClock()); - - System.out.println("_________________________________________________"); + if (val != null) { + isEvent(val); } - isEvent(); }); // Register a handler which will be executed upon receiving the signal // from delegate process signalFromDeligator.change().addHandler((oldVal, val) -> { - Task t = (Task) val.getTask(); - if (t != null) { - System.out.println("[DELEGATOR]\nTask Id:" + t.getId() + "\nAssignee:" + t.getAssignee() - + "\nTaskName: " + t.getName() + "\nDescription " + t.getDescription() + "\nAnnotated clock: " - + val.getClock()); - System.out.println("_________________________________________________"); + if (val != null) { + isEvent(val); } - p2.put(t.getClock(), t); - isEvent(); - }); - signalForComplexEvent.change().addHandler((oldVal, val) -> { - Task t = val; - listner.updateTasks("Task Id:" + t.getId() + "\nAssignee:" + t.getAssignee() + "\nTaskName: " + t.getName() - + "\nDescription " + t.getDescription()); }); + } public void addListners(WorkerProcess dsUi) { diff --git a/Dream2/src/examples/java/dream/examples/tasks/WorkerProcess.java b/Dream2/src/examples/java/dream/examples/tasks/WorkerProcess.java index f3df503..49cb8aa 100644 --- a/Dream2/src/examples/java/dream/examples/tasks/WorkerProcess.java +++ b/Dream2/src/examples/java/dream/examples/tasks/WorkerProcess.java @@ -3,23 +3,23 @@ */ package dream.examples.tasks; -import javax.swing.JFrame; +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ /** * - * @author kamathar + * @author Ram */ -public class WorkerProcess implements UiUpdatesListner { - private javax.swing.JFrame mainFrame; - private javax.swing.JPanel DisplayPanel; - private javax.swing.JTextArea infoTextArea; - private javax.swing.JScrollPane jScrollPane1; - private javax.swing.JLabel infoLabel; - WorkerHelper manager; +public class WorkerProcess extends javax.swing.JFrame implements UiUpdatesListner { /** - * Creates new form WorkerProcess + * Creates new form TaskWindow */ + WorkerHelper manager; + public WorkerProcess(WorkerHelper manager2) { this.manager = manager2; this.manager.addListners(this); @@ -27,61 +27,26 @@ public WorkerProcess(WorkerHelper manager2) { } - /** - * This method is called from within the constructor to initialize the form. - * WARNING: Do NOT modify this code. The content of this method is always - * regenerated by the Form Editor. - */ - private void initComponents() { - mainFrame = new JFrame("Distributed Task Management"); - mainFrame.setResizable(true); - mainFrame.setSize(300, 100); - DisplayPanel = new javax.swing.JPanel(); - jScrollPane1 = new javax.swing.JScrollPane(); - infoTextArea = new javax.swing.JTextArea(); - - infoLabel = new javax.swing.JLabel(); - - mainFrame.setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE); - infoTextArea.setEditable(false); - infoTextArea.setColumns(100); - infoTextArea.setRows(20); - jScrollPane1.setViewportView(infoTextArea); - - javax.swing.GroupLayout DisplayPanelLayout = new javax.swing.GroupLayout(DisplayPanel); - DisplayPanel.setLayout(DisplayPanelLayout); - DisplayPanelLayout - .setHorizontalGroup(DisplayPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(DisplayPanelLayout.createSequentialGroup().addContainerGap() - .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 379, Short.MAX_VALUE) - .addContainerGap())); - DisplayPanelLayout.setVerticalGroup(DisplayPanelLayout - .createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING).addGroup(DisplayPanelLayout - .createSequentialGroup().addGap(0, 0, 0).addComponent(jScrollPane1).addContainerGap())); - - infoLabel.setFont(new java.awt.Font("Times New Roman", 1, 12)); // NOI18N - infoLabel.setText("Task List"); - - javax.swing.GroupLayout layout = new javax.swing.GroupLayout(mainFrame.getContentPane()); - mainFrame.getContentPane().setLayout(layout); - layout.setHorizontalGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(layout.createSequentialGroup().addContainerGap() - .addComponent(DisplayPanel, javax.swing.GroupLayout.PREFERRED_SIZE, - javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addGap(18, 18, 18).addGap(25, 25, 25)) - .addGroup(layout.createSequentialGroup().addGap(24, 24, 24) - .addComponent(infoLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 93, - javax.swing.GroupLayout.PREFERRED_SIZE) - .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))); - layout.setVerticalGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(layout.createSequentialGroup().addContainerGap().addComponent(infoLabel).addGap(3, 3, 3) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING).addComponent( - DisplayPanel, javax.swing.GroupLayout.DEFAULT_SIZE, - javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) - .addContainerGap())); + @Override + public void updateTasks(String task, boolean accepted) { + if (accepted) { + jTextArea1.append("\n" + task); + jTextArea1.append("\n**************************************************************"); + } else { + jTextArea2.append("\n" + task); + jTextArea2.append("\n**************************************************************"); + } - mainFrame.setVisible(true); - mainFrame.pack(); + this.repaint(); + this.revalidate(); + } + + public static void main(String args[]) { + WorkerHelper manager = new WorkerHelper(); + WorkerProcess snapshotUi = new WorkerProcess(manager); + snapshotUi.initUi(); + Thread S = new Thread(manager); + S.start(); } void initUi() { @@ -90,34 +55,164 @@ void initUi() { for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) { if ("Nimbus".equals(info.getName())) { javax.swing.UIManager.setLookAndFeel(info.getClassName()); + this.setVisible(true); break; } } } catch (ClassNotFoundException ex) { - java.util.logging.Logger.getLogger(WorkerProcess.class.getName()).log(java.util.logging.Level.SEVERE, null, ex); + java.util.logging.Logger.getLogger(WorkerProcess.class.getName()).log(java.util.logging.Level.SEVERE, null, + ex); } catch (InstantiationException ex) { - java.util.logging.Logger.getLogger(WorkerProcess.class.getName()).log(java.util.logging.Level.SEVERE, null, ex); + java.util.logging.Logger.getLogger(WorkerProcess.class.getName()).log(java.util.logging.Level.SEVERE, null, + ex); } catch (IllegalAccessException ex) { - java.util.logging.Logger.getLogger(WorkerProcess.class.getName()).log(java.util.logging.Level.SEVERE, null, ex); + java.util.logging.Logger.getLogger(WorkerProcess.class.getName()).log(java.util.logging.Level.SEVERE, null, + ex); } catch (javax.swing.UnsupportedLookAndFeelException ex) { - java.util.logging.Logger.getLogger(WorkerProcess.class.getName()).log(java.util.logging.Level.SEVERE, null, ex); + java.util.logging.Logger.getLogger(WorkerProcess.class.getName()).log(java.util.logging.Level.SEVERE, null, + ex); } - } + private void initComponents() { + + jPanel1 = new javax.swing.JPanel(); + jLabel1 = new javax.swing.JLabel(); + jTextField1 = new javax.swing.JTextField(); + jPanel2 = new javax.swing.JPanel(); + jScrollPane1 = new javax.swing.JScrollPane(); + jTextArea1 = new javax.swing.JTextArea(); + jScrollPane2 = new javax.swing.JScrollPane(); + jTextArea2 = new javax.swing.JTextArea(); + jLabel2 = new javax.swing.JLabel(); + jLabel3 = new javax.swing.JLabel(); + jButton1 = new javax.swing.JButton(); + + setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE); + + jLabel1.setText("Local Clock"); + + jTextField1.setEditable(false); + jTextField1.setText("Local clock will be displayed here"); + + javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1); + jPanel1.setLayout(jPanel1Layout); + jPanel1Layout.setHorizontalGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jPanel1Layout.createSequentialGroup().addGap(67, 67, 67).addComponent(jLabel1) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED).addComponent(jTextField1) + .addContainerGap())); + jPanel1Layout.setVerticalGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jPanel1Layout.createSequentialGroup() + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(jLabel1).addComponent(jTextField1, javax.swing.GroupLayout.PREFERRED_SIZE, + javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addGap(0, 8, Short.MAX_VALUE))); + + jTextArea1.setEditable(false); + jTextArea1.setColumns(20); + jTextArea1.setRows(50); + jScrollPane1.setViewportView(jTextArea1); + + jTextArea2.setEditable(false); + jTextArea2.setColumns(20); + jTextArea2.setRows(50); + jScrollPane2.setViewportView(jTextArea2); + + jLabel2.setText("Accepted Events"); + + jLabel3.setText("Rejected Events"); + + jButton1.setText("Clear"); + jButton1.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + jButton1ActionPerformed(evt); + } + }); + + javax.swing.GroupLayout jPanel2Layout = new javax.swing.GroupLayout(jPanel2); + jPanel2.setLayout(jPanel2Layout); + jPanel2Layout.setHorizontalGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel2Layout.createSequentialGroup() + .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jPanel2Layout.createSequentialGroup().addGap(22, 22, 22).addComponent(jLabel2) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, + javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addGroup(jPanel2Layout.createSequentialGroup().addContainerGap() + .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 345, + Short.MAX_VALUE) + .addGap(18, 18, 18))) + .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, + jPanel2Layout.createSequentialGroup().addComponent(jLabel3).addGap(106, 106, + 106)) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, + jPanel2Layout.createSequentialGroup() + .addComponent(jScrollPane2, javax.swing.GroupLayout.PREFERRED_SIZE, 335, + javax.swing.GroupLayout.PREFERRED_SIZE) + .addContainerGap()))) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel2Layout.createSequentialGroup() + .addGap(0, 0, Short.MAX_VALUE).addComponent(jButton1).addGap(297, 297, 297))); + jPanel2Layout.setVerticalGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, + jPanel2Layout.createSequentialGroup().addContainerGap() + .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(jLabel3).addComponent(jLabel2)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(jScrollPane2, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE) + .addGroup(jPanel2Layout.createSequentialGroup() + .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 360, + javax.swing.GroupLayout.PREFERRED_SIZE) + .addGap(0, 0, Short.MAX_VALUE))) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED).addComponent(jButton1))); + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); + getContentPane().setLayout(layout); + layout.setHorizontalGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup().addContainerGap() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(jPanel1, javax.swing.GroupLayout.DEFAULT_SIZE, + javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(jPanel2, javax.swing.GroupLayout.DEFAULT_SIZE, + javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addContainerGap())); + layout.setVerticalGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup().addContainerGap() + .addComponent(jPanel1, javax.swing.GroupLayout.PREFERRED_SIZE, + javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(jPanel2, javax.swing.GroupLayout.DEFAULT_SIZE, + javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addContainerGap())); + + pack(); + }// //GEN-END:initComponents + + private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {// GEN-FIRST:event_jButton1ActionPerformed + this.jTextArea1.setText(""); + this.jTextArea2.setText(""); + }// GEN-LAST:event_jButton1ActionPerformed + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JButton jButton1; + private javax.swing.JLabel jLabel1; + private javax.swing.JLabel jLabel2; + private javax.swing.JLabel jLabel3; + private javax.swing.JPanel jPanel1; + private javax.swing.JPanel jPanel2; + private javax.swing.JScrollPane jScrollPane1; + private javax.swing.JScrollPane jScrollPane2; + private javax.swing.JTextArea jTextArea1; + private javax.swing.JTextArea jTextArea2; + private javax.swing.JTextField jTextField1; + // End of variables declaration//GEN-END:variables + @Override - public void updateTasks(String task) { - infoTextArea.append("\n" + task); - infoTextArea.append("\n**************************************************************"); - mainFrame.repaint(); - mainFrame.revalidate(); - } + public void updateClockinUi(String clock) { + this.jTextField1.setText(clock); + this.repaint(); + this.revalidate(); - public static void main(String args[]) { - WorkerHelper manager = new WorkerHelper(); - WorkerProcess snapshotUi = new WorkerProcess(manager); - snapshotUi.initUi(); - Thread S = new Thread(manager); - S.start(); } + } \ No newline at end of file diff --git a/Dream2/src/examples/java/dream/examples/util/VectorClock.java b/Dream2/src/examples/java/dream/examples/util/VectorClock.java new file mode 100644 index 0000000..d762874 --- /dev/null +++ b/Dream2/src/examples/java/dream/examples/util/VectorClock.java @@ -0,0 +1,124 @@ +/** + * + */ +package dream.examples.util; + +import java.util.HashMap; +import java.util.Set; + +/** + * @author Ram + * + */ +public class VectorClock { + + HashMap localClock; + String processId; + + public VectorClock(String processId) { + this.processId = processId; + } + + /** + * @return the localClock + */ + public HashMap getLocalClock() { + checkNull(); + return localClock; + } + + void checkNull() { + if (localClock == null) { + localClock = new HashMap<>(); + localClock.put(processId, 0); + } + } + + /** + * Method to be used to compare two clock vectors, true will be returned if + * both clocks have same keyset and have same values for individual keys. + * Usually used to compare if the arrived message is older than the local + * state. + * + * @param localClock + * @param messageClock + * @return + */ + public boolean isEqual(HashMap messageClock) { + checkNull(); + // clocks are not same if their lengths are not same + if (localClock.keySet().size() != messageClock.keySet().size()) { + return false; + } + // if length are same then we will check if both maps have same value + // for same keys. If there are different keys then null will be returned + // to compare statement which makes if statement true. + + for (String key : localClock.keySet()) { + if (!localClock.get(key).equals(messageClock.get(key))) { + return false; + } + } + // Keys are similar, values are same. + return true; + + } + + public boolean isNew(HashMap messageClock) { + checkNull(); + Set localKeyList = localClock.keySet(); + for (String key : messageClock.keySet()) { + + if (localKeyList.contains(key)) { + if (messageClock.get(key) > localClock.get(key)) { + return true; + } + } else { + return true; + } + } + return false; + } + + /** + * Method invoked by local process to update the clock after execution of an + * instruction which has global effect Ex: Sending of message or receiving + * of message. processId should be provided as an input key and local clock + * vector is provided as localClock. + * + * @param localClock + * @param key + * @return + */ + + public HashMap updateClock() { + checkNull(); + localClock.put(processId, localClock.get(processId) + 1); + return localClock; + + } + + /** + * @param localClock + * @param messageClock + * @return + */ + public void updateClock(HashMap messageClock) { + checkNull(); + Set localKeyList = localClock.keySet(); + for (String key : messageClock.keySet()) { + if (localKeyList.contains(key)) { + if (messageClock.get(key) > localClock.get(key)) { + localClock.put(key, messageClock.get(key)); + } + } else { + localClock.put(key, messageClock.get(key)); + } + } + } + + public Integer getTimeStamp() { + checkNull(); + return localClock.get(processId); + } +} From 669fbcf721977b8cba1e9e741ab07587a36cb430 Mon Sep 17 00:00:00 2001 From: Tobias Becker Date: Thu, 9 Jun 2016 18:28:01 +0200 Subject: [PATCH 117/161] form: added some notes and todos --- .../form/complete_glitchfree/Boss.java | 3 ++ .../form/complete_glitchfree/Secretary.java | 28 ++++++++++++++++++- .../dream/examples/form/package-info.java | 10 ++----- 3 files changed, 33 insertions(+), 8 deletions(-) diff --git a/Dream2/src/examples/java/dream/examples/form/complete_glitchfree/Boss.java b/Dream2/src/examples/java/dream/examples/form/complete_glitchfree/Boss.java index 0e21d88..097f7ea 100644 --- a/Dream2/src/examples/java/dream/examples/form/complete_glitchfree/Boss.java +++ b/Dream2/src/examples/java/dream/examples/form/complete_glitchfree/Boss.java @@ -29,16 +29,19 @@ public void typedText(int i, String typedText) { switch (i) { case 0: Double value = Double.valueOf(typedText); + // TODO check locks eph.set(value); logger.fine("Set Euro_Per_Hour to " + value); break; case 1: Integer value2 = Integer.valueOf(typedText); + // TODO check locks rh.set(new Pair<>(value2, rh.get().getSecond())); logger.fine("Set minimum @ Required_Hours to " + value2); break; case 2: Integer value3 = Integer.valueOf(typedText); + // TODO check locks rh.set(new Pair<>(rh.get().getFirst(), value3)); logger.fine("Set maximum @ Required_Hours to " + value3); break; diff --git a/Dream2/src/examples/java/dream/examples/form/complete_glitchfree/Secretary.java b/Dream2/src/examples/java/dream/examples/form/complete_glitchfree/Secretary.java index d007cb4..259b9b5 100644 --- a/Dream2/src/examples/java/dream/examples/form/complete_glitchfree/Secretary.java +++ b/Dream2/src/examples/java/dream/examples/form/complete_glitchfree/Secretary.java @@ -1,6 +1,32 @@ package dream.examples.form.complete_glitchfree; -public class Secretary extends dream.examples.form.simple.Secretary { +import dream.client.Var; +import dream.examples.form.core.FormClient; + +public class Secretary extends FormClient { + + public static final String NAME = "Secretary"; + public static final String WorkingHours = "working_hours"; + + private Var wh; + + public Secretary() { + super(NAME, "Working Hours"); + setInitValues(Integer.toString(5)); + } + + @Override + protected void init() { + wh = new Var<>(WorkingHours, 5); + } + + @Override + public void typedText(int i, String typedText) { + Integer value = Integer.valueOf(typedText); + // TODO check locks + wh.set(value); + logger.fine("Set Working_Hours to " + value); + } public static void main(String[] args) { Secretary s = new Secretary(); diff --git a/Dream2/src/examples/java/dream/examples/form/package-info.java b/Dream2/src/examples/java/dream/examples/form/package-info.java index 2ab6845..14f2b0c 100644 --- a/Dream2/src/examples/java/dream/examples/form/package-info.java +++ b/Dream2/src/examples/java/dream/examples/form/package-info.java @@ -8,14 +8,10 @@ * which ensures itself that no glitch can occur.
*
* b) {@link dream.examples.form.complete_glitchfree complete} uses a different - * graph, which requires a locking mechanism. Again there are two different - * solutions:
- * {@link dream.examples.form.complete_glitchfree.FormServer FormServer} - * depending on Dream's complete_glitch_free consistency to solve the - * glitch and
+ * graph, which requires a locking mechanism. * {@link dream.examples.form.complete_glitchfree.CompleteGlitchFreeFormServer - * CompleteGlitchFreeFormServer}, which ensures itself with a own locking - * mechanism that no glitch can occur.
+ * CompleteGlitchFreeFormServer}, which ensures complete_glitch_freedom itself + * with a own locking mechanism.
*
*
* Both examples contain an image of their used dependency graph From 0c0a21f8184a7728b3126c89c505d9093667e200 Mon Sep 17 00:00:00 2001 From: rkamath3 Date: Mon, 20 Jun 2016 01:58:44 +0200 Subject: [PATCH 118/161] Implemented consistent message receiver --- Dream2/.project | 6 + .../dream/examples/tasks/DeligatProcess.java | 1 + .../dream/examples/tasks/MasterProcess.java | 3 +- .../java/dream/examples/tasks/Message.java | 20 ++- .../java/dream/examples/tasks/Relation.java | 16 ++ .../examples/tasks/VectorClockHelper.java | 150 ++++++++++++++++++ .../dream/examples/tasks/WorkerHelper.java | 53 +++---- .../dream/examples/tasks/WorkerProcess.java | 6 +- .../java/dream/examples/util/Relation.java | 16 ++ .../dream/examples/util/StringToList.java | 38 +++++ .../java/dream/examples/util/VectorClock.java | 38 +++-- .../src/main/java/dream/client/RemoteVar.java | 4 +- Dream2/src/main/java/dream/client/Signal.java | 4 +- 13 files changed, 305 insertions(+), 50 deletions(-) create mode 100644 Dream2/src/examples/java/dream/examples/tasks/Relation.java create mode 100644 Dream2/src/examples/java/dream/examples/tasks/VectorClockHelper.java create mode 100644 Dream2/src/examples/java/dream/examples/util/Relation.java create mode 100644 Dream2/src/examples/java/dream/examples/util/StringToList.java diff --git a/Dream2/.project b/Dream2/.project index efc6b2e..0722981 100644 --- a/Dream2/.project +++ b/Dream2/.project @@ -15,10 +15,16 @@ + + com.stateofflow.eclipse.metrics.MetricsBuilder + + + org.eclipse.ajdt.ui.ajnature org.eclipse.jdt.core.javanature org.eclipse.m2e.core.maven2Nature + com.stateofflow.eclipse.metrics.MetricsNature diff --git a/Dream2/src/examples/java/dream/examples/tasks/DeligatProcess.java b/Dream2/src/examples/java/dream/examples/tasks/DeligatProcess.java index d56d0d0..ddab317 100644 --- a/Dream2/src/examples/java/dream/examples/tasks/DeligatProcess.java +++ b/Dream2/src/examples/java/dream/examples/tasks/DeligatProcess.java @@ -72,6 +72,7 @@ private void init() { e.printStackTrace(); } val.getTask().setAssignee(i++ % 10 + ""); + val.setId("p2"); localClock.updateClock(); val.setClock(localClock.getLocalClock()); myVar.set(val); diff --git a/Dream2/src/examples/java/dream/examples/tasks/MasterProcess.java b/Dream2/src/examples/java/dream/examples/tasks/MasterProcess.java index ef2ad4d..d6b3c98 100644 --- a/Dream2/src/examples/java/dream/examples/tasks/MasterProcess.java +++ b/Dream2/src/examples/java/dream/examples/tasks/MasterProcess.java @@ -21,10 +21,11 @@ private void init() { Var initTask2 = new Var("TASK2", null); try { - int i = 0; + int i = 1; while (true) { // Create a message to be distributed Message message = new Message(); + message.setId("p1"); Thread.sleep(5000); // Add task to a message diff --git a/Dream2/src/examples/java/dream/examples/tasks/Message.java b/Dream2/src/examples/java/dream/examples/tasks/Message.java index 200da3e..b745cb9 100644 --- a/Dream2/src/examples/java/dream/examples/tasks/Message.java +++ b/Dream2/src/examples/java/dream/examples/tasks/Message.java @@ -15,8 +15,24 @@ public class Message implements Serializable { private static final long serialVersionUID = -9119849212879479791L; - Task task; - HashMap clock; + private Task task; + private HashMap clock; + private String id; + + /** + * @return the id + */ + public String getId() { + return id; + } + + /** + * @param id + * the id to set + */ + public void setId(String id) { + this.id = id; + } /** * @return the task diff --git a/Dream2/src/examples/java/dream/examples/tasks/Relation.java b/Dream2/src/examples/java/dream/examples/tasks/Relation.java new file mode 100644 index 0000000..df76c92 --- /dev/null +++ b/Dream2/src/examples/java/dream/examples/tasks/Relation.java @@ -0,0 +1,16 @@ +/** + * + */ +package dream.examples.tasks; + +/** + * @author Ram + * + */ +public enum Relation { + FOLLOWEDBY, OR, AND +} + +enum Clock { + EQUAL, OLD, NEW, QUEUE +} \ No newline at end of file diff --git a/Dream2/src/examples/java/dream/examples/tasks/VectorClockHelper.java b/Dream2/src/examples/java/dream/examples/tasks/VectorClockHelper.java new file mode 100644 index 0000000..19867b4 --- /dev/null +++ b/Dream2/src/examples/java/dream/examples/tasks/VectorClockHelper.java @@ -0,0 +1,150 @@ +/** + * + */ +package dream.examples.tasks; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; + +/** + * @author Ram + * + */ +public class VectorClockHelper implements Runnable { + + HashMap localClock; + String processId; + private WorkerHelper helper; + + public VectorClockHelper(String processId, WorkerHelper helper) { + this.processId = processId; + this.helper = helper; + } + + /** + * @return the localClock + */ + public HashMap getLocalClock() { + checkNull(); + return localClock; + } + + void checkNull() { + if (localClock == null) { + localClock = new HashMap<>(); + localClock.put(processId, 0); + } + } + + java.util.List queue1 = Collections.synchronizedList(new ArrayList()); + java.util.List queue2 = Collections.synchronizedList(new ArrayList()); + + synchronized void checkEvent(Message msg) { + + if (newEvent(msg) == Clock.NEW && msg.getId().equals("p1")) { + updateClock(msg.getId(), msg.getClock().get(msg.getId())); + helper.updateClock(); + queueEvent(msg, true); + + } + if (newEvent(msg) == Clock.QUEUE && msg.getId().equals("p2")) { + updateClock(msg.getId(), msg.getClock().get(msg.getId())); + helper.updateClock(); + queueEvent(msg, false); + } + + } + + private synchronized void queueEvent(Message msg, boolean newEvent) { + + if (newEvent) { + helper.listner.updateTasks( + "ENQUEUE to Q2 " + msg.getTask().getId() + " Message is from process " + msg.getId(), false); + queue2.add(msg); + } else { + helper.listner.updateTasks( + "ENQUEUE to Q1 " + msg.getTask().getId() + " Message is from process " + msg.getId(), false); + queue1.add(msg); + } + } + + private synchronized void composeEvent(Message msg) { + + } + + void updateClock() { + checkNull(); + int clock = localClock.get(processId); + clock++; + updateClock(processId, clock); + + } + + private void updateClock(String id, int clock) { + + localClock.put(id, clock); + + } + + private Clock newEvent(Message msg) { + int flag = 0; + checkNull(); + for (String key : localClock.keySet()) { + if (msg.getClock().keySet().contains(key)) { + if (localClock.get(key) <= msg.getClock().get(key) && !key.equals(msg.getId())) { + return Clock.QUEUE; + } + if (localClock.get(key) >= msg.getClock().get(key) && !key.equals(msg.getId())) { + flag = 1; + } + } else { + return Clock.NEW; + } + } + if (flag == 1 && localClock.keySet().contains(msg.getId()) + && localClock.get(msg.getId()) < msg.getClock().get(msg.getId())) { + return Clock.NEW; + } + return Clock.QUEUE; + } + + @Override + public void run() { + while (true) { + helper.updateClock(); + int flag = 0; + Iterator iterator2 = queue2.iterator(); + while (iterator2.hasNext()) { + Message msg = iterator2.next(); + Iterator iterator = queue1.iterator(); + while (iterator.hasNext()) { + Message qMsg = iterator.next(); + if (msg.getId().equals("p1") && qMsg.getId().equals("p2") + && msg.getClock().get(msg.getId()).equals(qMsg.getClock().get(msg.getId()))) { + helper.handleEvent(msg, qMsg); + helper.listner.updateTasks("Event detected with timestamp :" + msg.getClock().get(msg.getId()), + false); + flag = 1; + helper.listner.updateTasks("DQUEUE msg :" + qMsg.getTask().getId(), false); + iterator.remove(); + + } + + } + if (flag == 0) { + queue1.add(msg); + updateClock(msg.getId(), msg.getClock().get(msg.getId())); + } + } + try { + Thread.sleep(1000); + } catch (Exception e) { + e.printStackTrace(); + } + } + + } + +} diff --git a/Dream2/src/examples/java/dream/examples/tasks/WorkerHelper.java b/Dream2/src/examples/java/dream/examples/tasks/WorkerHelper.java index b9408b7..3d6d2de 100644 --- a/Dream2/src/examples/java/dream/examples/tasks/WorkerHelper.java +++ b/Dream2/src/examples/java/dream/examples/tasks/WorkerHelper.java @@ -7,7 +7,6 @@ import dream.client.Signal; import dream.client.Var; import dream.common.Consts; -import dream.examples.util.VectorClock; /** * @author Ram @@ -16,7 +15,7 @@ public class WorkerHelper implements Runnable { static int i = 0; UiUpdatesListner listner; - VectorClock localClock = new VectorClock("p3"); + VectorClockHelper localClock;// = new VectorClock("p3"); Var complexEvent = new Var("COMPEVENT", null); /** * @param args @@ -38,41 +37,35 @@ public void setProcessName(String processName) { this.processName = processName; } - public WorkerHelper(String processName, String host) { + public WorkerHelper() { this.setProcessName(processName); - } - public WorkerHelper() { - // TODO Auto-generated constructor stub } - public void isEvent(Message val) { + public void handleEvent(Message p1, Message p2) { // update clock on receiving new message - localClock.updateClock(); - // check if the received message is new - if (localClock.isNew(val.getClock())) { - // update clock with the help of new message - localClock.updateClock(val.getClock()); - Task task = val.getTask(); - listner.updateTasks("\nTask Name: " + task.getName() + "\n" + "Task Discription: " + task.getDescription() - + "\n" + "Assignee ID: " + task.getAssignee(), true); - System.out.println("New Message and Accepted \n" + "Task Name: " + task.getName() + "\n" - + "Task Discription: " + task.getDescription() + "\n" + "Assignee ID: " + task.getAssignee()); - - } else { - Task task = val.getTask(); - listner.updateTasks("\nTask Name: " + task.getName() + "\n" + "Task Discription: " + task.getDescription() - + "\n" + "Assignee ID: " + task.getAssignee(), false); - System.out.println("New Message and Rejected \n" + "Task Name: " + task.getName() + "\n" - + "Task Discription: " + task.getDescription() + "\n" + "Assignee ID: " + task.getAssignee()); - } + Task task = p1.getTask(); + updateClock(); + listner.updateTasks("\nTask Name: " + task.getName() + "\n" + "Task Discription: " + task.getDescription() + + "\n" + "Assignee ID: " + p2.getTask().getAssignee(), true); + + } + + void updateClock() { listner.updateClockinUi(localClock.getLocalClock().toString()); } - public void run() { + public void isEvent(String val) { + updateClock(); + listner.updateTasks(val, false); - Consts.hostName = "Host3"; + } + public void run() { + localClock = new VectorClockHelper("p3", this); + Consts.hostName = "Host3"; + Thread t = new Thread(localClock); + t.start(); RemoteVar task = new RemoteVar("Host1", "TASK2"); RemoteVar taskDeligated = new RemoteVar("Host2", "TASK_ASSIGNED"); @@ -87,7 +80,8 @@ public void run() { // from master process signalFromMaster.change().addHandler((oldVal, val) -> { if (val != null) { - isEvent(val); + localClock.updateClock(); + localClock.checkEvent(val); } }); @@ -95,7 +89,8 @@ public void run() { // from delegate process signalFromDeligator.change().addHandler((oldVal, val) -> { if (val != null) { - isEvent(val); + localClock.updateClock(); + localClock.checkEvent(val); } }); diff --git a/Dream2/src/examples/java/dream/examples/tasks/WorkerProcess.java b/Dream2/src/examples/java/dream/examples/tasks/WorkerProcess.java index 49cb8aa..3faf971 100644 --- a/Dream2/src/examples/java/dream/examples/tasks/WorkerProcess.java +++ b/Dream2/src/examples/java/dream/examples/tasks/WorkerProcess.java @@ -15,6 +15,10 @@ */ public class WorkerProcess extends javax.swing.JFrame implements UiUpdatesListner { + /** + * + */ + private static final long serialVersionUID = 1L; /** * Creates new form TaskWindow */ @@ -120,7 +124,7 @@ private void initComponents() { jLabel2.setText("Accepted Events"); - jLabel3.setText("Rejected Events"); + jLabel3.setText("Log"); jButton1.setText("Clear"); jButton1.addActionListener(new java.awt.event.ActionListener() { diff --git a/Dream2/src/examples/java/dream/examples/util/Relation.java b/Dream2/src/examples/java/dream/examples/util/Relation.java new file mode 100644 index 0000000..529d52e --- /dev/null +++ b/Dream2/src/examples/java/dream/examples/util/Relation.java @@ -0,0 +1,16 @@ +/** + * + */ +package dream.examples.util; + +/** + * @author Ram + * + */ +public enum Relation { + FOLLOWEDBY, OR, AND +} + +enum Clock { + EQUAL, OLD, NEW +} \ No newline at end of file diff --git a/Dream2/src/examples/java/dream/examples/util/StringToList.java b/Dream2/src/examples/java/dream/examples/util/StringToList.java new file mode 100644 index 0000000..1fa962e --- /dev/null +++ b/Dream2/src/examples/java/dream/examples/util/StringToList.java @@ -0,0 +1,38 @@ +/** + * + */ +package dream.examples.util; + +import java.util.HashMap; + +/** + * @author Ram + * + */ +public class StringToList { + + /** + * @param args + */ + public static void main(String[] args) { + String input = "p1:2@p2:3"; + String seperator = "@"; + StringToList.getClockFromString(input, seperator); + + } + + public static HashMap getClockFromString(String inputString, String seperator) { + HashMap clock = new HashMap<>(); + + String[] tokens = inputString.split(seperator); + int tokenCount = tokens.length; + for (int j = 0; j < tokenCount; j++) { + String kString = tokens[j].substring(0, tokens[j].indexOf(':')); + Integer value = new Integer( + Integer.parseInt(tokens[j].substring(tokens[j].indexOf(':') + 1, tokens[j].length()))); + System.out.println(tokens[j] + " " + kString + " " + value); + } + return clock; + + } +} diff --git a/Dream2/src/examples/java/dream/examples/util/VectorClock.java b/Dream2/src/examples/java/dream/examples/util/VectorClock.java index d762874..37d58ed 100644 --- a/Dream2/src/examples/java/dream/examples/util/VectorClock.java +++ b/Dream2/src/examples/java/dream/examples/util/VectorClock.java @@ -44,23 +44,35 @@ void checkNull() { * @param messageClock * @return */ - public boolean isEqual(HashMap messageClock) { + public Clock compareClock(HashMap messageClock) { checkNull(); - // clocks are not same if their lengths are not same - if (localClock.keySet().size() != messageClock.keySet().size()) { - return false; - } - // if length are same then we will check if both maps have same value - // for same keys. If there are different keys then null will be returned - // to compare statement which makes if statement true. + if (localClock.keySet().containsAll(messageClock.keySet()) + && localClock.keySet().size() == messageClock.keySet().size()) { + for (String key : localClock.keySet()) { + if (!localClock.get(key).equals(messageClock.get(key)) && localClock.get(key) > messageClock.get(key)) { + return Clock.OLD; + } + if (!localClock.get(key).equals(messageClock.get(key)) && localClock.get(key) < messageClock.get(key)) { + return Clock.NEW; + } + } + return Clock.EQUAL; - for (String key : localClock.keySet()) { - if (!localClock.get(key).equals(messageClock.get(key))) { - return false; + } else if (localClock.keySet().containsAll(messageClock.keySet()) + && localClock.keySet().size() > messageClock.keySet().size()) { + for (String key : messageClock.keySet()) { + if (!localClock.get(key).equals(messageClock.get(key)) && localClock.get(key) > messageClock.get(key)) { + return Clock.NEW; + } + if (!localClock.get(key).equals(messageClock.get(key)) && localClock.get(key) < messageClock.get(key)) { + return Clock.OLD; + } } + return Clock.EQUAL; + } - // Keys are similar, values are same. - return true; + + return null; } diff --git a/Dream2/src/main/java/dream/client/RemoteVar.java b/Dream2/src/main/java/dream/client/RemoteVar.java index 3f3a52c..6e87851 100755 --- a/Dream2/src/main/java/dream/client/RemoteVar.java +++ b/Dream2/src/main/java/dream/client/RemoteVar.java @@ -60,9 +60,9 @@ public final synchronized T get() { @Override public synchronized void notifyEventReceived(EventPacket evPkt) { eventsQueue.add(evPkt); - logger.finest("Received event packet " + evPkt + ". Added to the queue."); + logger.finest("Received event packet " + evPkt + ". Added to the queue1."); if (eventsQueue.size() == 1) { - logger.finest("The element is the only one in the queue. Let's process it."); + logger.finest("The element is the only one in the queue1. Let's process it."); processNextEvent(); } } diff --git a/Dream2/src/main/java/dream/client/Signal.java b/Dream2/src/main/java/dream/client/Signal.java index 75e1c38..02f7618 100755 --- a/Dream2/src/main/java/dream/client/Signal.java +++ b/Dream2/src/main/java/dream/client/Signal.java @@ -157,9 +157,9 @@ public UpdateProducer filter(SerializablePredicate constraint) { public final synchronized void updateFromProducer(EventPacket packet, UpdateProducer producer) { final EventProducerPair pair = new EventProducerPair(packet, producer); eventQueue.add(pair); - logger.finest("Method update called for event " + pair + ". Added to the queue."); + logger.finest("Method update called for event " + pair + ". Added to the queue1."); if (eventQueue.size() == 1) { - logger.finest("The element is the only one in the queue. Let's process it."); + logger.finest("The element is the only one in the queue1. Let's process it."); processNextUpdate(); } } From 65104960312d6f47ac9538a35e40e2b2b9b8da45 Mon Sep 17 00:00:00 2001 From: rkamath3 Date: Mon, 20 Jun 2016 01:58:44 +0200 Subject: [PATCH 119/161] Implemented inconsistent message receiver --- Dream2/.project | 6 + .../dream/examples/tasks/DeligatProcess.java | 1 + .../dream/examples/tasks/MasterProcess.java | 3 +- .../java/dream/examples/tasks/Message.java | 20 +- .../java/dream/examples/tasks/Relation.java | 16 ++ .../examples/tasks/VectorClockHelper.java | 150 ++++++++++++ .../dream/examples/tasks/WorkerHelper.java | 53 ++--- .../dream/examples/tasks/WorkerProcess.java | 6 +- .../tasks/inconsistent/DeligatProcess.java | 88 +++++++ .../tasks/inconsistent/MasterProcess.java | 69 ++++++ .../examples/tasks/inconsistent/Message.java | 67 ++++++ .../examples/tasks/inconsistent/Task.java | 103 ++++++++ .../tasks/inconsistent/UiUpdatesListner.java | 15 ++ .../tasks/inconsistent/WorkerHelper.java | 93 ++++++++ .../tasks/inconsistent/WorkerProcess.java | 222 ++++++++++++++++++ .../java/dream/examples/util/Relation.java | 16 ++ .../dream/examples/util/StringToList.java | 38 +++ .../java/dream/examples/util/VectorClock.java | 38 ++- .../src/main/java/dream/client/RemoteVar.java | 4 +- Dream2/src/main/java/dream/client/Signal.java | 4 +- 20 files changed, 962 insertions(+), 50 deletions(-) create mode 100644 Dream2/src/examples/java/dream/examples/tasks/Relation.java create mode 100644 Dream2/src/examples/java/dream/examples/tasks/VectorClockHelper.java create mode 100644 Dream2/src/examples/java/dream/examples/tasks/inconsistent/DeligatProcess.java create mode 100644 Dream2/src/examples/java/dream/examples/tasks/inconsistent/MasterProcess.java create mode 100644 Dream2/src/examples/java/dream/examples/tasks/inconsistent/Message.java create mode 100644 Dream2/src/examples/java/dream/examples/tasks/inconsistent/Task.java create mode 100644 Dream2/src/examples/java/dream/examples/tasks/inconsistent/UiUpdatesListner.java create mode 100644 Dream2/src/examples/java/dream/examples/tasks/inconsistent/WorkerHelper.java create mode 100644 Dream2/src/examples/java/dream/examples/tasks/inconsistent/WorkerProcess.java create mode 100644 Dream2/src/examples/java/dream/examples/util/Relation.java create mode 100644 Dream2/src/examples/java/dream/examples/util/StringToList.java diff --git a/Dream2/.project b/Dream2/.project index efc6b2e..0722981 100644 --- a/Dream2/.project +++ b/Dream2/.project @@ -15,10 +15,16 @@ + + com.stateofflow.eclipse.metrics.MetricsBuilder + + + org.eclipse.ajdt.ui.ajnature org.eclipse.jdt.core.javanature org.eclipse.m2e.core.maven2Nature + com.stateofflow.eclipse.metrics.MetricsNature diff --git a/Dream2/src/examples/java/dream/examples/tasks/DeligatProcess.java b/Dream2/src/examples/java/dream/examples/tasks/DeligatProcess.java index d56d0d0..ddab317 100644 --- a/Dream2/src/examples/java/dream/examples/tasks/DeligatProcess.java +++ b/Dream2/src/examples/java/dream/examples/tasks/DeligatProcess.java @@ -72,6 +72,7 @@ private void init() { e.printStackTrace(); } val.getTask().setAssignee(i++ % 10 + ""); + val.setId("p2"); localClock.updateClock(); val.setClock(localClock.getLocalClock()); myVar.set(val); diff --git a/Dream2/src/examples/java/dream/examples/tasks/MasterProcess.java b/Dream2/src/examples/java/dream/examples/tasks/MasterProcess.java index ef2ad4d..d6b3c98 100644 --- a/Dream2/src/examples/java/dream/examples/tasks/MasterProcess.java +++ b/Dream2/src/examples/java/dream/examples/tasks/MasterProcess.java @@ -21,10 +21,11 @@ private void init() { Var initTask2 = new Var("TASK2", null); try { - int i = 0; + int i = 1; while (true) { // Create a message to be distributed Message message = new Message(); + message.setId("p1"); Thread.sleep(5000); // Add task to a message diff --git a/Dream2/src/examples/java/dream/examples/tasks/Message.java b/Dream2/src/examples/java/dream/examples/tasks/Message.java index 200da3e..b745cb9 100644 --- a/Dream2/src/examples/java/dream/examples/tasks/Message.java +++ b/Dream2/src/examples/java/dream/examples/tasks/Message.java @@ -15,8 +15,24 @@ public class Message implements Serializable { private static final long serialVersionUID = -9119849212879479791L; - Task task; - HashMap clock; + private Task task; + private HashMap clock; + private String id; + + /** + * @return the id + */ + public String getId() { + return id; + } + + /** + * @param id + * the id to set + */ + public void setId(String id) { + this.id = id; + } /** * @return the task diff --git a/Dream2/src/examples/java/dream/examples/tasks/Relation.java b/Dream2/src/examples/java/dream/examples/tasks/Relation.java new file mode 100644 index 0000000..df76c92 --- /dev/null +++ b/Dream2/src/examples/java/dream/examples/tasks/Relation.java @@ -0,0 +1,16 @@ +/** + * + */ +package dream.examples.tasks; + +/** + * @author Ram + * + */ +public enum Relation { + FOLLOWEDBY, OR, AND +} + +enum Clock { + EQUAL, OLD, NEW, QUEUE +} \ No newline at end of file diff --git a/Dream2/src/examples/java/dream/examples/tasks/VectorClockHelper.java b/Dream2/src/examples/java/dream/examples/tasks/VectorClockHelper.java new file mode 100644 index 0000000..19867b4 --- /dev/null +++ b/Dream2/src/examples/java/dream/examples/tasks/VectorClockHelper.java @@ -0,0 +1,150 @@ +/** + * + */ +package dream.examples.tasks; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; + +/** + * @author Ram + * + */ +public class VectorClockHelper implements Runnable { + + HashMap localClock; + String processId; + private WorkerHelper helper; + + public VectorClockHelper(String processId, WorkerHelper helper) { + this.processId = processId; + this.helper = helper; + } + + /** + * @return the localClock + */ + public HashMap getLocalClock() { + checkNull(); + return localClock; + } + + void checkNull() { + if (localClock == null) { + localClock = new HashMap<>(); + localClock.put(processId, 0); + } + } + + java.util.List queue1 = Collections.synchronizedList(new ArrayList()); + java.util.List queue2 = Collections.synchronizedList(new ArrayList()); + + synchronized void checkEvent(Message msg) { + + if (newEvent(msg) == Clock.NEW && msg.getId().equals("p1")) { + updateClock(msg.getId(), msg.getClock().get(msg.getId())); + helper.updateClock(); + queueEvent(msg, true); + + } + if (newEvent(msg) == Clock.QUEUE && msg.getId().equals("p2")) { + updateClock(msg.getId(), msg.getClock().get(msg.getId())); + helper.updateClock(); + queueEvent(msg, false); + } + + } + + private synchronized void queueEvent(Message msg, boolean newEvent) { + + if (newEvent) { + helper.listner.updateTasks( + "ENQUEUE to Q2 " + msg.getTask().getId() + " Message is from process " + msg.getId(), false); + queue2.add(msg); + } else { + helper.listner.updateTasks( + "ENQUEUE to Q1 " + msg.getTask().getId() + " Message is from process " + msg.getId(), false); + queue1.add(msg); + } + } + + private synchronized void composeEvent(Message msg) { + + } + + void updateClock() { + checkNull(); + int clock = localClock.get(processId); + clock++; + updateClock(processId, clock); + + } + + private void updateClock(String id, int clock) { + + localClock.put(id, clock); + + } + + private Clock newEvent(Message msg) { + int flag = 0; + checkNull(); + for (String key : localClock.keySet()) { + if (msg.getClock().keySet().contains(key)) { + if (localClock.get(key) <= msg.getClock().get(key) && !key.equals(msg.getId())) { + return Clock.QUEUE; + } + if (localClock.get(key) >= msg.getClock().get(key) && !key.equals(msg.getId())) { + flag = 1; + } + } else { + return Clock.NEW; + } + } + if (flag == 1 && localClock.keySet().contains(msg.getId()) + && localClock.get(msg.getId()) < msg.getClock().get(msg.getId())) { + return Clock.NEW; + } + return Clock.QUEUE; + } + + @Override + public void run() { + while (true) { + helper.updateClock(); + int flag = 0; + Iterator iterator2 = queue2.iterator(); + while (iterator2.hasNext()) { + Message msg = iterator2.next(); + Iterator iterator = queue1.iterator(); + while (iterator.hasNext()) { + Message qMsg = iterator.next(); + if (msg.getId().equals("p1") && qMsg.getId().equals("p2") + && msg.getClock().get(msg.getId()).equals(qMsg.getClock().get(msg.getId()))) { + helper.handleEvent(msg, qMsg); + helper.listner.updateTasks("Event detected with timestamp :" + msg.getClock().get(msg.getId()), + false); + flag = 1; + helper.listner.updateTasks("DQUEUE msg :" + qMsg.getTask().getId(), false); + iterator.remove(); + + } + + } + if (flag == 0) { + queue1.add(msg); + updateClock(msg.getId(), msg.getClock().get(msg.getId())); + } + } + try { + Thread.sleep(1000); + } catch (Exception e) { + e.printStackTrace(); + } + } + + } + +} diff --git a/Dream2/src/examples/java/dream/examples/tasks/WorkerHelper.java b/Dream2/src/examples/java/dream/examples/tasks/WorkerHelper.java index b9408b7..3d6d2de 100644 --- a/Dream2/src/examples/java/dream/examples/tasks/WorkerHelper.java +++ b/Dream2/src/examples/java/dream/examples/tasks/WorkerHelper.java @@ -7,7 +7,6 @@ import dream.client.Signal; import dream.client.Var; import dream.common.Consts; -import dream.examples.util.VectorClock; /** * @author Ram @@ -16,7 +15,7 @@ public class WorkerHelper implements Runnable { static int i = 0; UiUpdatesListner listner; - VectorClock localClock = new VectorClock("p3"); + VectorClockHelper localClock;// = new VectorClock("p3"); Var complexEvent = new Var("COMPEVENT", null); /** * @param args @@ -38,41 +37,35 @@ public void setProcessName(String processName) { this.processName = processName; } - public WorkerHelper(String processName, String host) { + public WorkerHelper() { this.setProcessName(processName); - } - public WorkerHelper() { - // TODO Auto-generated constructor stub } - public void isEvent(Message val) { + public void handleEvent(Message p1, Message p2) { // update clock on receiving new message - localClock.updateClock(); - // check if the received message is new - if (localClock.isNew(val.getClock())) { - // update clock with the help of new message - localClock.updateClock(val.getClock()); - Task task = val.getTask(); - listner.updateTasks("\nTask Name: " + task.getName() + "\n" + "Task Discription: " + task.getDescription() - + "\n" + "Assignee ID: " + task.getAssignee(), true); - System.out.println("New Message and Accepted \n" + "Task Name: " + task.getName() + "\n" - + "Task Discription: " + task.getDescription() + "\n" + "Assignee ID: " + task.getAssignee()); - - } else { - Task task = val.getTask(); - listner.updateTasks("\nTask Name: " + task.getName() + "\n" + "Task Discription: " + task.getDescription() - + "\n" + "Assignee ID: " + task.getAssignee(), false); - System.out.println("New Message and Rejected \n" + "Task Name: " + task.getName() + "\n" - + "Task Discription: " + task.getDescription() + "\n" + "Assignee ID: " + task.getAssignee()); - } + Task task = p1.getTask(); + updateClock(); + listner.updateTasks("\nTask Name: " + task.getName() + "\n" + "Task Discription: " + task.getDescription() + + "\n" + "Assignee ID: " + p2.getTask().getAssignee(), true); + + } + + void updateClock() { listner.updateClockinUi(localClock.getLocalClock().toString()); } - public void run() { + public void isEvent(String val) { + updateClock(); + listner.updateTasks(val, false); - Consts.hostName = "Host3"; + } + public void run() { + localClock = new VectorClockHelper("p3", this); + Consts.hostName = "Host3"; + Thread t = new Thread(localClock); + t.start(); RemoteVar task = new RemoteVar("Host1", "TASK2"); RemoteVar taskDeligated = new RemoteVar("Host2", "TASK_ASSIGNED"); @@ -87,7 +80,8 @@ public void run() { // from master process signalFromMaster.change().addHandler((oldVal, val) -> { if (val != null) { - isEvent(val); + localClock.updateClock(); + localClock.checkEvent(val); } }); @@ -95,7 +89,8 @@ public void run() { // from delegate process signalFromDeligator.change().addHandler((oldVal, val) -> { if (val != null) { - isEvent(val); + localClock.updateClock(); + localClock.checkEvent(val); } }); diff --git a/Dream2/src/examples/java/dream/examples/tasks/WorkerProcess.java b/Dream2/src/examples/java/dream/examples/tasks/WorkerProcess.java index 49cb8aa..3faf971 100644 --- a/Dream2/src/examples/java/dream/examples/tasks/WorkerProcess.java +++ b/Dream2/src/examples/java/dream/examples/tasks/WorkerProcess.java @@ -15,6 +15,10 @@ */ public class WorkerProcess extends javax.swing.JFrame implements UiUpdatesListner { + /** + * + */ + private static final long serialVersionUID = 1L; /** * Creates new form TaskWindow */ @@ -120,7 +124,7 @@ private void initComponents() { jLabel2.setText("Accepted Events"); - jLabel3.setText("Rejected Events"); + jLabel3.setText("Log"); jButton1.setText("Clear"); jButton1.addActionListener(new java.awt.event.ActionListener() { diff --git a/Dream2/src/examples/java/dream/examples/tasks/inconsistent/DeligatProcess.java b/Dream2/src/examples/java/dream/examples/tasks/inconsistent/DeligatProcess.java new file mode 100644 index 0000000..a2c7610 --- /dev/null +++ b/Dream2/src/examples/java/dream/examples/tasks/inconsistent/DeligatProcess.java @@ -0,0 +1,88 @@ +/** + * + */ +package dream.examples.tasks.inconsistent; + +import java.util.HashMap; + +import dream.client.RemoteVar; +import dream.client.Signal; +import dream.client.Var; +import dream.common.Consts; +import dream.examples.util.VectorClock; + +/** + * @author Ram + * + */ +public class DeligatProcess { + + static int i; + int clock; + VectorClock localClock = new VectorClock("p2"); + /** + * @param args + */ + private String processName; + + /** + * @return the processName + */ + public String getProcessName() { + return processName; + } + + /** + * @param processName + * the processName to set + */ + public void setProcessName(String processName) { + this.processName = processName; + } + + public DeligatProcess(String processName, String host) { + this.setProcessName(processName); + } + + public DeligatProcess() { + // TODO Auto-generated constructor stub + } + + private void init() { + Consts.hostName = "Host2"; + RemoteVar rv = new RemoteVar("Host1", "TASK"); + Var myVar = new Var("TASK_ASSIGNED", null); + + Signal s = new Signal("s", () -> { + return rv.get(); + } , rv); + + // Register a handler which will be executed upon receiving the signal + s.change().addHandler((oldVal, val) -> { + + localClock.updateClock(); + HashMap messageClock = val.getClock(); + if (localClock.isNew(messageClock)) { + localClock.updateClock(messageClock); + } + try { + Thread.sleep(1000); + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + val.getTask().setAssignee(i++ % 10 + ""); + val.setId("p2"); + localClock.updateClock(); + val.setClock(localClock.getLocalClock()); + myVar.set(val); + + }); + } + + public static void main(String[] args) { + new DeligatProcess().init(); + + } + +} diff --git a/Dream2/src/examples/java/dream/examples/tasks/inconsistent/MasterProcess.java b/Dream2/src/examples/java/dream/examples/tasks/inconsistent/MasterProcess.java new file mode 100644 index 0000000..3988855 --- /dev/null +++ b/Dream2/src/examples/java/dream/examples/tasks/inconsistent/MasterProcess.java @@ -0,0 +1,69 @@ +/** + * + */ +package dream.examples.tasks.inconsistent; + +import dream.client.Var; +import dream.common.Consts; +import dream.examples.util.VectorClock; + +/** + * @author Ram + * + */ +public class MasterProcess { + + private void init() { + Consts.hostName = "Host1"; + + VectorClock vectorClock = new VectorClock("p1"); + Var initTask = new Var("TASK", null); + Var initTask2 = new Var("TASK2", null); + try { + + int i = 1; + while (true) { + // Create a message to be distributed + Message message = new Message(); + message.setId("p1"); + Thread.sleep(5000); + + // Add task to a message + Task task = new Task("Task" + i); + + // Set id for the task + task.setId(1000 + i); + + message.setTask(task); + // increment local clock + vectorClock.updateClock(); + + // set clock for the task + message.setClock(vectorClock.getLocalClock()); + + // Add description to task + task.setDescription("This is " + i + "th task"); + + // Send task to Deligator + initTask.set(message); + + // link latency + Thread.sleep(5000); + // Send task to worker + initTask2.set(message); + i++; + } + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + /** + * @param args + */ + public static void main(String[] args) { + new MasterProcess().init(); + } + +} diff --git a/Dream2/src/examples/java/dream/examples/tasks/inconsistent/Message.java b/Dream2/src/examples/java/dream/examples/tasks/inconsistent/Message.java new file mode 100644 index 0000000..30f9896 --- /dev/null +++ b/Dream2/src/examples/java/dream/examples/tasks/inconsistent/Message.java @@ -0,0 +1,67 @@ +/** + * + */ +package dream.examples.tasks.inconsistent; + +import java.io.Serializable; +import java.util.HashMap; + +/** + * Message format used for communication between different nodes + * + * @author Ram + * + */ +public class Message implements Serializable { + + private static final long serialVersionUID = -9119849212879479791L; + private Task task; + private HashMap clock; + private String id; + + /** + * @return the id + */ + public String getId() { + return id; + } + + /** + * @param id + * the id to set + */ + public void setId(String id) { + this.id = id; + } + + /** + * @return the task + */ + public Task getTask() { + return task; + } + + /** + * @param task + * the task to set + */ + public void setTask(Task task) { + this.task = task; + } + + /** + * @return the clock + */ + public HashMap getClock() { + return clock; + } + + /** + * @param clock + * the clock to set + */ + public void setClock(HashMap clock) { + this.clock = clock; + } + +} diff --git a/Dream2/src/examples/java/dream/examples/tasks/inconsistent/Task.java b/Dream2/src/examples/java/dream/examples/tasks/inconsistent/Task.java new file mode 100644 index 0000000..f388d0a --- /dev/null +++ b/Dream2/src/examples/java/dream/examples/tasks/inconsistent/Task.java @@ -0,0 +1,103 @@ +/** + * + */ +package dream.examples.tasks.inconsistent; + +import java.util.ArrayList; + +/** + * @author Ram + * + */ +public class Task extends ArrayList { + /** + * + */ + private static final long serialVersionUID = 7635579009380741902L; + private String name; + private String assignee; + private int id; + + /** + * @return the id + */ + public int getId() { + return id; + } + + /** + * @param id + * the id to set + */ + public void setId(int id) { + this.id = id; + } + + /** + * @return the clock + */ + public int getClock() { + return clock; + } + + /** + * @param clock + * the clock to set + */ + public void setClock(int clock) { + this.clock = clock; + } + + /** + * @return the description + */ + public String getDescription() { + return description; + } + + /** + * @param description + * the description to set + */ + public void setDescription(String description) { + this.description = description; + } + + private int clock; + private String description; + + public Task(String name) { + this.setName(name); + } + + /** + * @return the name + */ + public String getName() { + return name; + } + + /** + * @param name + * the name to set + */ + public void setName(String name) { + this.name = name; + } + + /** + * @return the assignee + */ + public String getAssignee() { + return assignee; + } + + /** + * @param assignee + * the assignee to set + */ + public void setAssignee(String assignee) { + this.assignee = assignee; + } + +} diff --git a/Dream2/src/examples/java/dream/examples/tasks/inconsistent/UiUpdatesListner.java b/Dream2/src/examples/java/dream/examples/tasks/inconsistent/UiUpdatesListner.java new file mode 100644 index 0000000..cdc0002 --- /dev/null +++ b/Dream2/src/examples/java/dream/examples/tasks/inconsistent/UiUpdatesListner.java @@ -0,0 +1,15 @@ +/** + * + */ +package dream.examples.tasks.inconsistent; + +/** + * @author Ram + * + */ +public interface UiUpdatesListner { + + void updateTasks(String task, boolean accepted); + + void updateClockinUi(String string); +} diff --git a/Dream2/src/examples/java/dream/examples/tasks/inconsistent/WorkerHelper.java b/Dream2/src/examples/java/dream/examples/tasks/inconsistent/WorkerHelper.java new file mode 100644 index 0000000..b78a7a2 --- /dev/null +++ b/Dream2/src/examples/java/dream/examples/tasks/inconsistent/WorkerHelper.java @@ -0,0 +1,93 @@ +/** + * + */ +package dream.examples.tasks.inconsistent; + +import dream.client.RemoteVar; +import dream.client.Signal; +import dream.client.Var; +import dream.common.Consts; + +/** + * @author Ram + * + */ +public class WorkerHelper implements Runnable { + static int i = 0; + UiUpdatesListner listner; + Var complexEvent = new Var("COMPEVENT", null); + /** + * @param args + */ + private String processName; + + /** + * @return the processName + */ + public String getProcessName() { + return processName; + } + + /** + * @param processName + * the processName to set + */ + public void setProcessName(String processName) { + this.processName = processName; + } + + public WorkerHelper() { + this.setProcessName(processName); + + } + + public void handleEvent(Message p1, Message p2) { + // update clock on receiving new message + Task task = p1.getTask(); + listner.updateTasks("\nTask Name: " + task.getName() + "\n" + "Task Discription: " + task.getDescription() + + "\n" + "Assignee ID: " + p1.getTask().getAssignee(), true); + + } + + public void isEvent(String val) { + listner.updateTasks(val, false); + + } + + public void run() { + + Consts.hostName = "Host3"; + + RemoteVar task = new RemoteVar("Host1", "TASK2"); + RemoteVar taskDeligated = new RemoteVar("Host2", "TASK_ASSIGNED"); + + Signal signalFromMaster = new Signal("s", () -> { + return task.get(); + } , task); + Signal signalFromDeligator = new Signal("s1", () -> { + return taskDeligated.get(); + } , taskDeligated); + + // Register a handler which will be executed upon receiving the signal + // from master process + signalFromMaster.change().addHandler((oldVal, val) -> { + if (val != null) { + handleEvent(val, null); + } + }); + + // Register a handler which will be executed upon receiving the signal + // from delegate process + signalFromDeligator.change().addHandler((oldVal, val) -> { + if (val != null) { + handleEvent(val, null); + } + }); + + } + + public void addListners(WorkerProcess dsUi) { + this.listner = dsUi; + } + +} diff --git a/Dream2/src/examples/java/dream/examples/tasks/inconsistent/WorkerProcess.java b/Dream2/src/examples/java/dream/examples/tasks/inconsistent/WorkerProcess.java new file mode 100644 index 0000000..2783d3b --- /dev/null +++ b/Dream2/src/examples/java/dream/examples/tasks/inconsistent/WorkerProcess.java @@ -0,0 +1,222 @@ +/** + * + */ +package dream.examples.tasks.inconsistent; + +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +/** + * + * @author Ram + */ +public class WorkerProcess extends javax.swing.JFrame implements UiUpdatesListner { + + /** + * + */ + private static final long serialVersionUID = 1L; + /** + * Creates new form TaskWindow + */ + WorkerHelper manager; + + public WorkerProcess(WorkerHelper manager2) { + this.manager = manager2; + this.manager.addListners(this); + initComponents(); + + } + + @Override + public void updateTasks(String task, boolean accepted) { + if (accepted) { + jTextArea1.append("\n" + task); + jTextArea1.append("\n**************************************************************"); + } else { + jTextArea2.append("\n" + task); + jTextArea2.append("\n**************************************************************"); + } + + this.repaint(); + this.revalidate(); + } + + public static void main(String args[]) { + WorkerHelper manager = new WorkerHelper(); + WorkerProcess snapshotUi = new WorkerProcess(manager); + snapshotUi.initUi(); + Thread S = new Thread(manager); + S.start(); + } + + void initUi() { + + try { + for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) { + if ("Nimbus".equals(info.getName())) { + javax.swing.UIManager.setLookAndFeel(info.getClassName()); + this.setVisible(true); + break; + } + } + } catch (ClassNotFoundException ex) { + java.util.logging.Logger.getLogger(WorkerProcess.class.getName()).log(java.util.logging.Level.SEVERE, null, + ex); + } catch (InstantiationException ex) { + java.util.logging.Logger.getLogger(WorkerProcess.class.getName()).log(java.util.logging.Level.SEVERE, null, + ex); + } catch (IllegalAccessException ex) { + java.util.logging.Logger.getLogger(WorkerProcess.class.getName()).log(java.util.logging.Level.SEVERE, null, + ex); + } catch (javax.swing.UnsupportedLookAndFeelException ex) { + java.util.logging.Logger.getLogger(WorkerProcess.class.getName()).log(java.util.logging.Level.SEVERE, null, + ex); + } + } + + private void initComponents() { + + jPanel1 = new javax.swing.JPanel(); + jLabel1 = new javax.swing.JLabel(); + jTextField1 = new javax.swing.JTextField(); + jPanel2 = new javax.swing.JPanel(); + jScrollPane1 = new javax.swing.JScrollPane(); + jTextArea1 = new javax.swing.JTextArea(); + jScrollPane2 = new javax.swing.JScrollPane(); + jTextArea2 = new javax.swing.JTextArea(); + jLabel2 = new javax.swing.JLabel(); + jLabel3 = new javax.swing.JLabel(); + jButton1 = new javax.swing.JButton(); + + setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE); + + jLabel1.setText("Local Clock"); + + jTextField1.setEditable(false); + jTextField1.setText("Local clock will be displayed here"); + + javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1); + jPanel1.setLayout(jPanel1Layout); + jPanel1Layout.setHorizontalGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jPanel1Layout.createSequentialGroup().addGap(67, 67, 67).addComponent(jLabel1) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED).addComponent(jTextField1) + .addContainerGap())); + jPanel1Layout.setVerticalGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jPanel1Layout.createSequentialGroup() + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(jLabel1).addComponent(jTextField1, javax.swing.GroupLayout.PREFERRED_SIZE, + javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addGap(0, 8, Short.MAX_VALUE))); + + jTextArea1.setEditable(false); + jTextArea1.setColumns(20); + jTextArea1.setRows(50); + jScrollPane1.setViewportView(jTextArea1); + + jTextArea2.setEditable(false); + jTextArea2.setColumns(20); + jTextArea2.setRows(50); + jScrollPane2.setViewportView(jTextArea2); + + jLabel2.setText("Accepted Events"); + + jLabel3.setText("Log"); + + jButton1.setText("Clear"); + jButton1.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + jButton1ActionPerformed(evt); + } + }); + + javax.swing.GroupLayout jPanel2Layout = new javax.swing.GroupLayout(jPanel2); + jPanel2.setLayout(jPanel2Layout); + jPanel2Layout.setHorizontalGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel2Layout.createSequentialGroup() + .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jPanel2Layout.createSequentialGroup().addGap(22, 22, 22).addComponent(jLabel2) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, + javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addGroup(jPanel2Layout.createSequentialGroup().addContainerGap() + .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 345, + Short.MAX_VALUE) + .addGap(18, 18, 18))) + .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, + jPanel2Layout.createSequentialGroup().addComponent(jLabel3).addGap(106, 106, + 106)) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, + jPanel2Layout.createSequentialGroup() + .addComponent(jScrollPane2, javax.swing.GroupLayout.PREFERRED_SIZE, 335, + javax.swing.GroupLayout.PREFERRED_SIZE) + .addContainerGap()))) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel2Layout.createSequentialGroup() + .addGap(0, 0, Short.MAX_VALUE).addComponent(jButton1).addGap(297, 297, 297))); + jPanel2Layout.setVerticalGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, + jPanel2Layout.createSequentialGroup().addContainerGap() + .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(jLabel3).addComponent(jLabel2)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(jScrollPane2, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE) + .addGroup(jPanel2Layout.createSequentialGroup() + .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 360, + javax.swing.GroupLayout.PREFERRED_SIZE) + .addGap(0, 0, Short.MAX_VALUE))) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED).addComponent(jButton1))); + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); + getContentPane().setLayout(layout); + layout.setHorizontalGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup().addContainerGap() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(jPanel1, javax.swing.GroupLayout.DEFAULT_SIZE, + javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(jPanel2, javax.swing.GroupLayout.DEFAULT_SIZE, + javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addContainerGap())); + layout.setVerticalGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup().addContainerGap() + .addComponent(jPanel1, javax.swing.GroupLayout.PREFERRED_SIZE, + javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(jPanel2, javax.swing.GroupLayout.DEFAULT_SIZE, + javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addContainerGap())); + + pack(); + }// //GEN-END:initComponents + + private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {// GEN-FIRST:event_jButton1ActionPerformed + this.jTextArea1.setText(""); + this.jTextArea2.setText(""); + }// GEN-LAST:event_jButton1ActionPerformed + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JButton jButton1; + private javax.swing.JLabel jLabel1; + private javax.swing.JLabel jLabel2; + private javax.swing.JLabel jLabel3; + private javax.swing.JPanel jPanel1; + private javax.swing.JPanel jPanel2; + private javax.swing.JScrollPane jScrollPane1; + private javax.swing.JScrollPane jScrollPane2; + private javax.swing.JTextArea jTextArea1; + private javax.swing.JTextArea jTextArea2; + private javax.swing.JTextField jTextField1; + // End of variables declaration//GEN-END:variables + + @Override + public void updateClockinUi(String clock) { + this.jTextField1.setText(clock); + this.repaint(); + this.revalidate(); + + } + +} \ No newline at end of file diff --git a/Dream2/src/examples/java/dream/examples/util/Relation.java b/Dream2/src/examples/java/dream/examples/util/Relation.java new file mode 100644 index 0000000..529d52e --- /dev/null +++ b/Dream2/src/examples/java/dream/examples/util/Relation.java @@ -0,0 +1,16 @@ +/** + * + */ +package dream.examples.util; + +/** + * @author Ram + * + */ +public enum Relation { + FOLLOWEDBY, OR, AND +} + +enum Clock { + EQUAL, OLD, NEW +} \ No newline at end of file diff --git a/Dream2/src/examples/java/dream/examples/util/StringToList.java b/Dream2/src/examples/java/dream/examples/util/StringToList.java new file mode 100644 index 0000000..1fa962e --- /dev/null +++ b/Dream2/src/examples/java/dream/examples/util/StringToList.java @@ -0,0 +1,38 @@ +/** + * + */ +package dream.examples.util; + +import java.util.HashMap; + +/** + * @author Ram + * + */ +public class StringToList { + + /** + * @param args + */ + public static void main(String[] args) { + String input = "p1:2@p2:3"; + String seperator = "@"; + StringToList.getClockFromString(input, seperator); + + } + + public static HashMap getClockFromString(String inputString, String seperator) { + HashMap clock = new HashMap<>(); + + String[] tokens = inputString.split(seperator); + int tokenCount = tokens.length; + for (int j = 0; j < tokenCount; j++) { + String kString = tokens[j].substring(0, tokens[j].indexOf(':')); + Integer value = new Integer( + Integer.parseInt(tokens[j].substring(tokens[j].indexOf(':') + 1, tokens[j].length()))); + System.out.println(tokens[j] + " " + kString + " " + value); + } + return clock; + + } +} diff --git a/Dream2/src/examples/java/dream/examples/util/VectorClock.java b/Dream2/src/examples/java/dream/examples/util/VectorClock.java index d762874..37d58ed 100644 --- a/Dream2/src/examples/java/dream/examples/util/VectorClock.java +++ b/Dream2/src/examples/java/dream/examples/util/VectorClock.java @@ -44,23 +44,35 @@ void checkNull() { * @param messageClock * @return */ - public boolean isEqual(HashMap messageClock) { + public Clock compareClock(HashMap messageClock) { checkNull(); - // clocks are not same if their lengths are not same - if (localClock.keySet().size() != messageClock.keySet().size()) { - return false; - } - // if length are same then we will check if both maps have same value - // for same keys. If there are different keys then null will be returned - // to compare statement which makes if statement true. + if (localClock.keySet().containsAll(messageClock.keySet()) + && localClock.keySet().size() == messageClock.keySet().size()) { + for (String key : localClock.keySet()) { + if (!localClock.get(key).equals(messageClock.get(key)) && localClock.get(key) > messageClock.get(key)) { + return Clock.OLD; + } + if (!localClock.get(key).equals(messageClock.get(key)) && localClock.get(key) < messageClock.get(key)) { + return Clock.NEW; + } + } + return Clock.EQUAL; - for (String key : localClock.keySet()) { - if (!localClock.get(key).equals(messageClock.get(key))) { - return false; + } else if (localClock.keySet().containsAll(messageClock.keySet()) + && localClock.keySet().size() > messageClock.keySet().size()) { + for (String key : messageClock.keySet()) { + if (!localClock.get(key).equals(messageClock.get(key)) && localClock.get(key) > messageClock.get(key)) { + return Clock.NEW; + } + if (!localClock.get(key).equals(messageClock.get(key)) && localClock.get(key) < messageClock.get(key)) { + return Clock.OLD; + } } + return Clock.EQUAL; + } - // Keys are similar, values are same. - return true; + + return null; } diff --git a/Dream2/src/main/java/dream/client/RemoteVar.java b/Dream2/src/main/java/dream/client/RemoteVar.java index 3f3a52c..6e87851 100755 --- a/Dream2/src/main/java/dream/client/RemoteVar.java +++ b/Dream2/src/main/java/dream/client/RemoteVar.java @@ -60,9 +60,9 @@ public final synchronized T get() { @Override public synchronized void notifyEventReceived(EventPacket evPkt) { eventsQueue.add(evPkt); - logger.finest("Received event packet " + evPkt + ". Added to the queue."); + logger.finest("Received event packet " + evPkt + ". Added to the queue1."); if (eventsQueue.size() == 1) { - logger.finest("The element is the only one in the queue. Let's process it."); + logger.finest("The element is the only one in the queue1. Let's process it."); processNextEvent(); } } diff --git a/Dream2/src/main/java/dream/client/Signal.java b/Dream2/src/main/java/dream/client/Signal.java index 75e1c38..02f7618 100755 --- a/Dream2/src/main/java/dream/client/Signal.java +++ b/Dream2/src/main/java/dream/client/Signal.java @@ -157,9 +157,9 @@ public UpdateProducer filter(SerializablePredicate constraint) { public final synchronized void updateFromProducer(EventPacket packet, UpdateProducer producer) { final EventProducerPair pair = new EventProducerPair(packet, producer); eventQueue.add(pair); - logger.finest("Method update called for event " + pair + ". Added to the queue."); + logger.finest("Method update called for event " + pair + ". Added to the queue1."); if (eventQueue.size() == 1) { - logger.finest("The element is the only one in the queue. Let's process it."); + logger.finest("The element is the only one in the queue1. Let's process it."); processNextUpdate(); } } From 9fed6b1a8dc6acf14d447ea35004cff5c037e6f3 Mon Sep 17 00:00:00 2001 From: Tobias Becker Date: Mon, 20 Jun 2016 13:45:32 +0200 Subject: [PATCH 120/161] renamed taskBoard to scrumBoard --- .../{taskBoard => scrumBoard}/Assignment.java | 2 +- .../{taskBoard => scrumBoard}/Creator.java | 2 +- .../{taskBoard => scrumBoard}/InitApp.java | 120 +++++++++--------- .../{taskBoard => scrumBoard}/Monitor.java | 2 +- .../{taskBoard => scrumBoard}/Server.java | 2 +- 5 files changed, 64 insertions(+), 64 deletions(-) rename Dream2/src/examples/java/dream/examples/{taskBoard => scrumBoard}/Assignment.java (96%) rename Dream2/src/examples/java/dream/examples/{taskBoard => scrumBoard}/Creator.java (99%) rename Dream2/src/examples/java/dream/examples/{taskBoard => scrumBoard}/InitApp.java (94%) rename Dream2/src/examples/java/dream/examples/{taskBoard => scrumBoard}/Monitor.java (99%) rename Dream2/src/examples/java/dream/examples/{taskBoard => scrumBoard}/Server.java (98%) diff --git a/Dream2/src/examples/java/dream/examples/taskBoard/Assignment.java b/Dream2/src/examples/java/dream/examples/scrumBoard/Assignment.java similarity index 96% rename from Dream2/src/examples/java/dream/examples/taskBoard/Assignment.java rename to Dream2/src/examples/java/dream/examples/scrumBoard/Assignment.java index b0442c5..955a514 100644 --- a/Dream2/src/examples/java/dream/examples/taskBoard/Assignment.java +++ b/Dream2/src/examples/java/dream/examples/scrumBoard/Assignment.java @@ -1,4 +1,4 @@ -package dream.examples.taskBoard; +package dream.examples.scrumBoard; import java.io.Serializable; import java.util.regex.Matcher; diff --git a/Dream2/src/examples/java/dream/examples/taskBoard/Creator.java b/Dream2/src/examples/java/dream/examples/scrumBoard/Creator.java similarity index 99% rename from Dream2/src/examples/java/dream/examples/taskBoard/Creator.java rename to Dream2/src/examples/java/dream/examples/scrumBoard/Creator.java index 7ca6899..071ee88 100644 --- a/Dream2/src/examples/java/dream/examples/taskBoard/Creator.java +++ b/Dream2/src/examples/java/dream/examples/scrumBoard/Creator.java @@ -1,4 +1,4 @@ -package dream.examples.taskBoard; +package dream.examples.scrumBoard; import java.awt.Container; import java.awt.event.ActionEvent; diff --git a/Dream2/src/examples/java/dream/examples/taskBoard/InitApp.java b/Dream2/src/examples/java/dream/examples/scrumBoard/InitApp.java similarity index 94% rename from Dream2/src/examples/java/dream/examples/taskBoard/InitApp.java rename to Dream2/src/examples/java/dream/examples/scrumBoard/InitApp.java index 7711074..da5c3fe 100644 --- a/Dream2/src/examples/java/dream/examples/taskBoard/InitApp.java +++ b/Dream2/src/examples/java/dream/examples/scrumBoard/InitApp.java @@ -1,61 +1,61 @@ -package dream.examples.taskBoard; - -import java.util.ArrayList; -import java.util.List; - -import dream.examples.util.NewJvmHelper; - -/** - * To start this example either:
- * - run {@link Server}, {@link Creator} and {@link Monitor} in any - * order
- * - or run this class.
- * This class will start all three classes each in a seperate instance of the - * JVM. It will also stop all classes if one of them is stopped normally.
- * WARNING: If they are destroyed forcefully via Eclipse (or any other - * way) there may continue to run and will have to be exited manually. - * - * @author Min Yang - * @author Tobias Becker - */ -public class InitApp { - private static List processes; - - public static void main(String... args) { - processes = new ArrayList<>(); - processes.add(NewJvmHelper.startNewJVM(Server.class)); - processes.add(NewJvmHelper.startNewJVM(Creator.class)); - processes.add(NewJvmHelper.startNewJVM(Creator.class)); - processes.add(NewJvmHelper.startNewJVM(Monitor.class)); - - sleep(-1); - } - - private static void sleep(int time) { - do { - try { - Thread.sleep(time == -1 ? 1000 : time); - checkExit(); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } while (time == -1); - - } - - private static void checkExit() { - for (Process p : processes) { - if (!p.isAlive()) { - System.out.println(p.getClass().getSimpleName() + " closed ... exiting!"); - destr(); - System.exit(0); - } - } - } - - private static void destr() { - for (Process p : processes) { - p.destroyForcibly(); - } - } +package dream.examples.scrumBoard; + +import java.util.ArrayList; +import java.util.List; + +import dream.examples.util.NewJvmHelper; + +/** + * To start this example either:
+ * - run {@link Server}, {@link Creator} and {@link Monitor} in any + * order
+ * - or run this class.
+ * This class will start all three classes each in a seperate instance of the + * JVM. It will also stop all classes if one of them is stopped normally.
+ * WARNING: If they are destroyed forcefully via Eclipse (or any other + * way) there may continue to run and will have to be exited manually. + * + * @author Min Yang + * @author Tobias Becker + */ +public class InitApp { + private static List processes; + + public static void main(String... args) { + processes = new ArrayList<>(); + processes.add(NewJvmHelper.startNewJVM(Server.class)); + processes.add(NewJvmHelper.startNewJVM(Creator.class)); + processes.add(NewJvmHelper.startNewJVM(Creator.class)); + processes.add(NewJvmHelper.startNewJVM(Monitor.class)); + + sleep(-1); + } + + private static void sleep(int time) { + do { + try { + Thread.sleep(time == -1 ? 1000 : time); + checkExit(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } while (time == -1); + + } + + private static void checkExit() { + for (Process p : processes) { + if (!p.isAlive()) { + System.out.println(p.getClass().getSimpleName() + " closed ... exiting!"); + destr(); + System.exit(0); + } + } + } + + private static void destr() { + for (Process p : processes) { + p.destroyForcibly(); + } + } } \ No newline at end of file diff --git a/Dream2/src/examples/java/dream/examples/taskBoard/Monitor.java b/Dream2/src/examples/java/dream/examples/scrumBoard/Monitor.java similarity index 99% rename from Dream2/src/examples/java/dream/examples/taskBoard/Monitor.java rename to Dream2/src/examples/java/dream/examples/scrumBoard/Monitor.java index 77a085a..1bd7782 100644 --- a/Dream2/src/examples/java/dream/examples/taskBoard/Monitor.java +++ b/Dream2/src/examples/java/dream/examples/scrumBoard/Monitor.java @@ -1,4 +1,4 @@ -package dream.examples.taskBoard; +package dream.examples.scrumBoard; import java.awt.Container; import java.awt.event.ActionEvent; diff --git a/Dream2/src/examples/java/dream/examples/taskBoard/Server.java b/Dream2/src/examples/java/dream/examples/scrumBoard/Server.java similarity index 98% rename from Dream2/src/examples/java/dream/examples/taskBoard/Server.java rename to Dream2/src/examples/java/dream/examples/scrumBoard/Server.java index 3c3ecc5..c356cff 100644 --- a/Dream2/src/examples/java/dream/examples/taskBoard/Server.java +++ b/Dream2/src/examples/java/dream/examples/scrumBoard/Server.java @@ -1,4 +1,4 @@ -package dream.examples.taskBoard; +package dream.examples.scrumBoard; import java.util.ArrayList; import java.util.Set; From 3a0d1b0b03bd84727af4ac5cc923a1bdfe593bd7 Mon Sep 17 00:00:00 2001 From: Tobias Becker Date: Mon, 20 Jun 2016 13:52:30 +0200 Subject: [PATCH 121/161] scrumBoard: renamed clients to creators --- .../java/dream/examples/scrumBoard/Server.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/Dream2/src/examples/java/dream/examples/scrumBoard/Server.java b/Dream2/src/examples/java/dream/examples/scrumBoard/Server.java index c356cff..f126959 100644 --- a/Dream2/src/examples/java/dream/examples/scrumBoard/Server.java +++ b/Dream2/src/examples/java/dream/examples/scrumBoard/Server.java @@ -23,7 +23,7 @@ public class Server extends Client { public static final String VAR_developers = "developers"; public static final String VAR_tasks = "tasks"; - private final ArrayList myClients; + private final ArrayList creators; private final Var developers; private final Var tasks; @@ -35,14 +35,14 @@ public Server() { super(NAME); developers = new Var(VAR_developers, ""); tasks = new Var(VAR_tasks, ""); - myClients = new ArrayList(); + creators = new ArrayList(); detectClients(); } private void detectClients() { Set vars = DreamClient.instance.listVariables(); vars.stream().map(x -> new Pair(x.split("@")[1], x.split("@")[0])) - .filter(x -> !myClients.contains(toVar(x)) && (x.getSecond().equalsIgnoreCase(Creator.VAR_newTask) + .filter(x -> !creators.contains(toVar(x)) && (x.getSecond().equalsIgnoreCase(Creator.VAR_newTask) || x.getSecond().equalsIgnoreCase(Creator.VAR_newDev))) .forEach(x -> createClient(x.getFirst(), x.getSecond())); try { @@ -54,7 +54,9 @@ private void detectClients() { } private void createClient(String clientHost, String clientVar) { - logger.info("detected client " + clientHost + " " + clientVar); + logger.info("found creator instance " + clientHost + " " + clientVar); + creators.add(clientVar + "@" + clientHost); + RemoteVar rv = new RemoteVar<>(clientHost, clientVar); Signal sig = new Signal<>(clientHost + "-" + clientVar, () -> { if (rv.get() != null) { @@ -76,6 +78,5 @@ private void createClient(String clientHost, String clientVar) { System.out.println("new value from " + clientHost + "@" + clientVar); } }); - myClients.add(clientVar + "@" + clientHost); } } \ No newline at end of file From 8b9f17cc1edd5a890038f8ebbfaa58b02cb831af Mon Sep 17 00:00:00 2001 From: Tobias Becker Date: Mon, 20 Jun 2016 13:59:18 +0200 Subject: [PATCH 122/161] scrumBoard: reverted back to single channel between creator and server --- .../java/dream/examples/scrumBoard/Creator.java | 16 ++++++---------- .../java/dream/examples/scrumBoard/Server.java | 17 +++++++---------- 2 files changed, 13 insertions(+), 20 deletions(-) diff --git a/Dream2/src/examples/java/dream/examples/scrumBoard/Creator.java b/Dream2/src/examples/java/dream/examples/scrumBoard/Creator.java index 071ee88..67ccd3f 100644 --- a/Dream2/src/examples/java/dream/examples/scrumBoard/Creator.java +++ b/Dream2/src/examples/java/dream/examples/scrumBoard/Creator.java @@ -27,16 +27,13 @@ */ public class Creator extends Client { - public static final String VAR_newDev = "newDev"; - public static final String VAR_newTask = "newTask"; + public static final String VAR_newAssignment = "newAssign"; - private Var taskCreator; - private Var devCreator; + private Var assignmentCreator; public Creator() { super("Creator" + new Random().nextInt(1000)); - taskCreator = new Var<>(VAR_newTask, null); - devCreator = new Var<>(VAR_newDev, null); + assignmentCreator = new Var<>(VAR_newAssignment, null); new TaskCreaterGUI(this); } @@ -48,9 +45,8 @@ public Logger getLogger() { return logger; } - public void addTask(Assignment t) { - taskCreator.set(t.getTaskString()); - devCreator.set(t.getDevString()); + public void addAssignment(Assignment t) { + assignmentCreator.set(t); } } @@ -151,7 +147,7 @@ class ButtonListener implements ActionListener { public void actionPerformed(ActionEvent paramActionEvent) { String toTasks = textField1.getText(); if (Assignment.isValid(toTasks)) { - creator.addTask(new Assignment(toTasks)); + creator.addAssignment(new Assignment(toTasks)); textField1.setText(""); } else { textField1.setText(""); diff --git a/Dream2/src/examples/java/dream/examples/scrumBoard/Server.java b/Dream2/src/examples/java/dream/examples/scrumBoard/Server.java index f126959..cb770fc 100644 --- a/Dream2/src/examples/java/dream/examples/scrumBoard/Server.java +++ b/Dream2/src/examples/java/dream/examples/scrumBoard/Server.java @@ -42,8 +42,7 @@ public Server() { private void detectClients() { Set vars = DreamClient.instance.listVariables(); vars.stream().map(x -> new Pair(x.split("@")[1], x.split("@")[0])) - .filter(x -> !creators.contains(toVar(x)) && (x.getSecond().equalsIgnoreCase(Creator.VAR_newTask) - || x.getSecond().equalsIgnoreCase(Creator.VAR_newDev))) + .filter(x -> !creators.contains(toVar(x)) && x.getSecond().equalsIgnoreCase(Creator.VAR_newAssignment)) .forEach(x -> createClient(x.getFirst(), x.getSecond())); try { Thread.sleep(500); @@ -57,8 +56,8 @@ private void createClient(String clientHost, String clientVar) { logger.info("found creator instance " + clientHost + " " + clientVar); creators.add(clientVar + "@" + clientHost); - RemoteVar rv = new RemoteVar<>(clientHost, clientVar); - Signal sig = new Signal<>(clientHost + "-" + clientVar, () -> { + RemoteVar rv = new RemoteVar<>(clientHost, clientVar); + Signal sig = new Signal<>(clientHost + "-" + clientVar, () -> { if (rv.get() != null) { return rv.get(); } else { @@ -69,12 +68,10 @@ private void createClient(String clientHost, String clientVar) { sig.change().addHandler((oldValue, newValue) -> { if (newValue != null) { // Set vars for remote querying - if (clientVar.equalsIgnoreCase(Creator.VAR_newDev)) { - developers.set(developers.get().length() == 0 ? newValue : developers.get() + ":" + newValue); - } - if (clientVar.equalsIgnoreCase(Creator.VAR_newTask)) { - tasks.set(tasks.get().length() == 0 ? newValue : tasks.get() + ":" + newValue); - } + developers.set(developers.get().length() == 0 ? newValue.getDevString() + : developers.get() + ":" + newValue.getDevString()); + tasks.set(tasks.get().length() == 0 ? newValue.getTaskString() + : tasks.get() + ":" + newValue.getTaskString()); System.out.println("new value from " + clientHost + "@" + clientVar); } }); From e0b7ea6eccf88f93cf2ba7817d22f0b4cfcd7088 Mon Sep 17 00:00:00 2001 From: Tobias Becker Date: Mon, 20 Jun 2016 14:22:47 +0200 Subject: [PATCH 123/161] scrumBoard: refactored developer/task-lists to signals --- .../dream/examples/scrumBoard/Server.java | 70 ++++++++++--------- 1 file changed, 36 insertions(+), 34 deletions(-) diff --git a/Dream2/src/examples/java/dream/examples/scrumBoard/Server.java b/Dream2/src/examples/java/dream/examples/scrumBoard/Server.java index cb770fc..873f7f2 100644 --- a/Dream2/src/examples/java/dream/examples/scrumBoard/Server.java +++ b/Dream2/src/examples/java/dream/examples/scrumBoard/Server.java @@ -1,12 +1,10 @@ package dream.examples.scrumBoard; -import java.util.ArrayList; import java.util.Set; import dream.client.DreamClient; import dream.client.RemoteVar; import dream.client.Signal; -import dream.client.Var; import dream.examples.util.Client; import dream.examples.util.Pair; @@ -23,9 +21,8 @@ public class Server extends Client { public static final String VAR_developers = "developers"; public static final String VAR_tasks = "tasks"; - private final ArrayList creators; - private final Var developers; - private final Var tasks; + private Pair creator1; + private Pair creator2; public static void main(String... args) { new Server(); @@ -33,47 +30,52 @@ public static void main(String... args) { public Server() { super(NAME); - developers = new Var(VAR_developers, ""); - tasks = new Var(VAR_tasks, ""); - creators = new ArrayList(); detectClients(); } private void detectClients() { Set vars = DreamClient.instance.listVariables(); vars.stream().map(x -> new Pair(x.split("@")[1], x.split("@")[0])) - .filter(x -> !creators.contains(toVar(x)) && x.getSecond().equalsIgnoreCase(Creator.VAR_newAssignment)) - .forEach(x -> createClient(x.getFirst(), x.getSecond())); + .filter(x -> !creator1.equals(toVar(x)) && !creator2.equals(toVar(x)) + && x.getSecond().equalsIgnoreCase(Creator.VAR_newAssignment)) + .forEach(x -> foundCreator(x)); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } - detectClients(); + + // stop looking after two creators have been found + if (creator1 == null || creator2 == null) + detectClients(); + } + + private void foundCreator(Pair creator) { + logger.info("found creator instance " + creator); + if (creator1 == null) + creator1 = creator; + else if (creator2 == null) + creator2 = creator; + + if (creator1 != null || creator2 != null) + initDependencies(); } - private void createClient(String clientHost, String clientVar) { - logger.info("found creator instance " + clientHost + " " + clientVar); - creators.add(clientVar + "@" + clientHost); - - RemoteVar rv = new RemoteVar<>(clientHost, clientVar); - Signal sig = new Signal<>(clientHost + "-" + clientVar, () -> { - if (rv.get() != null) { - return rv.get(); - } else { - return null; - } - }, rv); - - sig.change().addHandler((oldValue, newValue) -> { - if (newValue != null) { - // Set vars for remote querying - developers.set(developers.get().length() == 0 ? newValue.getDevString() - : developers.get() + ":" + newValue.getDevString()); - tasks.set(tasks.get().length() == 0 ? newValue.getTaskString() - : tasks.get() + ":" + newValue.getTaskString()); - System.out.println("new value from " + clientHost + "@" + clientVar); - } - }); + private void initDependencies() { + + RemoteVar rv1 = new RemoteVar<>(creator1.getFirst(), creator1.getSecond()); + RemoteVar rv2 = new RemoteVar<>(creator2.getFirst(), creator2.getSecond()); + + Signal developers = new Signal(VAR_developers, () -> { + // TODO + return ""; + }, rv1, rv2); + + Signal tasks = new Signal(VAR_tasks, () -> { + // TODO + return ""; + }, rv1, rv2); + } + } \ No newline at end of file From 5fcb025fb5d951ee414542200e96ddb3af78523c Mon Sep 17 00:00:00 2001 From: Tobias Becker Date: Mon, 20 Jun 2016 15:23:39 +0200 Subject: [PATCH 124/161] scrumBoard: fixed some bugs --- .../examples/java/dream/examples/scrumBoard/Monitor.java | 6 ++++-- .../src/examples/java/dream/examples/scrumBoard/Server.java | 5 +++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/Dream2/src/examples/java/dream/examples/scrumBoard/Monitor.java b/Dream2/src/examples/java/dream/examples/scrumBoard/Monitor.java index 1bd7782..9e73fc4 100644 --- a/Dream2/src/examples/java/dream/examples/scrumBoard/Monitor.java +++ b/Dream2/src/examples/java/dream/examples/scrumBoard/Monitor.java @@ -68,8 +68,10 @@ public static void main(String[] args) { public void clickButton1() { readLock(toVar(Server.NAME, Server.VAR_tasks), toVar(Server.NAME, Server.VAR_developers)); - gui.setTasks(tasks.get()); - gui.setDevs(devs.get()); + if (tasks.get() != null) + gui.setTasks(tasks.get()); + if (devs.get() != null) + gui.setDevs(devs.get()); unlock(); } } diff --git a/Dream2/src/examples/java/dream/examples/scrumBoard/Server.java b/Dream2/src/examples/java/dream/examples/scrumBoard/Server.java index 873f7f2..1bbabaa 100644 --- a/Dream2/src/examples/java/dream/examples/scrumBoard/Server.java +++ b/Dream2/src/examples/java/dream/examples/scrumBoard/Server.java @@ -36,7 +36,8 @@ public Server() { private void detectClients() { Set vars = DreamClient.instance.listVariables(); vars.stream().map(x -> new Pair(x.split("@")[1], x.split("@")[0])) - .filter(x -> !creator1.equals(toVar(x)) && !creator2.equals(toVar(x)) + .filter(x -> (creator1 == null || !creator1.equals(toVar(x))) + && (creator2 == null || !creator2.equals(toVar(x))) && x.getSecond().equalsIgnoreCase(Creator.VAR_newAssignment)) .forEach(x -> foundCreator(x)); try { @@ -57,7 +58,7 @@ private void foundCreator(Pair creator) { else if (creator2 == null) creator2 = creator; - if (creator1 != null || creator2 != null) + if (creator1 != null && creator2 != null) initDependencies(); } From 92e1688aa621064cb0d2b10d1affbeb64fb51e94 Mon Sep 17 00:00:00 2001 From: Tobias Becker Date: Mon, 20 Jun 2016 15:24:06 +0200 Subject: [PATCH 125/161] scrumBoard: added image of graph --- .../java/dream/examples/scrumBoard/graph.png | Bin 0 -> 41141 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 Dream2/src/examples/java/dream/examples/scrumBoard/graph.png diff --git a/Dream2/src/examples/java/dream/examples/scrumBoard/graph.png b/Dream2/src/examples/java/dream/examples/scrumBoard/graph.png new file mode 100644 index 0000000000000000000000000000000000000000..2d2f07b09dddb3fc3d9caecdc942a8f28f87d71d GIT binary patch literal 41141 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

C~KT_|Ge{vtwh1?I%ZZ%OB!74il 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 literal 0 HcmV?d00001 From 09e482abbae01ddc27828ce3624c2470d3f4075c Mon Sep 17 00:00:00 2001 From: Tobias Becker Date: Thu, 23 Jun 2016 15:33:14 +0200 Subject: [PATCH 126/161] refactored starter classes to ensure proper shutdown --- .../java/dream/examples/chat/Starter.java | 53 +--------- .../dream/examples/scrumBoard/InitApp.java | 53 ++-------- .../dream/examples/util/MultipleStarter.java | 96 +++++++++++++++++++ 3 files changed, 108 insertions(+), 94 deletions(-) create mode 100644 Dream2/src/examples/java/dream/examples/util/MultipleStarter.java diff --git a/Dream2/src/examples/java/dream/examples/chat/Starter.java b/Dream2/src/examples/java/dream/examples/chat/Starter.java index 9f7e82d..3360b41 100644 --- a/Dream2/src/examples/java/dream/examples/chat/Starter.java +++ b/Dream2/src/examples/java/dream/examples/chat/Starter.java @@ -1,10 +1,8 @@ package dream.examples.chat; -import java.util.ArrayList; - import dream.examples.chat.core.Chat; import dream.examples.chat.core.ChatServer; -import dream.examples.util.NewJvmHelper; +import dream.examples.util.MultipleStarter; /** * Convenience class to start ChatServer and x Chats (x = CHAT_COUNT), each in @@ -16,35 +14,27 @@ public class Starter { public static final int CHAT_COUNT = 4; - private static ArrayList processes = new ArrayList(); - public static void main(String[] args) { new Starter().start(); } - private Thread serverThread; - private static final String[] names = { "Alice", "Bob", "Chris", "David", "Eve", "Fred", "Georg", "Hans", "Igor" }; int xStep = 450; int yStep = 175; private void start() { - Runtime.getRuntime().addShutdownHook(new Thread(() -> onExit())); - serverThread = new Thread(() -> ChatServer.main(null)); - serverThread.start(); - sleep(1500); + MultipleStarter.addStartQueue(ChatServer.class); int x = 0; int y = 0; for (int i = 0; i < CHAT_COUNT; i++) { - processes.add(NewJvmHelper.startNewJVM(Chat.class, getName(i), Integer.toString(x), Integer.toString(y))); + MultipleStarter.addStartQueue(Chat.class, getName(i), Integer.toString(x), Integer.toString(y)); x += xStep; if (x >= 3 * xStep) { x = 0; y += yStep; } } - // sleep infinite time - sleep(-1); + MultipleStarter.start(); } private String getName(int i) { @@ -53,39 +43,4 @@ private String getName(int i) { else return names[i % names.length] + "" + i; } - - @Override - protected void finalize() throws Throwable { - onExit(); - super.finalize(); - } - - private void sleep(int time) { - do { - try { - Thread.sleep(time == -1 ? 1000 : time); - checkExit(); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } while (time == -1); - - } - - private void checkExit() { - for (Process p : processes) { - if (!p.isAlive()) { - System.out.println("One window closed ... exiting!"); - System.exit(0); - } - } - } - - private void onExit() { - System.out.println("exit"); - for (Process p : processes) { - p.destroyForcibly(); - System.out.println("Destroying " + p.toString()); - } - } } diff --git a/Dream2/src/examples/java/dream/examples/scrumBoard/InitApp.java b/Dream2/src/examples/java/dream/examples/scrumBoard/InitApp.java index da5c3fe..22c356b 100644 --- a/Dream2/src/examples/java/dream/examples/scrumBoard/InitApp.java +++ b/Dream2/src/examples/java/dream/examples/scrumBoard/InitApp.java @@ -1,61 +1,24 @@ package dream.examples.scrumBoard; -import java.util.ArrayList; -import java.util.List; - -import dream.examples.util.NewJvmHelper; +import dream.examples.util.MultipleStarter; /** * To start this example either:
- * - run {@link Server}, {@link Creator} and {@link Monitor} in any - * order
+ * - run {@link Server}, {@link Creator} and {@link Monitor} in any order
* - or run this class.
* This class will start all three classes each in a seperate instance of the * JVM. It will also stop all classes if one of them is stopped normally.
- * WARNING: If they are destroyed forcefully via Eclipse (or any other - * way) there may continue to run and will have to be exited manually. - * + * * @author Min Yang * @author Tobias Becker */ public class InitApp { - private static List processes; public static void main(String... args) { - processes = new ArrayList<>(); - processes.add(NewJvmHelper.startNewJVM(Server.class)); - processes.add(NewJvmHelper.startNewJVM(Creator.class)); - processes.add(NewJvmHelper.startNewJVM(Creator.class)); - processes.add(NewJvmHelper.startNewJVM(Monitor.class)); - - sleep(-1); - } - - private static void sleep(int time) { - do { - try { - Thread.sleep(time == -1 ? 1000 : time); - checkExit(); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } while (time == -1); - - } - - private static void checkExit() { - for (Process p : processes) { - if (!p.isAlive()) { - System.out.println(p.getClass().getSimpleName() + " closed ... exiting!"); - destr(); - System.exit(0); - } - } - } - - private static void destr() { - for (Process p : processes) { - p.destroyForcibly(); - } + MultipleStarter.addStartQueue(Server.class); + MultipleStarter.addStartQueue(Creator.class); + MultipleStarter.addStartQueue(Creator.class); + MultipleStarter.addStartQueue(Monitor.class); + MultipleStarter.start(); } } \ No newline at end of file diff --git a/Dream2/src/examples/java/dream/examples/util/MultipleStarter.java b/Dream2/src/examples/java/dream/examples/util/MultipleStarter.java new file mode 100644 index 0000000..3f4ff00 --- /dev/null +++ b/Dream2/src/examples/java/dream/examples/util/MultipleStarter.java @@ -0,0 +1,96 @@ +package dream.examples.util; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.ArrayList; +import java.util.Base64; +import java.util.LinkedList; +import java.util.List; + +/** + * Utility class to start multiple classes in their own vm and ensure that all + * are closed properly afterwards. + * + * @author Tobias Becker + */ +public class MultipleStarter { + + private static final LinkedList, String[]>> queue = new LinkedList<>(); + private static List processes; + + public static void main(String[] args) { + if (args.length < 1) + throw new UnsupportedOperationException("You are not supposed to run this Class directly"); + LinkedList, String[]>> q = queueFromString(args[0]); + processes = new ArrayList<>(); + for (Pair, String[]> p : q) { + processes.add(NewJvmHelper.startNewJVM(p.getFirst(), p.getSecond())); + } + sleep(-1); + } + + public static void addStartQueue(Class c, String... args) { + queue.add(new Pair<>(c, args)); + } + + public static void start() { + NewJvmHelper.startNewJVM(MultipleStarter.class, queueToString()); + } + + private static void sleep(int time) { + do { + try { + Thread.sleep(time == -1 ? 1000 : time); + checkExit(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } while (time == -1); + + } + + private static void checkExit() { + for (Process p : processes) { + if (!p.isAlive()) { + System.out.println(p.getClass().getSimpleName() + " closed ... exiting!"); + destr(); + System.exit(0); + } + } + } + + private static void destr() { + for (Process p : processes) { + p.destroyForcibly(); + } + } + + private static String queueToString() { + String s = ""; + try { + ByteArrayOutputStream bo = new ByteArrayOutputStream(); + ObjectOutputStream so = new ObjectOutputStream(bo); + so.writeObject(queue); + so.flush(); + s = new String(Base64.getEncoder().encode(bo.toByteArray())); + } catch (Exception e) { + System.err.println(e); + } + return s; + } + + private static LinkedList, String[]>> queueFromString(String s) { + try { + byte b[] = Base64.getDecoder().decode(s.getBytes()); + ByteArrayInputStream bi = new ByteArrayInputStream(b); + ObjectInputStream si = new ObjectInputStream(bi); + LinkedList, String[]>> obj = (LinkedList, String[]>>) si.readObject(); + return obj; + } catch (Exception e) { + System.err.println(e); + return null; + } + } +} From a24bdd0e279269302765c5f01ba51132e9952239 Mon Sep 17 00:00:00 2001 From: Tobias Becker Date: Thu, 23 Jun 2016 16:13:57 +0200 Subject: [PATCH 127/161] scrumBoard: fixed a bug in the creator detection --- .../java/dream/examples/scrumBoard/Server.java | 3 +-- .../src/examples/java/dream/examples/util/Pair.java | 13 +++++++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/Dream2/src/examples/java/dream/examples/scrumBoard/Server.java b/Dream2/src/examples/java/dream/examples/scrumBoard/Server.java index 1bbabaa..a74e6e2 100644 --- a/Dream2/src/examples/java/dream/examples/scrumBoard/Server.java +++ b/Dream2/src/examples/java/dream/examples/scrumBoard/Server.java @@ -36,8 +36,7 @@ public Server() { private void detectClients() { Set vars = DreamClient.instance.listVariables(); vars.stream().map(x -> new Pair(x.split("@")[1], x.split("@")[0])) - .filter(x -> (creator1 == null || !creator1.equals(toVar(x))) - && (creator2 == null || !creator2.equals(toVar(x))) + .filter(x -> (creator1 == null || !creator1.equals(x)) && (creator2 == null || !creator2.equals(x)) && x.getSecond().equalsIgnoreCase(Creator.VAR_newAssignment)) .forEach(x -> foundCreator(x)); try { diff --git a/Dream2/src/examples/java/dream/examples/util/Pair.java b/Dream2/src/examples/java/dream/examples/util/Pair.java index caa061b..6ec0c8c 100644 --- a/Dream2/src/examples/java/dream/examples/util/Pair.java +++ b/Dream2/src/examples/java/dream/examples/util/Pair.java @@ -19,4 +19,17 @@ public S getFirst() { public T getSecond() { return second; } + + @Override + public String toString() { + return "(" + first + ", " + second + ")"; + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof Pair)) + return false; + Pair p2 = (Pair) obj; + return first.equals(p2.first) && second.equals(p2.second); + } } \ No newline at end of file From 5b6ad261781cfcc131d6b25c091265b65be3251f Mon Sep 17 00:00:00 2001 From: Tobias Becker Date: Thu, 23 Jun 2016 16:44:37 +0200 Subject: [PATCH 128/161] scrumBoard: creator supplies a list of all added assigments --- .../dream/examples/scrumBoard/Assignment.java | 14 ++++++++++- .../dream/examples/scrumBoard/Creator.java | 7 +++--- .../dream/examples/scrumBoard/Server.java | 25 ++++++++++++++----- 3 files changed, 36 insertions(+), 10 deletions(-) diff --git a/Dream2/src/examples/java/dream/examples/scrumBoard/Assignment.java b/Dream2/src/examples/java/dream/examples/scrumBoard/Assignment.java index 955a514..f41d9c3 100644 --- a/Dream2/src/examples/java/dream/examples/scrumBoard/Assignment.java +++ b/Dream2/src/examples/java/dream/examples/scrumBoard/Assignment.java @@ -1,6 +1,7 @@ package dream.examples.scrumBoard; import java.io.Serializable; +import java.util.Date; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -10,17 +11,19 @@ * @author Min Yang * @author Tobias Becker */ -public class Assignment implements Serializable { +public class Assignment implements Serializable, Comparable { private static final long serialVersionUID = 8329097603920137211L; public static String pattern = "D(\\d*):T(\\d*)"; private int developer; private int task; + private Date time; public Assignment(String input) { Matcher m = Pattern.compile(pattern).matcher(input); if (m.matches()) { developer = Integer.parseInt(m.group(1)); task = Integer.parseInt(m.group(2)); + time = new Date(); } else throw new UnsupportedOperationException("Wrong Input"); } @@ -45,4 +48,13 @@ public String getTaskString() { return Integer.toString(task); } + public Date getTime() { + return time; + } + + @Override + public int compareTo(Assignment o) { + return time.compareTo(o.time); + } + } diff --git a/Dream2/src/examples/java/dream/examples/scrumBoard/Creator.java b/Dream2/src/examples/java/dream/examples/scrumBoard/Creator.java index 67ccd3f..c3ff67d 100644 --- a/Dream2/src/examples/java/dream/examples/scrumBoard/Creator.java +++ b/Dream2/src/examples/java/dream/examples/scrumBoard/Creator.java @@ -7,6 +7,7 @@ import java.awt.event.KeyListener; import java.awt.event.WindowEvent; import java.awt.event.WindowListener; +import java.util.LinkedList; import java.util.Random; import java.util.logging.Logger; @@ -29,11 +30,11 @@ public class Creator extends Client { public static final String VAR_newAssignment = "newAssign"; - private Var assignmentCreator; + private Var> assignmentCreator; public Creator() { super("Creator" + new Random().nextInt(1000)); - assignmentCreator = new Var<>(VAR_newAssignment, null); + assignmentCreator = new Var<>(VAR_newAssignment, new LinkedList<>()); new TaskCreaterGUI(this); } @@ -46,7 +47,7 @@ public Logger getLogger() { } public void addAssignment(Assignment t) { - assignmentCreator.set(t); + assignmentCreator.modify((old) -> old.addLast(t)); } } diff --git a/Dream2/src/examples/java/dream/examples/scrumBoard/Server.java b/Dream2/src/examples/java/dream/examples/scrumBoard/Server.java index a74e6e2..9134ecb 100644 --- a/Dream2/src/examples/java/dream/examples/scrumBoard/Server.java +++ b/Dream2/src/examples/java/dream/examples/scrumBoard/Server.java @@ -1,6 +1,9 @@ package dream.examples.scrumBoard; +import java.util.Collections; +import java.util.LinkedList; import java.util.Set; +import java.util.stream.Collectors; import dream.client.DreamClient; import dream.client.RemoteVar; @@ -63,17 +66,27 @@ else if (creator2 == null) private void initDependencies() { - RemoteVar rv1 = new RemoteVar<>(creator1.getFirst(), creator1.getSecond()); - RemoteVar rv2 = new RemoteVar<>(creator2.getFirst(), creator2.getSecond()); + RemoteVar> rv1 = new RemoteVar<>(creator1.getFirst(), creator1.getSecond()); + RemoteVar> rv2 = new RemoteVar<>(creator2.getFirst(), creator2.getSecond()); Signal developers = new Signal(VAR_developers, () -> { - // TODO - return ""; + LinkedList temp = new LinkedList<>(); + if (rv1.get() != null) + temp.addAll(rv1.get()); + if (rv2.get() != null) + temp.addAll(rv2.get()); + Collections.sort(temp); + return temp.stream().map(a -> a.getDevString()).collect(Collectors.joining(":")); }, rv1, rv2); Signal tasks = new Signal(VAR_tasks, () -> { - // TODO - return ""; + LinkedList temp = new LinkedList<>(); + if (rv1.get() != null) + temp.addAll(rv1.get()); + if (rv2.get() != null) + temp.addAll(rv2.get()); + Collections.sort(temp); + return temp.stream().map(a -> a.getTaskString()).collect(Collectors.joining(":")); }, rv1, rv2); } From e96ccbe93a0d995beb2882f99f21adad6ec70675 Mon Sep 17 00:00:00 2001 From: Tobias Becker Date: Thu, 23 Jun 2016 17:00:11 +0200 Subject: [PATCH 129/161] scrumBoard: some refactoring --- .../scrumBoard/{ => common}/Assignment.java | 2 +- .../{Creator.java => common/CreatorGUI.java} | 44 ++--------- .../{Monitor.java => common/MonitorGUI.java} | 71 ++---------------- .../examples/scrumBoard/core/Creator.java | 41 ++++++++++ .../scrumBoard/{ => core}/InitApp.java | 2 +- .../examples/scrumBoard/core/Monitor.java | 69 +++++++++++++++++ .../scrumBoard/{ => core}/Server.java | 3 +- .../examples/scrumBoard/{ => core}/graph.png | Bin 8 files changed, 126 insertions(+), 106 deletions(-) rename Dream2/src/examples/java/dream/examples/scrumBoard/{ => common}/Assignment.java (96%) rename Dream2/src/examples/java/dream/examples/scrumBoard/{Creator.java => common/CreatorGUI.java} (79%) rename Dream2/src/examples/java/dream/examples/scrumBoard/{Monitor.java => common/MonitorGUI.java} (64%) create mode 100644 Dream2/src/examples/java/dream/examples/scrumBoard/core/Creator.java rename Dream2/src/examples/java/dream/examples/scrumBoard/{ => core}/InitApp.java (94%) create mode 100644 Dream2/src/examples/java/dream/examples/scrumBoard/core/Monitor.java rename Dream2/src/examples/java/dream/examples/scrumBoard/{ => core}/Server.java (96%) rename Dream2/src/examples/java/dream/examples/scrumBoard/{ => core}/graph.png (100%) diff --git a/Dream2/src/examples/java/dream/examples/scrumBoard/Assignment.java b/Dream2/src/examples/java/dream/examples/scrumBoard/common/Assignment.java similarity index 96% rename from Dream2/src/examples/java/dream/examples/scrumBoard/Assignment.java rename to Dream2/src/examples/java/dream/examples/scrumBoard/common/Assignment.java index f41d9c3..5318a76 100644 --- a/Dream2/src/examples/java/dream/examples/scrumBoard/Assignment.java +++ b/Dream2/src/examples/java/dream/examples/scrumBoard/common/Assignment.java @@ -1,4 +1,4 @@ -package dream.examples.scrumBoard; +package dream.examples.scrumBoard.common; import java.io.Serializable; import java.util.Date; diff --git a/Dream2/src/examples/java/dream/examples/scrumBoard/Creator.java b/Dream2/src/examples/java/dream/examples/scrumBoard/common/CreatorGUI.java similarity index 79% rename from Dream2/src/examples/java/dream/examples/scrumBoard/Creator.java rename to Dream2/src/examples/java/dream/examples/scrumBoard/common/CreatorGUI.java index c3ff67d..9451e3e 100644 --- a/Dream2/src/examples/java/dream/examples/scrumBoard/Creator.java +++ b/Dream2/src/examples/java/dream/examples/scrumBoard/common/CreatorGUI.java @@ -1,4 +1,4 @@ -package dream.examples.scrumBoard; +package dream.examples.scrumBoard.common; import java.awt.Container; import java.awt.event.ActionEvent; @@ -7,8 +7,6 @@ import java.awt.event.KeyListener; import java.awt.event.WindowEvent; import java.awt.event.WindowListener; -import java.util.LinkedList; -import java.util.Random; import java.util.logging.Logger; import javax.swing.GroupLayout; @@ -17,47 +15,19 @@ import javax.swing.JOptionPane; import javax.swing.JTextField; -import dream.client.Var; -import dream.examples.util.Client; +public class CreatorGUI { + public interface Creator { + void addAssignment(Assignment assignment); -/** - * Interface to create new Tasks. May be started multiple times! - * - * @author Min Yang - * @author Tobias Becker - */ -public class Creator extends Client { - - public static final String VAR_newAssignment = "newAssign"; - - private Var> assignmentCreator; - - public Creator() { - super("Creator" + new Random().nextInt(1000)); - assignmentCreator = new Var<>(VAR_newAssignment, new LinkedList<>()); - new TaskCreaterGUI(this); - } - - public static void main(String[] args) { - new Creator(); - } - - public Logger getLogger() { - return logger; - } - - public void addAssignment(Assignment t) { - assignmentCreator.modify((old) -> old.addLast(t)); + Logger getLogger(); } -} -class TaskCreaterGUI { private JTextField textField1; private JFrame frame1; private JButton button1; private Creator creator; - public TaskCreaterGUI(Creator t) { + public CreatorGUI(Creator t) { this.creator = t; initComponents(); } @@ -157,4 +127,4 @@ public void actionPerformed(ActionEvent paramActionEvent) { } } } -} +} \ No newline at end of file diff --git a/Dream2/src/examples/java/dream/examples/scrumBoard/Monitor.java b/Dream2/src/examples/java/dream/examples/scrumBoard/common/MonitorGUI.java similarity index 64% rename from Dream2/src/examples/java/dream/examples/scrumBoard/Monitor.java rename to Dream2/src/examples/java/dream/examples/scrumBoard/common/MonitorGUI.java index 9e73fc4..0bb5e24 100644 --- a/Dream2/src/examples/java/dream/examples/scrumBoard/Monitor.java +++ b/Dream2/src/examples/java/dream/examples/scrumBoard/common/MonitorGUI.java @@ -1,9 +1,7 @@ -package dream.examples.scrumBoard; +package dream.examples.scrumBoard.common; import java.awt.Container; import java.awt.event.ActionEvent; -import java.util.Arrays; -import java.util.List; import javax.swing.GroupLayout; import javax.swing.JButton; @@ -12,71 +10,12 @@ import javax.swing.JTextArea; import javax.swing.LayoutStyle; -import dream.client.RemoteVar; -import dream.client.Signal; -import dream.examples.util.Client; +public class MonitorGUI { -/** - * Displays both lists, the developers and the tasks. - * - * @author Min Yang - * @author Tobias Becker - */ -public class Monitor extends Client { - - public static final String NAME = "Monitor"; - - private final MonitorGUI gui; - private final RemoteVar devs; - private final RemoteVar tasks; - private final Signal sigDevs; - private final Signal sigTasks; - - @Override - protected List waitForVars() { - return Arrays.asList(toVar(Server.NAME, Server.VAR_developers), toVar(Server.NAME, Server.VAR_tasks)); - } - - public Monitor() { - super(NAME); - gui = new MonitorGUI(this); - - devs = new RemoteVar(Server.NAME, Server.VAR_developers); - sigDevs = new Signal("sigDevs", () -> { - return devs.get(); - }, devs); - - tasks = new RemoteVar(Server.NAME, Server.VAR_tasks); - sigTasks = new Signal("sigTests", () -> { - return tasks.get(); - }, tasks); - - sigDevs.change().addHandler((oldVa, newVal) -> { - System.out.println("newVal devs:" + newVal); - // gui.setDevs(newVal); - }); - - sigTasks.change().addHandler((oldVa, newVal) -> { - System.out.println("newVal tasks:" + newVal); - // gui.setTasks(newVal); - }); - } - - public static void main(String[] args) { - new Monitor(); - } - - public void clickButton1() { - readLock(toVar(Server.NAME, Server.VAR_tasks), toVar(Server.NAME, Server.VAR_developers)); - if (tasks.get() != null) - gui.setTasks(tasks.get()); - if (devs.get() != null) - gui.setDevs(devs.get()); - unlock(); + public interface Monitor { + void clickButton(); } -} -class MonitorGUI { private JFrame frame1; private JTextArea textAreaTasks; private JLabel label1; @@ -139,7 +78,7 @@ public MonitorGUI(Monitor m) { } private void button1ActionPerformed(ActionEvent e) { - monitor.clickButton1(); + monitor.clickButton(); } public void setDevs(String value) { diff --git a/Dream2/src/examples/java/dream/examples/scrumBoard/core/Creator.java b/Dream2/src/examples/java/dream/examples/scrumBoard/core/Creator.java new file mode 100644 index 0000000..d6eb040 --- /dev/null +++ b/Dream2/src/examples/java/dream/examples/scrumBoard/core/Creator.java @@ -0,0 +1,41 @@ +package dream.examples.scrumBoard.core; + +import java.util.LinkedList; +import java.util.Random; +import java.util.logging.Logger; + +import dream.client.Var; +import dream.examples.scrumBoard.common.Assignment; +import dream.examples.scrumBoard.common.CreatorGUI; +import dream.examples.util.Client; + +/** + * Interface to create new Tasks. May be started multiple times! + * + * @author Min Yang + * @author Tobias Becker + */ +public class Creator extends Client implements dream.examples.scrumBoard.common.CreatorGUI.Creator { + + public static final String VAR_newAssignment = "newAssign"; + + private Var> assignmentCreator; + + public Creator() { + super("Creator" + new Random().nextInt(1000)); + assignmentCreator = new Var<>(VAR_newAssignment, new LinkedList<>()); + new CreatorGUI(this); + } + + public static void main(String[] args) { + new Creator(); + } + + public Logger getLogger() { + return logger; + } + + public void addAssignment(Assignment t) { + assignmentCreator.modify((old) -> old.addLast(t)); + } +} diff --git a/Dream2/src/examples/java/dream/examples/scrumBoard/InitApp.java b/Dream2/src/examples/java/dream/examples/scrumBoard/core/InitApp.java similarity index 94% rename from Dream2/src/examples/java/dream/examples/scrumBoard/InitApp.java rename to Dream2/src/examples/java/dream/examples/scrumBoard/core/InitApp.java index 22c356b..6356038 100644 --- a/Dream2/src/examples/java/dream/examples/scrumBoard/InitApp.java +++ b/Dream2/src/examples/java/dream/examples/scrumBoard/core/InitApp.java @@ -1,4 +1,4 @@ -package dream.examples.scrumBoard; +package dream.examples.scrumBoard.core; import dream.examples.util.MultipleStarter; diff --git a/Dream2/src/examples/java/dream/examples/scrumBoard/core/Monitor.java b/Dream2/src/examples/java/dream/examples/scrumBoard/core/Monitor.java new file mode 100644 index 0000000..eb8f12c --- /dev/null +++ b/Dream2/src/examples/java/dream/examples/scrumBoard/core/Monitor.java @@ -0,0 +1,69 @@ +package dream.examples.scrumBoard.core; + +import java.util.Arrays; +import java.util.List; + +import dream.client.RemoteVar; +import dream.client.Signal; +import dream.examples.scrumBoard.common.MonitorGUI; +import dream.examples.util.Client; + +/** + * Displays both lists, the developers and the tasks. + * + * @author Min Yang + * @author Tobias Becker + */ +public class Monitor extends Client implements dream.examples.scrumBoard.common.MonitorGUI.Monitor { + + public static final String NAME = "Monitor"; + + private final MonitorGUI gui; + private final RemoteVar devs; + private final RemoteVar tasks; + private final Signal sigDevs; + private final Signal sigTasks; + + @Override + protected List waitForVars() { + return Arrays.asList(toVar(Server.NAME, Server.VAR_developers), toVar(Server.NAME, Server.VAR_tasks)); + } + + public Monitor() { + super(NAME); + gui = new MonitorGUI(this); + + devs = new RemoteVar(Server.NAME, Server.VAR_developers); + sigDevs = new Signal("sigDevs", () -> { + return devs.get(); + }, devs); + + tasks = new RemoteVar(Server.NAME, Server.VAR_tasks); + sigTasks = new Signal("sigTests", () -> { + return tasks.get(); + }, tasks); + + sigDevs.change().addHandler((oldVa, newVal) -> { + System.out.println("newVal devs:" + newVal); + // gui.setDevs(newVal); + }); + + sigTasks.change().addHandler((oldVa, newVal) -> { + System.out.println("newVal tasks:" + newVal); + // gui.setTasks(newVal); + }); + } + + public static void main(String[] args) { + new Monitor(); + } + + public void clickButton() { + readLock(toVar(Server.NAME, Server.VAR_tasks), toVar(Server.NAME, Server.VAR_developers)); + if (tasks.get() != null) + gui.setTasks(tasks.get()); + if (devs.get() != null) + gui.setDevs(devs.get()); + unlock(); + } +} \ No newline at end of file diff --git a/Dream2/src/examples/java/dream/examples/scrumBoard/Server.java b/Dream2/src/examples/java/dream/examples/scrumBoard/core/Server.java similarity index 96% rename from Dream2/src/examples/java/dream/examples/scrumBoard/Server.java rename to Dream2/src/examples/java/dream/examples/scrumBoard/core/Server.java index 9134ecb..7db4274 100644 --- a/Dream2/src/examples/java/dream/examples/scrumBoard/Server.java +++ b/Dream2/src/examples/java/dream/examples/scrumBoard/core/Server.java @@ -1,4 +1,4 @@ -package dream.examples.scrumBoard; +package dream.examples.scrumBoard.core; import java.util.Collections; import java.util.LinkedList; @@ -8,6 +8,7 @@ import dream.client.DreamClient; import dream.client.RemoteVar; import dream.client.Signal; +import dream.examples.scrumBoard.common.Assignment; import dream.examples.util.Client; import dream.examples.util.Pair; diff --git a/Dream2/src/examples/java/dream/examples/scrumBoard/graph.png b/Dream2/src/examples/java/dream/examples/scrumBoard/core/graph.png similarity index 100% rename from Dream2/src/examples/java/dream/examples/scrumBoard/graph.png rename to Dream2/src/examples/java/dream/examples/scrumBoard/core/graph.png From 31aad649195dcd52f8e49ffde536c3af7e60e163 Mon Sep 17 00:00:00 2001 From: Tobias Becker Date: Fri, 24 Jun 2016 14:34:33 +0200 Subject: [PATCH 130/161] scrumBoard: added LockManager for atomic --- .../scrumBoard/atomic/LockManager.java | 90 +++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 Dream2/src/examples/java/dream/examples/scrumBoard/atomic/LockManager.java diff --git a/Dream2/src/examples/java/dream/examples/scrumBoard/atomic/LockManager.java b/Dream2/src/examples/java/dream/examples/scrumBoard/atomic/LockManager.java new file mode 100644 index 0000000..1d6990b --- /dev/null +++ b/Dream2/src/examples/java/dream/examples/scrumBoard/atomic/LockManager.java @@ -0,0 +1,90 @@ +package dream.examples.scrumBoard.atomic; + +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.Set; +import java.util.logging.Level; + +import dream.client.DreamClient; +import dream.client.RemoteVar; +import dream.client.Signal; +import dream.client.Var; +import dream.examples.util.Client; +import dream.examples.util.Pair; + +public class LockManager extends Client { + + public static final String VAR_lock = "lock"; + public static final String VAR_requestLock = "requestLock"; + public static final String VAR_clients = "clients"; + public static final String NAME = "LockManager"; + + /** + * lock:
+ * "" -> no lock
+ * [ClientName] -> graph is locked for this Client + */ + private Var lock; + private final Var>> clients; + private LinkedList lockRequests; + + public LockManager() { + super(NAME); + clients = new Var<>(VAR_clients, new ArrayList<>()); + lock = new Var<>(VAR_lock, ""); + detectNewSession(); + } + + private void detectNewSession() { + Set vars = DreamClient.instance.listVariables(); + vars.stream().map(x -> new Pair(x.split("@")[1], x.split("@")[0])).// Pair(Host,Var) + filter(x -> !clients.get().contains(x) && x.getSecond().equalsIgnoreCase(VAR_requestLock)).// + forEach(x -> createNewSessionFor(x)); + try { + Thread.sleep(500); + } catch (InterruptedException e) { + logger.log(Level.SEVERE, "Failed to sleep for 0.5 seconds", e); + } + + detectNewSession(); + } + + private void createNewSessionFor(Pair x) { + RemoteVar rv = new RemoteVar<>(x.getFirst(), x.getSecond()); + Signal s = new Signal<>(x.getFirst() + "request", () -> { + if (rv.get() != null) + return rv.get(); + else + return false; + }, rv); + s.change().addHandler((oldValue, newValue) -> { + if (newValue) { + // client requesting a lock + if (lock.get().equals("")) { + // no lock present, granting + lock.set(x.getFirst()); + } else { + // already locked, adding to queue + lockRequests.add(x.getFirst()); + } + } else if (!newValue) { + // client trying to release a lock + if (lock.get().equals(x.getFirst())) { + // client had the lock, releasing + lock.set(""); + // granting lock request for next client + if (!lockRequests.isEmpty()) + lock.set(lockRequests.poll()); + } else { + // client didn't have the lock -> withdrawing lock request + lockRequests.remove(x.getFirst()); + } + } + }); + clients.modify(old -> old.add(x)); + } + + public static void main(String[] args) { + new LockManager(); + } +} From 543012077ef93be39be85afba88e6e2369a596ab Mon Sep 17 00:00:00 2001 From: Tobias Becker Date: Fri, 24 Jun 2016 14:35:43 +0200 Subject: [PATCH 131/161] scrumBoard: added Creator with connection to LockManager --- .../examples/scrumBoard/atomic/Creator.java | 67 +++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 Dream2/src/examples/java/dream/examples/scrumBoard/atomic/Creator.java diff --git a/Dream2/src/examples/java/dream/examples/scrumBoard/atomic/Creator.java b/Dream2/src/examples/java/dream/examples/scrumBoard/atomic/Creator.java new file mode 100644 index 0000000..f617708 --- /dev/null +++ b/Dream2/src/examples/java/dream/examples/scrumBoard/atomic/Creator.java @@ -0,0 +1,67 @@ +package dream.examples.scrumBoard.atomic; + +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.Random; +import java.util.logging.Logger; + +import dream.client.RemoteVar; +import dream.client.Signal; +import dream.client.Var; +import dream.examples.scrumBoard.common.Assignment; +import dream.examples.scrumBoard.common.CreatorGUI; +import dream.examples.util.Client; +import dream.examples.util.Pair; + +/** + * Interface to create new Tasks. May be started multiple times! + * + * @author Min Yang + * @author Tobias Becker + */ +public class Creator extends Client implements dream.examples.scrumBoard.common.CreatorGUI.Creator { + + public static final String VAR_newAssignment = "newAssign"; + + private Var> assignmentCreator; + + private Var lockRequest; + + public Creator() { + super("Creator" + new Random().nextInt(1000)); + + // Establish new session with LockManager + RemoteVar>> registeredClients = new RemoteVar<>(LockManager.NAME, + LockManager.VAR_clients); + Signal>> s = new Signal<>("s", () -> { + if (registeredClients.get() == null) + return new ArrayList>(); + else + return registeredClients.get(); + }, registeredClients); + s.change().addHandler((o, n) -> { + if (n.contains(new Pair<>(this.getHostName(), LockManager.VAR_requestLock)) && assignmentCreator == null) + setup(); + }); + + lockRequest = new Var<>(LockManager.VAR_requestLock, false); + logger.fine("Setup: Waiting for Registration to LockManager ..."); + } + + private void setup() { + assignmentCreator = new Var<>(VAR_newAssignment, new LinkedList<>()); + new CreatorGUI(this); + } + + public static void main(String[] args) { + new Creator(); + } + + public Logger getLogger() { + return logger; + } + + public void addAssignment(Assignment t) { + assignmentCreator.modify((old) -> old.addLast(t)); + } +} From 8a834037e30eda970db05722ec18f08b2886e7f0 Mon Sep 17 00:00:00 2001 From: Tobias Becker Date: Fri, 24 Jun 2016 14:36:25 +0200 Subject: [PATCH 132/161] scrumBoard: added InitApp for atomic --- .../examples/scrumBoard/atomic/InitApp.java | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 Dream2/src/examples/java/dream/examples/scrumBoard/atomic/InitApp.java diff --git a/Dream2/src/examples/java/dream/examples/scrumBoard/atomic/InitApp.java b/Dream2/src/examples/java/dream/examples/scrumBoard/atomic/InitApp.java new file mode 100644 index 0000000..0358a98 --- /dev/null +++ b/Dream2/src/examples/java/dream/examples/scrumBoard/atomic/InitApp.java @@ -0,0 +1,25 @@ +package dream.examples.scrumBoard.atomic; + +import dream.examples.util.MultipleStarter; + +/** + * To start this example either:
+ * - run {@link Server}, {@link Creator} and {@link Monitor} in any order
+ * - or run this class.
+ * This class will start all three classes each in a seperate instance of the + * JVM. It will also stop all classes if one of them is stopped normally.
+ * + * @author Min Yang + * @author Tobias Becker + */ +public class InitApp { + + public static void main(String... args) { + MultipleStarter.addStartQueue(LockManager.class); + MultipleStarter.addStartQueue(Server.class); + MultipleStarter.addStartQueue(Creator.class); + MultipleStarter.addStartQueue(Creator.class); + MultipleStarter.addStartQueue(Monitor.class); + MultipleStarter.start(); + } +} \ No newline at end of file From cdf12e145848f562ec747ef0085f07f4e4b8d669 Mon Sep 17 00:00:00 2001 From: Tobias Becker Date: Fri, 24 Jun 2016 14:38:11 +0200 Subject: [PATCH 133/161] scrumBoard: use core.Server for atomic --- .../examples/java/dream/examples/scrumBoard/atomic/InitApp.java | 1 + 1 file changed, 1 insertion(+) diff --git a/Dream2/src/examples/java/dream/examples/scrumBoard/atomic/InitApp.java b/Dream2/src/examples/java/dream/examples/scrumBoard/atomic/InitApp.java index 0358a98..1bfabc6 100644 --- a/Dream2/src/examples/java/dream/examples/scrumBoard/atomic/InitApp.java +++ b/Dream2/src/examples/java/dream/examples/scrumBoard/atomic/InitApp.java @@ -1,5 +1,6 @@ package dream.examples.scrumBoard.atomic; +import dream.examples.scrumBoard.core.Server; import dream.examples.util.MultipleStarter; /** From 66351c8de7d431af9f1a9befed60f7da477b3491 Mon Sep 17 00:00:00 2001 From: Tobias Becker Date: Fri, 24 Jun 2016 14:44:01 +0200 Subject: [PATCH 134/161] scrumBoard: added Monitor with connection to LockManager --- .../examples/scrumBoard/atomic/Creator.java | 2 + .../examples/scrumBoard/atomic/Monitor.java | 77 +++++++++++++++++++ 2 files changed, 79 insertions(+) create mode 100644 Dream2/src/examples/java/dream/examples/scrumBoard/atomic/Monitor.java diff --git a/Dream2/src/examples/java/dream/examples/scrumBoard/atomic/Creator.java b/Dream2/src/examples/java/dream/examples/scrumBoard/atomic/Creator.java index f617708..b83b572 100644 --- a/Dream2/src/examples/java/dream/examples/scrumBoard/atomic/Creator.java +++ b/Dream2/src/examples/java/dream/examples/scrumBoard/atomic/Creator.java @@ -62,6 +62,8 @@ public Logger getLogger() { } public void addAssignment(Assignment t) { + // TODO: lock assignmentCreator.modify((old) -> old.addLast(t)); + // TODO: unlock } } diff --git a/Dream2/src/examples/java/dream/examples/scrumBoard/atomic/Monitor.java b/Dream2/src/examples/java/dream/examples/scrumBoard/atomic/Monitor.java new file mode 100644 index 0000000..8851ae5 --- /dev/null +++ b/Dream2/src/examples/java/dream/examples/scrumBoard/atomic/Monitor.java @@ -0,0 +1,77 @@ +package dream.examples.scrumBoard.atomic; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import dream.client.RemoteVar; +import dream.client.Signal; +import dream.client.Var; +import dream.examples.scrumBoard.common.MonitorGUI; +import dream.examples.scrumBoard.core.Server; +import dream.examples.util.Client; +import dream.examples.util.Pair; + +/** + * Displays both lists, the developers and the tasks. + * + * @author Min Yang + * @author Tobias Becker + */ +public class Monitor extends Client implements dream.examples.scrumBoard.common.MonitorGUI.Monitor { + + public static final String NAME = "Monitor"; + + private MonitorGUI gui; + private RemoteVar devs; + private RemoteVar tasks; + + private Var lockRequest; + + @Override + protected List waitForVars() { + return Arrays.asList(toVar(Server.NAME, Server.VAR_developers), toVar(Server.NAME, Server.VAR_tasks)); + } + + public Monitor() { + super(NAME); + + // Establish new session with LockManager + RemoteVar>> registeredClients = new RemoteVar<>(LockManager.NAME, + LockManager.VAR_clients); + Signal>> s = new Signal<>("s", () -> { + if (registeredClients.get() == null) + return new ArrayList>(); + else + return registeredClients.get(); + }, registeredClients); + s.change().addHandler((o, n) -> { + if (n.contains(new Pair<>(this.getHostName(), LockManager.VAR_requestLock)) && gui == null) + setup(); + }); + + lockRequest = new Var<>(LockManager.VAR_requestLock, false); + logger.fine("Setup: Waiting for Registration to LockManager ..."); + + } + + private void setup() { + gui = new MonitorGUI(this); + + devs = new RemoteVar(Server.NAME, Server.VAR_developers); + tasks = new RemoteVar(Server.NAME, Server.VAR_tasks); + } + + public static void main(String[] args) { + new Monitor(); + } + + public void clickButton() { + // TODO: lock + if (tasks.get() != null) + gui.setTasks(tasks.get()); + if (devs.get() != null) + gui.setDevs(devs.get()); + // TODO: unlock + } +} \ No newline at end of file From 3e82d640d296e9f77a8927aa5b3a4440698c124f Mon Sep 17 00:00:00 2001 From: Tobias Becker Date: Fri, 24 Jun 2016 15:01:34 +0200 Subject: [PATCH 135/161] scrumBoard: fixed bug in LockManager --- .../java/dream/examples/scrumBoard/atomic/LockManager.java | 1 + 1 file changed, 1 insertion(+) diff --git a/Dream2/src/examples/java/dream/examples/scrumBoard/atomic/LockManager.java b/Dream2/src/examples/java/dream/examples/scrumBoard/atomic/LockManager.java index 1d6990b..6b85d87 100644 --- a/Dream2/src/examples/java/dream/examples/scrumBoard/atomic/LockManager.java +++ b/Dream2/src/examples/java/dream/examples/scrumBoard/atomic/LockManager.java @@ -32,6 +32,7 @@ public LockManager() { super(NAME); clients = new Var<>(VAR_clients, new ArrayList<>()); lock = new Var<>(VAR_lock, ""); + lockRequests = new LinkedList<>(); detectNewSession(); } From 5b174de0387bd93c9e3a3f319d0f8a66ec6545c9 Mon Sep 17 00:00:00 2001 From: Tobias Becker Date: Fri, 24 Jun 2016 15:02:08 +0200 Subject: [PATCH 136/161] scrumBoard: refactored locking to common super class --- .../examples/scrumBoard/atomic/Creator.java | 32 ++------- .../scrumBoard/atomic/LockClient.java | 70 +++++++++++++++++++ .../examples/scrumBoard/atomic/Monitor.java | 33 ++------- 3 files changed, 78 insertions(+), 57 deletions(-) create mode 100644 Dream2/src/examples/java/dream/examples/scrumBoard/atomic/LockClient.java diff --git a/Dream2/src/examples/java/dream/examples/scrumBoard/atomic/Creator.java b/Dream2/src/examples/java/dream/examples/scrumBoard/atomic/Creator.java index b83b572..8cc9090 100644 --- a/Dream2/src/examples/java/dream/examples/scrumBoard/atomic/Creator.java +++ b/Dream2/src/examples/java/dream/examples/scrumBoard/atomic/Creator.java @@ -1,17 +1,12 @@ package dream.examples.scrumBoard.atomic; -import java.util.ArrayList; import java.util.LinkedList; import java.util.Random; import java.util.logging.Logger; -import dream.client.RemoteVar; -import dream.client.Signal; import dream.client.Var; import dream.examples.scrumBoard.common.Assignment; import dream.examples.scrumBoard.common.CreatorGUI; -import dream.examples.util.Client; -import dream.examples.util.Pair; /** * Interface to create new Tasks. May be started multiple times! @@ -19,36 +14,17 @@ * @author Min Yang * @author Tobias Becker */ -public class Creator extends Client implements dream.examples.scrumBoard.common.CreatorGUI.Creator { +public class Creator extends LockClient implements dream.examples.scrumBoard.common.CreatorGUI.Creator { public static final String VAR_newAssignment = "newAssign"; private Var> assignmentCreator; - private Var lockRequest; - public Creator() { super("Creator" + new Random().nextInt(1000)); - - // Establish new session with LockManager - RemoteVar>> registeredClients = new RemoteVar<>(LockManager.NAME, - LockManager.VAR_clients); - Signal>> s = new Signal<>("s", () -> { - if (registeredClients.get() == null) - return new ArrayList>(); - else - return registeredClients.get(); - }, registeredClients); - s.change().addHandler((o, n) -> { - if (n.contains(new Pair<>(this.getHostName(), LockManager.VAR_requestLock)) && assignmentCreator == null) - setup(); - }); - - lockRequest = new Var<>(LockManager.VAR_requestLock, false); - logger.fine("Setup: Waiting for Registration to LockManager ..."); } - private void setup() { + protected void setup() { assignmentCreator = new Var<>(VAR_newAssignment, new LinkedList<>()); new CreatorGUI(this); } @@ -62,8 +38,8 @@ public Logger getLogger() { } public void addAssignment(Assignment t) { - // TODO: lock + lock(); assignmentCreator.modify((old) -> old.addLast(t)); - // TODO: unlock + unlock(); } } diff --git a/Dream2/src/examples/java/dream/examples/scrumBoard/atomic/LockClient.java b/Dream2/src/examples/java/dream/examples/scrumBoard/atomic/LockClient.java new file mode 100644 index 0000000..46d6581 --- /dev/null +++ b/Dream2/src/examples/java/dream/examples/scrumBoard/atomic/LockClient.java @@ -0,0 +1,70 @@ +package dream.examples.scrumBoard.atomic; + +import java.util.ArrayList; + +import dream.client.RemoteVar; +import dream.client.Signal; +import dream.client.Var; +import dream.examples.util.Client; +import dream.examples.util.Pair; + +public abstract class LockClient extends Client { + private boolean setup = false; + private Var lockRequest; + private boolean hasLock; + + public LockClient(String name) { + super(name); + + // Establish new session with LockManager + RemoteVar>> registeredClients = new RemoteVar<>(LockManager.NAME, + LockManager.VAR_clients); + Signal>> s = new Signal<>("s", () -> { + if (registeredClients.get() == null) + return new ArrayList>(); + else + return registeredClients.get(); + }, registeredClients); + s.change().addHandler((o, n) -> { + if (n.contains(new Pair<>(this.getHostName(), LockManager.VAR_requestLock)) && setup == false) + lockSetup(); + }); + + lockRequest = new Var<>(LockManager.VAR_requestLock, false); + logger.fine("Setup: Waiting for Registration to LockManager ..."); + } + + private void lockSetup() { + setup = true; + RemoteVar lock = new RemoteVar<>(LockManager.NAME, LockManager.VAR_lock); + Signal sLock = new Signal<>("lock", () -> { + return lock.get(); + }, lock); + sLock.change().addHandler((oldValue, newValue) -> { + if (newValue.equals(getHostName())) { + hasLock = true; + } else + hasLock = false; + }); + + setup(); + } + + protected abstract void setup(); + + public void lock() { + lockRequest.set(true); + while (!hasLock) { + try { + Thread.sleep(10); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + + public void unlock() { + lockRequest.set(false); + } + +} diff --git a/Dream2/src/examples/java/dream/examples/scrumBoard/atomic/Monitor.java b/Dream2/src/examples/java/dream/examples/scrumBoard/atomic/Monitor.java index 8851ae5..6deae7f 100644 --- a/Dream2/src/examples/java/dream/examples/scrumBoard/atomic/Monitor.java +++ b/Dream2/src/examples/java/dream/examples/scrumBoard/atomic/Monitor.java @@ -1,16 +1,11 @@ package dream.examples.scrumBoard.atomic; -import java.util.ArrayList; import java.util.Arrays; import java.util.List; import dream.client.RemoteVar; -import dream.client.Signal; -import dream.client.Var; import dream.examples.scrumBoard.common.MonitorGUI; import dream.examples.scrumBoard.core.Server; -import dream.examples.util.Client; -import dream.examples.util.Pair; /** * Displays both lists, the developers and the tasks. @@ -18,7 +13,7 @@ * @author Min Yang * @author Tobias Becker */ -public class Monitor extends Client implements dream.examples.scrumBoard.common.MonitorGUI.Monitor { +public class Monitor extends LockClient implements dream.examples.scrumBoard.common.MonitorGUI.Monitor { public static final String NAME = "Monitor"; @@ -26,8 +21,6 @@ public class Monitor extends Client implements dream.examples.scrumBoard.common. private RemoteVar devs; private RemoteVar tasks; - private Var lockRequest; - @Override protected List waitForVars() { return Arrays.asList(toVar(Server.NAME, Server.VAR_developers), toVar(Server.NAME, Server.VAR_tasks)); @@ -35,27 +28,9 @@ protected List waitForVars() { public Monitor() { super(NAME); - - // Establish new session with LockManager - RemoteVar>> registeredClients = new RemoteVar<>(LockManager.NAME, - LockManager.VAR_clients); - Signal>> s = new Signal<>("s", () -> { - if (registeredClients.get() == null) - return new ArrayList>(); - else - return registeredClients.get(); - }, registeredClients); - s.change().addHandler((o, n) -> { - if (n.contains(new Pair<>(this.getHostName(), LockManager.VAR_requestLock)) && gui == null) - setup(); - }); - - lockRequest = new Var<>(LockManager.VAR_requestLock, false); - logger.fine("Setup: Waiting for Registration to LockManager ..."); - } - private void setup() { + protected void setup() { gui = new MonitorGUI(this); devs = new RemoteVar(Server.NAME, Server.VAR_developers); @@ -67,11 +42,11 @@ public static void main(String[] args) { } public void clickButton() { - // TODO: lock + lock(); if (tasks.get() != null) gui.setTasks(tasks.get()); if (devs.get() != null) gui.setDevs(devs.get()); - // TODO: unlock + unlock(); } } \ No newline at end of file From 88854fc62c9943a722c989fd60342fc054a8ef76 Mon Sep 17 00:00:00 2001 From: Tobias Becker Date: Sun, 26 Jun 2016 19:27:50 +0200 Subject: [PATCH 137/161] scrumBoard: added log messages --- .../dream/examples/scrumBoard/atomic/LockManager.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Dream2/src/examples/java/dream/examples/scrumBoard/atomic/LockManager.java b/Dream2/src/examples/java/dream/examples/scrumBoard/atomic/LockManager.java index 6b85d87..835ec14 100644 --- a/Dream2/src/examples/java/dream/examples/scrumBoard/atomic/LockManager.java +++ b/Dream2/src/examples/java/dream/examples/scrumBoard/atomic/LockManager.java @@ -64,21 +64,27 @@ private void createNewSessionFor(Pair x) { if (lock.get().equals("")) { // no lock present, granting lock.set(x.getFirst()); + logger.fine("Currently not locked, granting lock for: " + x.getFirst()); } else { // already locked, adding to queue lockRequests.add(x.getFirst()); + logger.fine("Already locked for \"" + lock.get() + "\". Adding \"" + x.getFirst() + "\"to queue"); } } else if (!newValue) { // client trying to release a lock if (lock.get().equals(x.getFirst())) { // client had the lock, releasing lock.set(""); + logger.fine("Releasing lock for:" + x.getFirst()); // granting lock request for next client - if (!lockRequests.isEmpty()) + if (!lockRequests.isEmpty()) { lock.set(lockRequests.poll()); + logger.fine("Processing next in queue"); + } } else { // client didn't have the lock -> withdrawing lock request lockRequests.remove(x.getFirst()); + logger.fine("\"" + x.getFirst() + "\" withdraw lock request"); } } }); From d9d70aa371be560ba4fd34efda22f7441f9d12f1 Mon Sep 17 00:00:00 2001 From: Tobias Becker Date: Fri, 1 Jul 2016 11:40:32 +0200 Subject: [PATCH 138/161] scrumBoard: refactored lock request to wait & notifiy --- .../scrumBoard/atomic/LockClient.java | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/Dream2/src/examples/java/dream/examples/scrumBoard/atomic/LockClient.java b/Dream2/src/examples/java/dream/examples/scrumBoard/atomic/LockClient.java index 46d6581..7984933 100644 --- a/Dream2/src/examples/java/dream/examples/scrumBoard/atomic/LockClient.java +++ b/Dream2/src/examples/java/dream/examples/scrumBoard/atomic/LockClient.java @@ -41,10 +41,13 @@ private void lockSetup() { return lock.get(); }, lock); sLock.change().addHandler((oldValue, newValue) -> { - if (newValue.equals(getHostName())) { + if (newValue.equals(getHostName())) hasLock = true; - } else + else hasLock = false; + synchronized (this) { + this.notify(); + } }); setup(); @@ -54,12 +57,13 @@ private void lockSetup() { public void lock() { lockRequest.set(true); - while (!hasLock) { - try { - Thread.sleep(10); - } catch (InterruptedException e) { - e.printStackTrace(); - } + synchronized (this) { + while (!hasLock) + try { + this.wait(); + } catch (InterruptedException e) { + e.printStackTrace(); + } } } From a3555b1eec4ce317abb67c069ecb7d47e8adeb5d Mon Sep 17 00:00:00 2001 From: Tobias Becker Date: Fri, 1 Jul 2016 11:53:01 +0200 Subject: [PATCH 139/161] form: updated queue solution to use ChangeEvents --- .../form/simple/GlitchFreeFormServer.java | 91 ++++++------------- 1 file changed, 28 insertions(+), 63 deletions(-) diff --git a/Dream2/src/examples/java/dream/examples/form/simple/GlitchFreeFormServer.java b/Dream2/src/examples/java/dream/examples/form/simple/GlitchFreeFormServer.java index 4622572..d98caab 100644 --- a/Dream2/src/examples/java/dream/examples/form/simple/GlitchFreeFormServer.java +++ b/Dream2/src/examples/java/dream/examples/form/simple/GlitchFreeFormServer.java @@ -3,46 +3,47 @@ import java.util.LinkedList; import dream.client.Signal; -import dream.examples.util.Pair; +import dream.client.Var; public class GlitchFreeFormServer extends FormServer { + final LinkedList minimumQueue = new LinkedList<>(); + final LinkedList maximumQueue = new LinkedList<>(); + final Var settingsOkay = new Var<>(SettingsOkay, false); + Signal minimumEuroPerHour; + + private void updateSettingsOkay() { + if (minimumQueue.size() > 0 && maximumQueue.size() > 0 && minimumEuroPerHour.get() != null) + settingsOkay.set(minimumQueue.pop() && maximumQueue.pop() && minimumEuroPerHour.get()); + } + @Override protected void createDependencies() { logger.fine("Building Dependencies"); - final UpdateCounter minimumCounter = new UpdateCounter(); - final UpdateCounter maximumCounter = new UpdateCounter(); + minimumEuroPerHour = new Signal<>(MinimumEuroPerHour, () -> { + return euro_per_hour.get() > 10; + }, euro_per_hour); - final Signal> minimumHours = new Signal<>(MinimumHours, () -> { - return new Pair<>(working_hours.get() > 10, minimumCounter.incAndGet()); + final Signal minimumHours = new Signal<>(MinimumHours, () -> { + return working_hours.get() > 10; }, working_hours); - final Signal> maximumHours = new Signal<>(MaximumHours, () -> { - return new Pair<>(working_hours.get() < 60, maximumCounter.incAndGet()); + final Signal maximumHours = new Signal<>(MaximumHours, () -> { + return working_hours.get() < 60; }, working_hours); - final Signal minimumEuroPerHour = new Signal<>(MinimumEuroPerHour, () -> { - return euro_per_hour.get() > 10; - }, euro_per_hour); - - final LinkedList> minimumQueue = new LinkedList<>(); - final LinkedList> maximumQueue = new LinkedList<>(); - final Value currentValue = new Value<>(false); + minimumEuroPerHour.change().addHandler((o, n) -> updateSettingsOkay()); - new Signal<>(SettingsOkay, () -> { - if (minimumHours.get() != null - && (minimumQueue.isEmpty() || minimumQueue.getLast().getSecond() < minimumHours.get().getSecond())) - minimumQueue.add(minimumHours.get()); - if (maximumHours.get() != null - && (maximumQueue.isEmpty() || maximumQueue.getLast().getSecond() < maximumHours.get().getSecond())) - maximumQueue.add(maximumHours.get()); + minimumHours.change().addHandler((o, n) -> { + minimumQueue.add(n); + updateSettingsOkay(); + }); - if (minimumQueue.size() > 0 && maximumQueue.size() > 0 && minimumEuroPerHour.get() != null) - currentValue.set( - minimumQueue.pop().getFirst() && maximumQueue.pop().getFirst() && minimumEuroPerHour.get()); - return currentValue.get(); - }, minimumHours, maximumHours, minimumEuroPerHour); + maximumHours.change().addHandler((o, n) -> { + maximumQueue.add(n); + updateSettingsOkay(); + }); new Signal<>(Salary, () -> { if (working_hours.get() != null && euro_per_hour.get() != null) @@ -57,40 +58,4 @@ protected void createDependencies() { public static void main(String[] args) { new GlitchFreeFormServer(); } -} - -class UpdateCounter { - private int i = 0; - - public void inc() { - i += 1; - } - - public int incAndGet() { - inc(); - return get(); - } - - public int get() { - return i; - } -} - -class Value { - private T value; - - public Value() { - } - - public Value(T init) { - set(init); - } - - public T get() { - return value; - } - - public void set(T v) { - value = v; - } -} +} \ No newline at end of file From fcdbdf8b1600e865928f2d31d216c6cf971c859c Mon Sep 17 00:00:00 2001 From: Tobias Becker Date: Fri, 1 Jul 2016 13:44:43 +0200 Subject: [PATCH 140/161] form: first step to a more advanced LockManager --- .../form/complete_glitchfree/Boss.java | 20 ++- .../form/complete_glitchfree/FormServer.java | 1 + .../form/complete_glitchfree/LockClient.java | 75 ++++++++ .../form/complete_glitchfree/LockManager.java | 170 ++++++++++++++++++ .../form/complete_glitchfree/Secretary.java | 13 +- 5 files changed, 267 insertions(+), 12 deletions(-) create mode 100644 Dream2/src/examples/java/dream/examples/form/complete_glitchfree/LockClient.java create mode 100644 Dream2/src/examples/java/dream/examples/form/complete_glitchfree/LockManager.java diff --git a/Dream2/src/examples/java/dream/examples/form/complete_glitchfree/Boss.java b/Dream2/src/examples/java/dream/examples/form/complete_glitchfree/Boss.java index 097f7ea..76f743f 100644 --- a/Dream2/src/examples/java/dream/examples/form/complete_glitchfree/Boss.java +++ b/Dream2/src/examples/java/dream/examples/form/complete_glitchfree/Boss.java @@ -1,10 +1,9 @@ package dream.examples.form.complete_glitchfree; import dream.client.Var; -import dream.examples.form.core.FormClient; import dream.examples.util.Pair; -public class Boss extends FormClient { +public class Boss extends LockClient { public static final String NAME = "Boss"; public static final String EuroPerHour = "euro_per_hour"; @@ -29,20 +28,23 @@ public void typedText(int i, String typedText) { switch (i) { case 0: Double value = Double.valueOf(typedText); - // TODO check locks + lock(Variable.get(eph)); eph.set(value); + unlock(); logger.fine("Set Euro_Per_Hour to " + value); break; case 1: Integer value2 = Integer.valueOf(typedText); - // TODO check locks + lock(Variable.get(rh)); rh.set(new Pair<>(value2, rh.get().getSecond())); + unlock(); logger.fine("Set minimum @ Required_Hours to " + value2); break; case 2: Integer value3 = Integer.valueOf(typedText); - // TODO check locks + lock(Variable.get(rh)); rh.set(new Pair<>(rh.get().getFirst(), value3)); + unlock(); logger.fine("Set maximum @ Required_Hours to " + value3); break; default: @@ -52,7 +54,11 @@ public void typedText(int i, String typedText) { } public static void main(String[] args) { - Boss b = new Boss(); - b.start(); + new Boss(); + } + + @Override + protected void setup() { + start(); } } diff --git a/Dream2/src/examples/java/dream/examples/form/complete_glitchfree/FormServer.java b/Dream2/src/examples/java/dream/examples/form/complete_glitchfree/FormServer.java index b617642..25cbb22 100644 --- a/Dream2/src/examples/java/dream/examples/form/complete_glitchfree/FormServer.java +++ b/Dream2/src/examples/java/dream/examples/form/complete_glitchfree/FormServer.java @@ -7,6 +7,7 @@ import dream.client.Signal; import dream.examples.util.Pair; +// TODO: refactor class setup and extends from LockClient public class FormServer extends dream.examples.form.core.FormServer { protected RemoteVar working_hours; diff --git a/Dream2/src/examples/java/dream/examples/form/complete_glitchfree/LockClient.java b/Dream2/src/examples/java/dream/examples/form/complete_glitchfree/LockClient.java new file mode 100644 index 0000000..3b0660f --- /dev/null +++ b/Dream2/src/examples/java/dream/examples/form/complete_glitchfree/LockClient.java @@ -0,0 +1,75 @@ +package dream.examples.form.complete_glitchfree; + +import java.util.ArrayList; + +import dream.client.RemoteVar; +import dream.client.Signal; +import dream.client.Var; +import dream.examples.form.core.FormClient; +import dream.examples.util.Pair; + +public abstract class LockClient extends FormClient { + private boolean setup = false; + private Var lockRequest; + private boolean hasLock; + + public LockClient(String name, String... labelText) { + super(name, labelText); + + // Establish new session with LockManager + RemoteVar>> registeredClients = new RemoteVar<>(LockManager.NAME, + LockManager.VAR_clients); + Signal>> s = new Signal<>("s", () -> { + if (registeredClients.get() == null) + return new ArrayList>(); + else + return registeredClients.get(); + }, registeredClients); + s.change().addHandler((o, n) -> { + if (n.contains(new Pair<>(this.getHostName(), LockManager.VAR_requestLock)) && setup == false) + lockSetup(); + }); + + lockRequest = new Var<>(LockManager.VAR_requestLock, null); + logger.fine("Setup: Waiting for Registration to LockManager ..."); + } + + private void lockSetup() { + setup = true; + RemoteVar lock = new RemoteVar<>(LockManager.NAME, LockManager.VAR_lock); + Signal sLock = new Signal<>("lock", () -> { + return lock.get(); + }, lock); + sLock.change().addHandler((oldValue, newValue) -> { + if (newValue.equals(getHostName())) + hasLock = true; + else + hasLock = false; + synchronized (this) { + this.notify(); + } + }); + + setup(); + } + + protected abstract void setup(); + + public void lock(Variable... vars) { + // TODO: add dependencies as well + lockRequest.set(new LockRequest(getHostName(), vars)); + synchronized (this) { + while (!hasLock) + try { + this.wait(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + + public void unlock() { + lockRequest.set(new LockRequest(getHostName())); + } + +} diff --git a/Dream2/src/examples/java/dream/examples/form/complete_glitchfree/LockManager.java b/Dream2/src/examples/java/dream/examples/form/complete_glitchfree/LockManager.java new file mode 100644 index 0000000..350d056 --- /dev/null +++ b/Dream2/src/examples/java/dream/examples/form/complete_glitchfree/LockManager.java @@ -0,0 +1,170 @@ +package dream.examples.form.complete_glitchfree; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.Set; +import java.util.logging.Level; + +import dream.client.DreamClient; +import dream.client.RemoteVar; +import dream.client.Signal; +import dream.client.Var; +import dream.examples.util.Client; +import dream.examples.util.Pair; + +public class LockManager extends Client { + + public static final String VAR_lock = "lock"; + public static final String VAR_requestLock = "requestLock"; + public static final String VAR_clients = "clients"; + public static final String NAME = "LockManager"; + + private Var lock; + private final Var> clients; + private LinkedList lockRequests; + + public LockManager() { + super(NAME); + clients = new Var<>(VAR_clients, new ArrayList<>()); + lock = new Var<>(VAR_lock, new Lock()); + lockRequests = new LinkedList<>(); + detectNewSession(); + } + + private void detectNewSession() { + Set vars = DreamClient.instance.listVariables(); + vars.stream().map(x -> new Variable(x.split("@")[1], x.split("@")[0])).// Pair(Host,Var) + filter(x -> !clients.get().contains(x) && x.getVar().equalsIgnoreCase(VAR_requestLock)).// + forEach(x -> createNewSessionFor(x)); + try { + Thread.sleep(500); + } catch (InterruptedException e) { + logger.log(Level.SEVERE, "Failed to sleep for 0.5 seconds", e); + } + + detectNewSession(); + } + + private void createNewSessionFor(Variable x) { + RemoteVar rv = new RemoteVar<>(x.getHost(), x.getVar()); + Signal s = new Signal<>(x.getHost() + "request", () -> { + if (rv.get() != null) + return rv.get(); + else + return null; + }, rv); + s.change().addHandler((oldValue, newValue) -> { + if (newValue.isLockRequest()) { + // client requesting a lock + if (!lock.get().isLocked(newValue.getVars())) { + lock.set(new Lock(lock.get(), newValue)); + } else { + // already locked, adding to queue + lockRequests.add(newValue); + } + } else { + // client trying to release a lock + lock.set(new Lock(lock.get(), newValue)); + removeLockRequestsFor(x.getHost()); + processNextRequest(); + } + }); + clients.modify(old -> old.add(x)); + } + + private void processNextRequest() { + for (LockRequest req : lockRequests) { + if (!lock.get().isLocked(req.getVars())) { + lock.set(new Lock(lock.get(), req)); + lockRequests.remove(req); + break; + } + } + } + + private void removeLockRequestsFor(String host) { + for (LockRequest req : lockRequests) { + if (req.getClient().equals(host)) { + lockRequests.remove(req); + } + } + } + + public static void main(String[] args) { + new LockManager(); + } +} + +class Variable extends Pair { + private static final long serialVersionUID = 4689891891604406371L; + + public Variable(String host, String var) { + super(host, var); + } + + public String getHost() { + return getFirst(); + } + + public String getVar() { + return getVar(); + } + + public static Variable get(Var v) { + return new Variable(v.getHost(), v.getObject()); + } + +} + +class LockRequest implements Serializable { + private static final long serialVersionUID = -7166632148414861582L; + private Variable[] vars; + private String client; + + public LockRequest(String client, Variable... vars) { + this.vars = vars; + this.client = client; + } + + public Variable[] getVars() { + return vars; + } + + public String getClient() { + return client; + } + + public boolean isLockRequest() { + return vars != null; + } +} + +class Lock extends HashMap { + private static final long serialVersionUID = -4195570287637533298L; + + public Lock() { + + } + + public Lock(Lock lock, LockRequest req) { + this.putAll(lock); + if (!req.isLockRequest()) { + // lock release + for (Variable v : keySet()) { + remove(v, req.getClient()); + } + } else { + for (int i = 0; i < req.getVars().length; i++) { + put(req.getVars()[i], req.getClient()); + } + } + } + + public boolean isLocked(Variable[] vars) { + return !Arrays.asList(vars).stream().anyMatch(x -> containsKey(x)); + } + +} \ No newline at end of file diff --git a/Dream2/src/examples/java/dream/examples/form/complete_glitchfree/Secretary.java b/Dream2/src/examples/java/dream/examples/form/complete_glitchfree/Secretary.java index 259b9b5..cdd81f3 100644 --- a/Dream2/src/examples/java/dream/examples/form/complete_glitchfree/Secretary.java +++ b/Dream2/src/examples/java/dream/examples/form/complete_glitchfree/Secretary.java @@ -1,9 +1,8 @@ package dream.examples.form.complete_glitchfree; import dream.client.Var; -import dream.examples.form.core.FormClient; -public class Secretary extends FormClient { +public class Secretary extends LockClient { public static final String NAME = "Secretary"; public static final String WorkingHours = "working_hours"; @@ -23,14 +22,18 @@ protected void init() { @Override public void typedText(int i, String typedText) { Integer value = Integer.valueOf(typedText); - // TODO check locks + lock(Variable.get(wh)); wh.set(value); + unlock(); logger.fine("Set Working_Hours to " + value); } public static void main(String[] args) { - Secretary s = new Secretary(); - s.start(); + new Secretary(); } + @Override + protected void setup() { + start(); + } } From abfd5393d63e555359e2e9b35784321722809e73 Mon Sep 17 00:00:00 2001 From: israphim Date: Fri, 1 Jul 2016 13:48:27 +0200 Subject: [PATCH 141/161] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ade1b82..ff41c14 100644 --- a/README.md +++ b/README.md @@ -51,10 +51,10 @@ Signal n = ``` ## Locks -When using Atomic_Consistency it is required to lock a Var before changing its value. +When using Atomic_Consistency it is required to lock a Var before reading its value. ``` LockToken lock = DreamClient.instance.readLock(new HashSet<>("exVar@Host1")); - myVar.set("CCC"); + String value = rv.get(); DreamClient.instance.unlock(lock); ``` From c3d498d376e86bc03d7b185d7d50170712f5cab7 Mon Sep 17 00:00:00 2001 From: Tobias Becker Date: Wed, 6 Jul 2016 13:37:02 +0200 Subject: [PATCH 142/161] refactored class setup and fixed a few bugs --- .../form/complete_glitchfree/Boss.java | 6 +- .../form/complete_glitchfree/FormClient.java | 71 ++++++++++ .../form/complete_glitchfree/FormGUI.java | 124 ++++++++++++++++++ .../form/complete_glitchfree/FormServer.java | 16 ++- .../form/complete_glitchfree/LockClient.java | 31 +++-- .../form/complete_glitchfree/LockManager.java | 20 ++- .../form/complete_glitchfree/Secretary.java | 7 +- 7 files changed, 246 insertions(+), 29 deletions(-) create mode 100644 Dream2/src/examples/java/dream/examples/form/complete_glitchfree/FormClient.java create mode 100644 Dream2/src/examples/java/dream/examples/form/complete_glitchfree/FormGUI.java diff --git a/Dream2/src/examples/java/dream/examples/form/complete_glitchfree/Boss.java b/Dream2/src/examples/java/dream/examples/form/complete_glitchfree/Boss.java index 76f743f..d278e17 100644 --- a/Dream2/src/examples/java/dream/examples/form/complete_glitchfree/Boss.java +++ b/Dream2/src/examples/java/dream/examples/form/complete_glitchfree/Boss.java @@ -3,7 +3,7 @@ import dream.client.Var; import dream.examples.util.Pair; -public class Boss extends LockClient { +public class Boss extends FormClient { public static final String NAME = "Boss"; public static final String EuroPerHour = "euro_per_hour"; @@ -57,8 +57,4 @@ public static void main(String[] args) { new Boss(); } - @Override - protected void setup() { - start(); - } } diff --git a/Dream2/src/examples/java/dream/examples/form/complete_glitchfree/FormClient.java b/Dream2/src/examples/java/dream/examples/form/complete_glitchfree/FormClient.java new file mode 100644 index 0000000..efe026a --- /dev/null +++ b/Dream2/src/examples/java/dream/examples/form/complete_glitchfree/FormClient.java @@ -0,0 +1,71 @@ +package dream.examples.form.complete_glitchfree; + +import java.awt.Color; +import java.util.Arrays; +import java.util.List; + +import dream.client.RemoteVar; +import dream.client.Signal; + +public abstract class FormClient extends LockClient { + + private RemoteVar salary; + private RemoteVar settings; + private Signal remoteSalary; + private Signal remoteSettings; + + private FormGUI gui; + private String[] labelText; + private String[] values; + + public FormClient(String name, String... labelText) { + super(name); + this.labelText = labelText; + } + + @Override + protected List waitForVars() { + return Arrays.asList(toVar(FormServer.NAME, FormServer.Salary), + toVar(FormServer.NAME, FormServer.SettingsOkay)); + } + + protected void start() { + gui = new FormGUI(getHostName(), labelText); + gui.setListener(this); + if (values != null) + gui.setInitValues(values); + + salary = new RemoteVar<>(FormServer.NAME, FormServer.Salary); + settings = new RemoteVar<>(FormServer.NAME, FormServer.SettingsOkay); + + remoteSalary = new Signal<>("remoteSalary", () -> { + if (salary.get() != null) + return salary.get(); + else + return 0.0; + }, salary); + + remoteSettings = new Signal<>("remoteSettings", () -> { + if (settings.get() != null) + return settings.get(); + else + return false; + }, settings); + + gui.setText("Salary: "); + gui.setColor(Color.red); + remoteSalary.change().addHandler((o, n) -> gui.setText("Salary: " + n.toString())); + remoteSettings.change().addHandler((o, n) -> gui.setColor((n ? Color.green : Color.red))); + } + + public abstract void typedText(int i, String typedText); + + public void setInitValues(String... values) { + this.values = values; + } + + @Override + protected void setup() { + start(); + } +} diff --git a/Dream2/src/examples/java/dream/examples/form/complete_glitchfree/FormGUI.java b/Dream2/src/examples/java/dream/examples/form/complete_glitchfree/FormGUI.java new file mode 100644 index 0000000..93f96dd --- /dev/null +++ b/Dream2/src/examples/java/dream/examples/form/complete_glitchfree/FormGUI.java @@ -0,0 +1,124 @@ +package dream.examples.form.complete_glitchfree; + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.GridLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.KeyEvent; +import java.awt.event.KeyListener; + +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JTextField; + +public class FormGUI extends JFrame { + + private static final long serialVersionUID = 9205614575242281482L; + private JTextField[] sendText; + private FormClient listener; + private JLabel display; + private String[] labelText; + + public FormGUI(String name, String... labelText) { + setTitle(name); + this.labelText = labelText; + initUI(); + } + + private void initUI() { + getContentPane().setLayout(new BorderLayout()); + JPanel all = new JPanel(); + all.setLayout(new GridLayout(0, 2)); + getContentPane().add(all, BorderLayout.CENTER); + + sendText = new JTextField[labelText.length]; + + for (int i = 0; i < labelText.length; i++) { + sendText[i] = new JTextField(20); + sendText[i].addKeyListener(new MultiListener(i)); + JButton sendButton = new JButton("Set"); + sendButton.addActionListener(new MultiListener(i)); + + JLabel label = new JLabel(labelText[i] + ": "); + + JPanel p = new JPanel(); + p.add(label); + p.add(sendText[i]); + + all.add(p); + all.add(sendButton); + } + + display = new JLabel(""); + display.setPreferredSize(new Dimension(100, 30)); + display.setMinimumSize(new Dimension(100, 30)); + display.setOpaque(true); + getContentPane().add(display, BorderLayout.NORTH); + + // setSize(300, 200); + setLocationRelativeTo(null); + setDefaultCloseOperation(EXIT_ON_CLOSE); + + pack(); + setVisible(true); + } + + public void setInitValues(String... values) { + for (int i = 0; i < values.length; i++) { + if (sendText[i].getText() == null || sendText[i].getText().isEmpty()) { + sendText[i].setText(values[i]); + } + } + } + + private void sendText(int i) { + listener.typedText(i, getTypedText(i)); + } + + public void setListener(FormClient c) { + listener = c; + } + + public String getTypedText(int i) { + return sendText[i].getText(); + } + + public void setText(String text) { + display.setText(text); + } + + public void setColor(Color bg) { + display.setBackground(bg); + } + + class MultiListener implements KeyListener, ActionListener { + private int i; + + public MultiListener(int i) { + this.i = i; + } + + @Override + public void keyTyped(KeyEvent e) { + } + + @Override + public void keyReleased(KeyEvent e) { + } + + @Override + public void keyPressed(KeyEvent e) { + if (e.getKeyCode() == KeyEvent.VK_ENTER) + sendText(i); + } + + @Override + public void actionPerformed(ActionEvent e) { + sendText(i); + } + } +} diff --git a/Dream2/src/examples/java/dream/examples/form/complete_glitchfree/FormServer.java b/Dream2/src/examples/java/dream/examples/form/complete_glitchfree/FormServer.java index 25cbb22..3789a9a 100644 --- a/Dream2/src/examples/java/dream/examples/form/complete_glitchfree/FormServer.java +++ b/Dream2/src/examples/java/dream/examples/form/complete_glitchfree/FormServer.java @@ -7,14 +7,21 @@ import dream.client.Signal; import dream.examples.util.Pair; -// TODO: refactor class setup and extends from LockClient -public class FormServer extends dream.examples.form.core.FormServer { +public class FormServer extends LockClient { protected RemoteVar working_hours; protected RemoteVar euro_per_hour; protected RemoteVar> required_hours; + public static final String NAME = "FormServer"; + public static final String MinimumHours = "minimumHours"; + public static final String MaximumHours = "maximumHours"; + public static final String MinimumEuroPerHour = "minimumEuroPerHour"; + public static final String SettingsOkay = "settingsOkay"; + public static final String Salary = "salary"; + public FormServer() { + super(NAME); detectNewSession(); } @@ -90,4 +97,9 @@ protected void createDependencies() { public static void main(String[] args) { new FormServer(); } + + @Override + protected void setup() { + } + } diff --git a/Dream2/src/examples/java/dream/examples/form/complete_glitchfree/LockClient.java b/Dream2/src/examples/java/dream/examples/form/complete_glitchfree/LockClient.java index 3b0660f..7b88796 100644 --- a/Dream2/src/examples/java/dream/examples/form/complete_glitchfree/LockClient.java +++ b/Dream2/src/examples/java/dream/examples/form/complete_glitchfree/LockClient.java @@ -1,20 +1,22 @@ package dream.examples.form.complete_glitchfree; import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; import dream.client.RemoteVar; import dream.client.Signal; import dream.client.Var; -import dream.examples.form.core.FormClient; +import dream.examples.util.Client; import dream.examples.util.Pair; -public abstract class LockClient extends FormClient { +public abstract class LockClient extends Client { private boolean setup = false; private Var lockRequest; - private boolean hasLock; + private List hasLock; - public LockClient(String name, String... labelText) { - super(name, labelText); + public LockClient(String name) { + super(name); // Establish new session with LockManager RemoteVar>> registeredClients = new RemoteVar<>(LockManager.NAME, @@ -30,21 +32,24 @@ public LockClient(String name, String... labelText) { lockSetup(); }); + hasLock = new ArrayList<>(); lockRequest = new Var<>(LockManager.VAR_requestLock, null); logger.fine("Setup: Waiting for Registration to LockManager ..."); } private void lockSetup() { setup = true; - RemoteVar lock = new RemoteVar<>(LockManager.NAME, LockManager.VAR_lock); - Signal sLock = new Signal<>("lock", () -> { + RemoteVar lock = new RemoteVar<>(LockManager.NAME, LockManager.VAR_lock); + Signal sLock = new Signal<>("lock", () -> { return lock.get(); }, lock); sLock.change().addHandler((oldValue, newValue) -> { - if (newValue.equals(getHostName())) - hasLock = true; - else - hasLock = false; + newValue.forEach((var, client) -> { + if (client.equals(getHostName())) + hasLock.add(var); + else + hasLock.remove(var); + }); synchronized (this) { this.notify(); } @@ -56,15 +61,15 @@ private void lockSetup() { protected abstract void setup(); public void lock(Variable... vars) { - // TODO: add dependencies as well lockRequest.set(new LockRequest(getHostName(), vars)); synchronized (this) { - while (!hasLock) + while (!hasLock.containsAll(Arrays.asList(vars))) { try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } + } } } diff --git a/Dream2/src/examples/java/dream/examples/form/complete_glitchfree/LockManager.java b/Dream2/src/examples/java/dream/examples/form/complete_glitchfree/LockManager.java index 350d056..6a3d542 100644 --- a/Dream2/src/examples/java/dream/examples/form/complete_glitchfree/LockManager.java +++ b/Dream2/src/examples/java/dream/examples/form/complete_glitchfree/LockManager.java @@ -110,13 +110,17 @@ public String getHost() { } public String getVar() { - return getVar(); + return getSecond(); } public static Variable get(Var v) { return new Variable(v.getHost(), v.getObject()); } + @Override + public String toString() { + return "Var" + super.toString(); + } } class LockRequest implements Serializable { @@ -138,7 +142,17 @@ public String getClient() { } public boolean isLockRequest() { - return vars != null; + return vars.length > 0; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + if (isLockRequest()) + sb.append("LockRequest(").append(client).append("->").append(Arrays.toString(vars)).append(")"); + else + sb.append("LockRelease()"); + return sb.toString(); } } @@ -164,7 +178,7 @@ public Lock(Lock lock, LockRequest req) { } public boolean isLocked(Variable[] vars) { - return !Arrays.asList(vars).stream().anyMatch(x -> containsKey(x)); + return Arrays.asList(vars).stream().anyMatch(x -> containsKey(x)); } } \ No newline at end of file diff --git a/Dream2/src/examples/java/dream/examples/form/complete_glitchfree/Secretary.java b/Dream2/src/examples/java/dream/examples/form/complete_glitchfree/Secretary.java index cdd81f3..638968e 100644 --- a/Dream2/src/examples/java/dream/examples/form/complete_glitchfree/Secretary.java +++ b/Dream2/src/examples/java/dream/examples/form/complete_glitchfree/Secretary.java @@ -2,7 +2,7 @@ import dream.client.Var; -public class Secretary extends LockClient { +public class Secretary extends FormClient { public static final String NAME = "Secretary"; public static final String WorkingHours = "working_hours"; @@ -31,9 +31,4 @@ public void typedText(int i, String typedText) { public static void main(String[] args) { new Secretary(); } - - @Override - protected void setup() { - start(); - } } From 3d3e383815bfbd1a338be949b9db38de9eb7297e Mon Sep 17 00:00:00 2001 From: Tobias Becker Date: Wed, 6 Jul 2016 14:54:58 +0200 Subject: [PATCH 143/161] form: added locking of dependent vars --- .../form/complete_glitchfree/LockManager.java | 54 ++++++++++++++++--- 1 file changed, 47 insertions(+), 7 deletions(-) diff --git a/Dream2/src/examples/java/dream/examples/form/complete_glitchfree/LockManager.java b/Dream2/src/examples/java/dream/examples/form/complete_glitchfree/LockManager.java index 6a3d542..d925fd0 100644 --- a/Dream2/src/examples/java/dream/examples/form/complete_glitchfree/LockManager.java +++ b/Dream2/src/examples/java/dream/examples/form/complete_glitchfree/LockManager.java @@ -3,15 +3,20 @@ import java.io.Serializable; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.HashMap; +import java.util.Iterator; import java.util.LinkedList; +import java.util.Map; import java.util.Set; +import java.util.concurrent.CopyOnWriteArrayList; import java.util.logging.Level; import dream.client.DreamClient; import dream.client.RemoteVar; import dream.client.Signal; import dream.client.Var; +import dream.common.utils.DependencyGraph; import dream.examples.util.Client; import dream.examples.util.Pair; @@ -117,6 +122,15 @@ public static Variable get(Var v) { return new Variable(v.getHost(), v.getObject()); } + public static Variable fromDreamString(String s) { + String[] temp = s.split("@", 2); + return new Variable(temp[1], temp[0]); + } + + public String toDreamString() { + return getVar() + "@" + getHost(); + } + @Override public String toString() { return "Var" + super.toString(); @@ -125,16 +139,41 @@ public String toString() { class LockRequest implements Serializable { private static final long serialVersionUID = -7166632148414861582L; - private Variable[] vars; + private CopyOnWriteArrayList vars; private String client; public LockRequest(String client, Variable... vars) { - this.vars = vars; + this.vars = new CopyOnWriteArrayList<>(); + this.vars.addAll(Arrays.asList(vars)); this.client = client; + + computeDependencies(); + } + + private void computeDependencies() { + Map> nodes = new HashMap<>(); + DependencyGraph.instance.getGraph().forEach((v1, v2) -> { + Collection t = new LinkedList<>(); + v2.forEach(x -> t.add(Variable.fromDreamString(x))); + nodes.put(Variable.fromDreamString(v1), t); + }); + for (Variable v : vars) { + computeDependencies(nodes, v); + } + } + + private void computeDependencies(Map> nodes, Variable v) { + nodes.forEach((v1, v2) -> { + if (v2.contains(v) && !vars.contains(v1)) { + // v1 depends on v + this.vars.add(v1); + computeDependencies(nodes, v1); + } + }); } public Variable[] getVars() { - return vars; + return vars.toArray(new Variable[] {}); } public String getClient() { @@ -142,14 +181,14 @@ public String getClient() { } public boolean isLockRequest() { - return vars.length > 0; + return vars.size() > 0; } @Override public String toString() { StringBuilder sb = new StringBuilder(); if (isLockRequest()) - sb.append("LockRequest(").append(client).append("->").append(Arrays.toString(vars)).append(")"); + sb.append("LockRequest(").append(client).append("->").append(vars).append(")"); else sb.append("LockRelease()"); return sb.toString(); @@ -167,8 +206,9 @@ public Lock(Lock lock, LockRequest req) { this.putAll(lock); if (!req.isLockRequest()) { // lock release - for (Variable v : keySet()) { - remove(v, req.getClient()); + for (Iterator k = keySet().iterator(); k.hasNext();) { + if (get(k.next()).equals(req.getClient())) + k.remove(); } } else { for (int i = 0; i < req.getVars().length; i++) { From 6e66aaeb4318e4bd5dbd52c13d914a9236d5987f Mon Sep 17 00:00:00 2001 From: Tobias Becker Date: Sat, 16 Jul 2016 13:26:50 +0200 Subject: [PATCH 144/161] Chat: added line count --- .../java/dream/examples/chat/line-count.core.txt | 8 ++++++++ .../examples/chat/line-count.diff.core.fifo.txt | 16 ++++++++++++++++ .../java/dream/examples/chat/line-count.fifo.txt | 8 ++++++++ 3 files changed, 32 insertions(+) create mode 100644 Dream2/src/examples/java/dream/examples/chat/line-count.core.txt create mode 100644 Dream2/src/examples/java/dream/examples/chat/line-count.diff.core.fifo.txt create mode 100644 Dream2/src/examples/java/dream/examples/chat/line-count.fifo.txt diff --git a/Dream2/src/examples/java/dream/examples/chat/line-count.core.txt b/Dream2/src/examples/java/dream/examples/chat/line-count.core.txt new file mode 100644 index 0000000..064384d --- /dev/null +++ b/Dream2/src/examples/java/dream/examples/chat/line-count.core.txt @@ -0,0 +1,8 @@ +http://cloc.sourceforge.net v 1.64 T=0.02 s (145.4 files/s, 32269.1 lines/s) +------------------------------------------------------------------------------- +Language files blank comment code +------------------------------------------------------------------------------- +Java 3 95 55 516 +------------------------------------------------------------------------------- +SUM: 3 95 55 516 +------------------------------------------------------------------------------- diff --git a/Dream2/src/examples/java/dream/examples/chat/line-count.diff.core.fifo.txt b/Dream2/src/examples/java/dream/examples/chat/line-count.diff.core.fifo.txt new file mode 100644 index 0000000..ca3fd13 --- /dev/null +++ b/Dream2/src/examples/java/dream/examples/chat/line-count.diff.core.fifo.txt @@ -0,0 +1,16 @@ +http://cloc.sourceforge.net v 1.64 T=0.08 s (13.1 files/s, 13.1 lines/s) +------------------------------------------------------------------------------- +Language files blank comment code +------------------------------------------------------------------------------- +Java + same 0 0 0 20 + modified 1 0 8 59 + added 1 31 75 153 + removed 2 79 47 437 +------------------------------------------------------------------------------- +SUM: + same 0 0 0 20 + modified 1 0 8 59 + added 1 31 75 153 + removed 2 79 47 437 +------------------------------------------------------------------------------- diff --git a/Dream2/src/examples/java/dream/examples/chat/line-count.fifo.txt b/Dream2/src/examples/java/dream/examples/chat/line-count.fifo.txt new file mode 100644 index 0000000..c974ab7 --- /dev/null +++ b/Dream2/src/examples/java/dream/examples/chat/line-count.fifo.txt @@ -0,0 +1,8 @@ +http://cloc.sourceforge.net v 1.64 T=0.03 s (57.9 files/s, 10488.8 lines/s) +------------------------------------------------------------------------------- +Language files blank comment code +------------------------------------------------------------------------------- +Java 2 47 83 232 +------------------------------------------------------------------------------- +SUM: 2 47 83 232 +------------------------------------------------------------------------------- From be038b63815c3c8ce4763beb49e64bf64d00001b Mon Sep 17 00:00:00 2001 From: Tobias Becker Date: Sat, 16 Jul 2016 13:27:09 +0200 Subject: [PATCH 145/161] Form: added line-count --- ...e-count-advanced-complete-glitch-freedom.txt | 17 +++++++++++++++++ .../examples/form/line-count-advanced-dream.txt | 14 ++++++++++++++ .../examples/form/line-count-simple-dream.txt | 14 ++++++++++++++ .../line-count-simple-single-glitch-freedom.txt | 14 ++++++++++++++ 4 files changed, 59 insertions(+) create mode 100644 Dream2/src/examples/java/dream/examples/form/line-count-advanced-complete-glitch-freedom.txt create mode 100644 Dream2/src/examples/java/dream/examples/form/line-count-advanced-dream.txt create mode 100644 Dream2/src/examples/java/dream/examples/form/line-count-simple-dream.txt create mode 100644 Dream2/src/examples/java/dream/examples/form/line-count-simple-single-glitch-freedom.txt diff --git a/Dream2/src/examples/java/dream/examples/form/line-count-advanced-complete-glitch-freedom.txt b/Dream2/src/examples/java/dream/examples/form/line-count-advanced-complete-glitch-freedom.txt new file mode 100644 index 0000000..14deb29 --- /dev/null +++ b/Dream2/src/examples/java/dream/examples/form/line-count-advanced-complete-glitch-freedom.txt @@ -0,0 +1,17 @@ +complete_glitchfree/Boss.java +complete_glitchfree/Secretary.java +complete_glitchfree/FormClient.java +complete_glitchfree/FormGUI.java +complete_glitchfree/FormServer.java +complete_glitchfree/LockClient.java +complete_glitchfree/CompleteGlitchFreeFormServer.java +complete_glitchfree/LockManager.java + +http://cloc.sourceforge.net v 1.64 T=0.03 s (314.6 files/s, 29496.1 lines/s) +------------------------------------------------------------------------------- +Language files blank comment code +------------------------------------------------------------------------------- +Java 8 132 11 607 +------------------------------------------------------------------------------- +SUM: 8 132 11 607 +------------------------------------------------------------------------------- diff --git a/Dream2/src/examples/java/dream/examples/form/line-count-advanced-dream.txt b/Dream2/src/examples/java/dream/examples/form/line-count-advanced-dream.txt new file mode 100644 index 0000000..9ba9b2d --- /dev/null +++ b/Dream2/src/examples/java/dream/examples/form/line-count-advanced-dream.txt @@ -0,0 +1,14 @@ +complete_glitchfree/Boss.java +complete_glitchfree/Secretary.java +complete_glitchfree/FormClient.java +complete_glitchfree/FormGUI.java +complete_glitchfree/FormServer.java + +http://cloc.sourceforge.net v 1.64 T=0.02 s (263.4 files/s, 20755.3 lines/s) +------------------------------------------------------------------------------- +Language files blank comment code +------------------------------------------------------------------------------- +Java 5 76 4 314 +------------------------------------------------------------------------------- +SUM: 5 76 4 314 +------------------------------------------------------------------------------- diff --git a/Dream2/src/examples/java/dream/examples/form/line-count-simple-dream.txt b/Dream2/src/examples/java/dream/examples/form/line-count-simple-dream.txt new file mode 100644 index 0000000..35c620c --- /dev/null +++ b/Dream2/src/examples/java/dream/examples/form/line-count-simple-dream.txt @@ -0,0 +1,14 @@ +Counted files: +core +simple/Boss.java +simple/Secretary.java +simple/FormServer.java + +http://cloc.sourceforge.net v 1.64 T=0.02 s (295.7 files/s, 18678.1 lines/s) +------------------------------------------------------------------------------- +Language files blank comment code +------------------------------------------------------------------------------- +Java 6 78 4 297 +------------------------------------------------------------------------------- +SUM: 6 78 4 297 +------------------------------------------------------------------------------- diff --git a/Dream2/src/examples/java/dream/examples/form/line-count-simple-single-glitch-freedom.txt b/Dream2/src/examples/java/dream/examples/form/line-count-simple-single-glitch-freedom.txt new file mode 100644 index 0000000..735239a --- /dev/null +++ b/Dream2/src/examples/java/dream/examples/form/line-count-simple-single-glitch-freedom.txt @@ -0,0 +1,14 @@ +Counted Files: +core +simple/Boss.java +simple/Secretary.java +simple/GlitchFreeFormServer.java + +http://cloc.sourceforge.net v 1.64 T=0.02 s (287.0 files/s, 16886.7 lines/s) +------------------------------------------------------------------------------- +Language files blank comment code +------------------------------------------------------------------------------- +Java 6 79 1 273 +------------------------------------------------------------------------------- +SUM: 6 79 1 273 +------------------------------------------------------------------------------- From f48e1c58d402d3f74c79f4e5019dd1e2fccea2d2 Mon Sep 17 00:00:00 2001 From: Tobias Becker Date: Sat, 16 Jul 2016 13:27:25 +0200 Subject: [PATCH 146/161] scrumBoard: added line count --- .../examples/scrumBoard/line-count.atomic.txt | 8 ++++++++ .../examples/scrumBoard/line-count.core.txt | 8 ++++++++ .../scrumBoard/line-count.diff.core.atomic.txt | 16 ++++++++++++++++ 3 files changed, 32 insertions(+) create mode 100644 Dream2/src/examples/java/dream/examples/scrumBoard/line-count.atomic.txt create mode 100644 Dream2/src/examples/java/dream/examples/scrumBoard/line-count.core.txt create mode 100644 Dream2/src/examples/java/dream/examples/scrumBoard/line-count.diff.core.atomic.txt diff --git a/Dream2/src/examples/java/dream/examples/scrumBoard/line-count.atomic.txt b/Dream2/src/examples/java/dream/examples/scrumBoard/line-count.atomic.txt new file mode 100644 index 0000000..9fb4c61 --- /dev/null +++ b/Dream2/src/examples/java/dream/examples/scrumBoard/line-count.atomic.txt @@ -0,0 +1,8 @@ +http://cloc.sourceforge.net v 1.64 T=0.25 s (20.1 files/s, 1183.5 lines/s) +------------------------------------------------------------------------------- +Language files blank comment code +------------------------------------------------------------------------------- +Java 5 46 35 213 +------------------------------------------------------------------------------- +SUM: 5 46 35 213 +------------------------------------------------------------------------------- diff --git a/Dream2/src/examples/java/dream/examples/scrumBoard/line-count.core.txt b/Dream2/src/examples/java/dream/examples/scrumBoard/line-count.core.txt new file mode 100644 index 0000000..10501c8 --- /dev/null +++ b/Dream2/src/examples/java/dream/examples/scrumBoard/line-count.core.txt @@ -0,0 +1,8 @@ +http://cloc.sourceforge.net v 1.64 T=0.09 s (46.2 files/s, 2643.9 lines/s) +------------------------------------------------------------------------------- +Language files blank comment code +------------------------------------------------------------------------------- +Java 4 41 33 155 +------------------------------------------------------------------------------- +SUM: 4 41 33 155 +------------------------------------------------------------------------------- diff --git a/Dream2/src/examples/java/dream/examples/scrumBoard/line-count.diff.core.atomic.txt b/Dream2/src/examples/java/dream/examples/scrumBoard/line-count.diff.core.atomic.txt new file mode 100644 index 0000000..ab54d76 --- /dev/null +++ b/Dream2/src/examples/java/dream/examples/scrumBoard/line-count.diff.core.atomic.txt @@ -0,0 +1,16 @@ +http://cloc.sourceforge.net v 1.64 T=0.28 s (3.5 files/s, 3.5 lines/s) +------------------------------------------------------------------------------- +Language files blank comment code +------------------------------------------------------------------------------- +Java + same 0 0 22 59 + modified 3 0 0 10 + added 2 23 13 144 + removed 1 18 11 86 +------------------------------------------------------------------------------- +SUM: + same 0 0 22 59 + modified 3 0 0 10 + added 2 23 13 144 + removed 1 18 11 86 +------------------------------------------------------------------------------- From 7cd06a0e8fbb255696e41fab3d438c92c50e2a01 Mon Sep 17 00:00:00 2001 From: guidosalva Date: Mon, 25 Jul 2016 18:59:02 +0200 Subject: [PATCH 147/161] Create LineCount.txt --- Dream2/src/examples/java/dream/examples/LineCount.txt | 1 + 1 file changed, 1 insertion(+) create mode 100644 Dream2/src/examples/java/dream/examples/LineCount.txt diff --git a/Dream2/src/examples/java/dream/examples/LineCount.txt b/Dream2/src/examples/java/dream/examples/LineCount.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/Dream2/src/examples/java/dream/examples/LineCount.txt @@ -0,0 +1 @@ + From 40027c450889450d29158990bbebe868d49d0b92 Mon Sep 17 00:00:00 2001 From: guidosalva Date: Mon, 25 Jul 2016 19:03:41 +0200 Subject: [PATCH 148/161] Update LineCount.txt --- .../java/dream/examples/LineCount.txt | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/Dream2/src/examples/java/dream/examples/LineCount.txt b/Dream2/src/examples/java/dream/examples/LineCount.txt index 8b13789..fb609dd 100644 --- a/Dream2/src/examples/java/dream/examples/LineCount.txt +++ b/Dream2/src/examples/java/dream/examples/LineCount.txt @@ -1 +1,20 @@ + + + +Distributed Form +Single-source glitch freedom with DREAM: ZZZ + .... + .... + + +Management applciation +causal consistency with DREAM: XXX +causal consistency without DREAM: YYY + + +Scrum board + + + +Chat application From 153e9a93de88ebb1aa609cf8e1a46c24006495aa Mon Sep 17 00:00:00 2001 From: Tobias Becker Date: Thu, 4 Aug 2016 13:39:36 +0200 Subject: [PATCH 149/161] Updated LineCount.txt --- .../java/dream/examples/LineCount.txt | 24 +++++++++---------- ...ine-count-simple-single-glitch-freedom.txt | 7 +++--- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/Dream2/src/examples/java/dream/examples/LineCount.txt b/Dream2/src/examples/java/dream/examples/LineCount.txt index fb609dd..dc926e5 100644 --- a/Dream2/src/examples/java/dream/examples/LineCount.txt +++ b/Dream2/src/examples/java/dream/examples/LineCount.txt @@ -1,20 +1,20 @@ - - - - Distributed Form -Single-source glitch freedom with DREAM: ZZZ - .... - .... + Single-source glitch freedom with DREAM: 297 + Single-source glitch freedom without DREAM: 343 + Complete glitch freedom with DREAM: 314 + Complete glitch freedom without DREAM: 607 -Management applciation -causal consistency with DREAM: XXX -causal consistency without DREAM: YYY +Management application + causal consistency with DREAM: XXX + causal consistency without DREAM: YYY Scrum board + atomic consistency with Dream: 155 + atomic consistency without Dream: 213 - -Chat application +Chat application + fifo consistency with Dream: 516 + fifo consistency without Dream: 798 \ No newline at end of file diff --git a/Dream2/src/examples/java/dream/examples/form/line-count-simple-single-glitch-freedom.txt b/Dream2/src/examples/java/dream/examples/form/line-count-simple-single-glitch-freedom.txt index 735239a..47cfd91 100644 --- a/Dream2/src/examples/java/dream/examples/form/line-count-simple-single-glitch-freedom.txt +++ b/Dream2/src/examples/java/dream/examples/form/line-count-simple-single-glitch-freedom.txt @@ -2,13 +2,14 @@ Counted Files: core simple/Boss.java simple/Secretary.java +simple/FormServer.java simple/GlitchFreeFormServer.java -http://cloc.sourceforge.net v 1.64 T=0.02 s (287.0 files/s, 16886.7 lines/s) +http://cloc.sourceforge.net v 1.64 T=0.03 s (279.9 files/s, 17590.9 lines/s) ------------------------------------------------------------------------------- Language files blank comment code ------------------------------------------------------------------------------- -Java 6 79 1 273 +Java 7 93 4 343 ------------------------------------------------------------------------------- -SUM: 6 79 1 273 +SUM: 7 93 4 343 ------------------------------------------------------------------------------- From 0e7bb79b153dfde5ec6d6d44629c4116f023fdd8 Mon Sep 17 00:00:00 2001 From: Alessandro Margara Date: Tue, 8 Nov 2016 15:34:52 +0100 Subject: [PATCH 150/161] New plot script --- .../scripts/plotScriptSidUpIntervals.plot | 46 +++++++++++-------- 1 file changed, 26 insertions(+), 20 deletions(-) diff --git a/DreamSim/scripts/plotScriptSidUpIntervals.plot b/DreamSim/scripts/plotScriptSidUpIntervals.plot index ff1149e..bbc5766 100644 --- a/DreamSim/scripts/plotScriptSidUpIntervals.plot +++ b/DreamSim/scripts/plotScriptSidUpIntervals.plot @@ -1,6 +1,6 @@ -set terminal postscript enhance color dashed dl 4 font 16 -set pointsize 3 -# set size ratio 0.55 +set terminal pdf enhance fsize 14 +set pointsize 2 +#set size ratio 0.8 set style line 1 lw 4 lt rgb 'orange' pt 1 set style line 2 lw 4 lt rgb 'black' pt 6 @@ -10,6 +10,7 @@ set style line 5 lw 4 lt rgb 'red' pt 2 set key above set log y +set format y "10^%T" ############ # LOCALITY # @@ -17,7 +18,7 @@ set log y set xlabel "Degree of locality" offset 0,0.2 -set output "../graphs/localityDelay.ps" +set output "../graphs/localityDelay.pdf" set ylabel "Average Delay (ms)" offset 0.5,0 plot "../resultsAvg/locality_causal_DelayAvg" u 1:2:3 t "Causal" w yerrorbars ls 1, \ @@ -31,7 +32,7 @@ plot "../resultsAvg/locality_causal_DelayAvg" u 1:2:3 t "Causal" w yerrorbars ls "../resultsAvg/locality_atomic_DelayAvg" u 1:2 notitle w lines ls 4, \ "../resultsAvg/locality_sid_up_DelayAvg" u 1:2 notitle w lines ls 5 -set output "../graphs/localityTraffic.ps" +set output "../graphs/localityTraffic.pdf" set ylabel "Overall Traffic (KB/s)" offset 0.5,0 plot "../resultsAvg/locality_causal_TrafficByte" u ($1):($8/1000):($15/1000) t "Causal" w yerrorbars ls 1, \ @@ -51,7 +52,7 @@ plot "../resultsAvg/locality_causal_TrafficByte" u ($1):($8/1000):($15/1000) t " set xlabel "Number of brokers" offset 0,0.2 -set output "../graphs/numBrokersDelay.ps" +set output "../graphs/numBrokersDelay.pdf" set ylabel "Average Delay (ms)" offset 0.5,0 plot "../resultsAvg/numBrokers_causal_DelayAvg" u 1:2:3 t "Causal" w yerrorbars ls 1, \ @@ -65,7 +66,7 @@ plot "../resultsAvg/numBrokers_causal_DelayAvg" u 1:2:3 t "Causal" w yerrorbars "../resultsAvg/numBrokers_atomic_DelayAvg" u 1:2 notitle w lines ls 4, \ "../resultsAvg/numBrokers_sid_up_DelayAvg" u 1:2 notitle w lines ls 5 -set output "../graphs/numBrokersTraffic.ps" +set output "../graphs/numBrokersTraffic.pdf" set ylabel "Overall Traffic (KB/s)" offset 0.5,0 plot "../resultsAvg/numBrokers_causal_TrafficByte" u ($1):($8/1000):($15/1000) t "Causal" w yerrorbars ls 1, \ @@ -85,7 +86,7 @@ plot "../resultsAvg/numBrokers_causal_TrafficByte" u ($1):($8/1000):($15/1000) t set xlabel "Number of sources (vars) in the graph" offset 0,0.2 -set output "../graphs/numVarsDelay.ps" +set output "../graphs/numVarsDelay.pdf" set ylabel "Average Delay (ms)" offset 0.5,0 plot "../resultsAvg/numVars_causal_DelayAvg" u 1:2:3 t "Causal" w yerrorbars ls 1, \ @@ -99,7 +100,7 @@ plot "../resultsAvg/numVars_causal_DelayAvg" u 1:2:3 t "Causal" w yerrorbars ls "../resultsAvg/numVars_atomic_DelayAvg" u 1:2 notitle w lines ls 4, \ "../resultsAvg/numVars_sid_up_DelayAvg" u 1:2 notitle w lines ls 5 -set output "../graphs/numVarsTraffic.ps" +set output "../graphs/numVarsTraffic.pdf" set ylabel "Overall Traffic (KB/s)" offset 0.5,0 plot "../resultsAvg/numVars_causal_TrafficByte" u ($1):($8/1000):($15/1000) t "Causal" w yerrorbars ls 1, \ @@ -119,7 +120,7 @@ plot "../resultsAvg/numVars_causal_TrafficByte" u ($1):($8/1000):($15/1000) t "C set xlabel "Depth of the dependency graph" offset 0,0.2 -set output "../graphs/graphDepthDelay.ps" +set output "../graphs/graphDepthDelay.pdf" set ylabel "Average Delay (ms)" offset 0.5,0 plot "../resultsAvg/graphDepth_causal_DelayAvg" u 1:2:3 t "Causal" w yerrorbars ls 1, \ @@ -133,7 +134,7 @@ plot "../resultsAvg/graphDepth_causal_DelayAvg" u 1:2:3 t "Causal" w yerrorbars "../resultsAvg/graphDepth_atomic_DelayAvg" u 1:2 notitle w lines ls 4, \ "../resultsAvg/graphDepth_sid_up_DelayAvg" u 1:2 notitle w lines ls 5 -set output "../graphs/graphDepthTraffic.ps" +set output "../graphs/graphDepthTraffic.pdf" set ylabel "Overall Traffic (KB/s)" offset 0.5,0 plot "../resultsAvg/graphDepth_causal_TrafficByte" u ($1):($8/1000):($15/1000) t "Causal" w yerrorbars ls 1, \ @@ -153,7 +154,7 @@ plot "../resultsAvg/graphDepth_causal_TrafficByte" u ($1):($8/1000):($15/1000) t set xlabel "Number of dependencies per signal" offset 0,0.2 -set output "../graphs/numGraphDependenciesDelay.ps" +set output "../graphs/numGraphDependenciesDelay.pdf" set ylabel "Average Delay (ms)" offset 0.5,0 plot "../resultsAvg/numGraphDependencies_causal_DelayAvg" u 1:2:3 t "Causal" w yerrorbars ls 1, \ @@ -167,7 +168,7 @@ plot "../resultsAvg/numGraphDependencies_causal_DelayAvg" u 1:2:3 t "Causal" w y "../resultsAvg/numGraphDependencies_atomic_DelayAvg" u 1:2 notitle w lines ls 4, \ "../resultsAvg/numGraphDependencies_sid_up_DelayAvg" u 1:2 notitle w lines ls 5 -set output "../graphs/numGraphDependenciesTraffic.ps" +set output "../graphs/numGraphDependenciesTraffic.pdf" set ylabel "Overall Traffic (KB/s)" offset 0.5,0 plot "../resultsAvg/numGraphDependencies_causal_TrafficByte" u ($1):($8/1000):($15/1000) t "Causal" w yerrorbars ls 1, \ @@ -187,7 +188,7 @@ plot "../resultsAvg/numGraphDependencies_causal_TrafficByte" u ($1):($8/1000):($ set xlabel "Degree of nodes sharing" offset 0,0.2 -set output "../graphs/graphShareDelay.ps" +set output "../graphs/graphShareDelay.pdf" set ylabel "Average Delay (ms)" offset 0.5,0 plot "../resultsAvg/graphShare_causal_DelayAvg" u 1:2:3 t "Causal" w yerrorbars ls 1, \ @@ -201,7 +202,7 @@ plot "../resultsAvg/graphShare_causal_DelayAvg" u 1:2:3 t "Causal" w yerrorbars "../resultsAvg/graphShare_atomic_DelayAvg" u 1:2 notitle w lines ls 4, \ "../resultsAvg/graphShare_sid_up_DelayAvg" u 1:2 notitle w lines ls 5 -set output "../graphs/graphShareTraffic.ps" +set output "../graphs/graphShareTraffic.pdf" set ylabel "Overall Traffic (KB/s)" offset 0.5,0 plot "../resultsAvg/graphShare_causal_TrafficByte" u ($1):($8/1000):($15/1000) t "Causal" w yerrorbars ls 1, \ @@ -222,7 +223,7 @@ plot "../resultsAvg/graphShare_causal_TrafficByte" u ($1):($8/1000):($15/1000) t set xlabel "Publication frequency (k events/s)" offset 0,0.2 set log x -set output "../graphs/pubFrequencyDelay.ps" +set output "../graphs/pubFrequencyDelay.pdf" set ylabel "Average Delay (ms)" offset 0.5,0 plot "../resultsAvg/timeBetweenEvents_causal_DelayAvg" u (1000/$1):($2):($3) t "Causal" w yerrorbars ls 1, \ @@ -236,7 +237,7 @@ plot "../resultsAvg/timeBetweenEvents_causal_DelayAvg" u (1000/$1):($2):($3) t " "../resultsAvg/timeBetweenEvents_atomic_DelayAvg" u (1000/$1):($2) notitle w lines ls 4, \ "../resultsAvg/timeBetweenEvents_sid_up_DelayAvg" u (1000/$1):($2) notitle w lines ls 5 -set output "../graphs/pubFrequencyTraffic.ps" +set output "../graphs/pubFrequencyTraffic.pdf" set ylabel "Overall Traffic (KB/s)" offset 0.5,0 plot "../resultsAvg/timeBetweenEvents_causal_TrafficByte" u (1000/$1):($8/1000):($15/1000) t "Causal" w yerrorbars ls 1, \ @@ -258,7 +259,7 @@ set xlabel "Signal access frequency (k reads/s)" offset 0,0.2 set log x set xr [0.1:2.5] -set output "../graphs/readFrequencyDelay.ps" +set output "../graphs/readFrequencyDelay.pdf" set ylabel "Average Delay (ms)" offset 0.5,0 plot "../resultsAvg/timeBetweenReads_causal_DelayAvg" u (1000/$1):($2):($3) t "Causal" w yerrorbars ls 1, \ @@ -272,7 +273,7 @@ plot "../resultsAvg/timeBetweenReads_causal_DelayAvg" u (1000/$1):($2):($3) t "C "../resultsAvg/timeBetweenReads_atomic_DelayAvg" u (1000/$1):($2) notitle w lines ls 4, \ "../resultsAvg/timeBetweenReads_sid_up_DelayAvg" u (1000/$1):($2) notitle w lines ls 5 -set output "../graphs/readFrequencyTraffic.ps" +set output "../graphs/readFrequencyTraffic.pdf" set ylabel "Overall Traffic (KB/s)" offset 0.5,0 plot "../resultsAvg/timeBetweenReads_causal_TrafficByte" u (1000/$1):($8/1000):($15/1000) t "Causal" w yerrorbars ls 1, \ @@ -301,11 +302,16 @@ set style line 5 lw 2 lt rgb 'red' pt 2 set style data histogram set style histogram cluster gap 5 set style fill solid border 0 + +set key above +set log y +set format y "10^%T" + set log y unset xlabel set xtics rotate by 35 offset -0.5,-2 -set output "../graphs/defaultTraffic.ps" +set output "../graphs/defaultTraffic.pdf" plot "../resultsAvg/defaultTraffic" u 2:xticlabel(1) ls 1 title "Causal", \ "../resultsAvg/defaultTraffic" u 3:xticlabel(1) ls 2 title "Single", \ From f34bc0caae092d2e00da15549e1482bcfad0167e Mon Sep 17 00:00:00 2001 From: Alessandro Margara Date: Tue, 8 Nov 2016 16:31:15 +0100 Subject: [PATCH 151/161] New graph script --- .../scripts/plotScriptSidUpIntervals.plot | 24 ++++++++++++------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/DreamSim/scripts/plotScriptSidUpIntervals.plot b/DreamSim/scripts/plotScriptSidUpIntervals.plot index bbc5766..b1ad6be 100644 --- a/DreamSim/scripts/plotScriptSidUpIntervals.plot +++ b/DreamSim/scripts/plotScriptSidUpIntervals.plot @@ -1,12 +1,18 @@ -set terminal pdf enhance fsize 14 -set pointsize 2 -#set size ratio 0.8 - -set style line 1 lw 4 lt rgb 'orange' pt 1 -set style line 2 lw 4 lt rgb 'black' pt 6 -set style line 3 lw 4 lt rgb 'green' pt 8 -set style line 4 lw 4 lt rgb 'blue' pt 3 -set style line 5 lw 4 lt rgb 'red' pt 2 +set terminal pdf enhance fsize 16 size 4.2,3 +set pointsize 3 +set size ratio 0.4 + +set style line 1 lw 3 lt 1 lc rgb 'orange' pt 1 +set style line 2 lw 3 lt 2 lc rgb 'black' pt 6 +set style line 3 lw 3 lt 3 lc rgb 'green' pt 8 +set style line 4 lw 3 lt 4 lc rgb 'blue' pt 3 +set style line 5 lw 3 lt 5 lc rgb 'red' pt 2 + +# set style line 1 linecolor rgb "black" lw 3 dashtype 1 pt 1 +# set style line 2 linecolor rgb "black" lw 3 dashtype 2 pt 6 +# set style line 3 linecolor rgb "black" lw 3 dashtype 3 pt 8 +# set style line 4 linecolor rgb "black" lw 3 dashtype 4 pt 3 +# set style line 5 linecolor rgb "black" lw 3 dashtype 5 pt 2 set key above set log y From 55f79b77b2ed3ee89bee558280fe760972398fa4 Mon Sep 17 00:00:00 2001 From: Alessandro Margara Date: Tue, 15 Nov 2016 16:39:36 +0100 Subject: [PATCH 152/161] Updated scripts --- DreamSim/scripts/plotScriptSidUp.plot | 2 +- .../scripts/plotScriptSidUpIntervals.plot | 48 ++++++++++++++----- DreamSim/scripts/summarize.py | 18 +++---- DreamSim/scripts/summarizeSingleRun.py | 20 +------- 4 files changed, 49 insertions(+), 39 deletions(-) diff --git a/DreamSim/scripts/plotScriptSidUp.plot b/DreamSim/scripts/plotScriptSidUp.plot index 8e190e2..58ad85d 100644 --- a/DreamSim/scripts/plotScriptSidUp.plot +++ b/DreamSim/scripts/plotScriptSidUp.plot @@ -1,4 +1,4 @@ -set terminal postscript enhance color dashed dl 4 font 16 +set terminal postscript enhance color dashed dl 4 font 1b6 set pointsize 3 # set size ratio 0.55 diff --git a/DreamSim/scripts/plotScriptSidUpIntervals.plot b/DreamSim/scripts/plotScriptSidUpIntervals.plot index b1ad6be..a2fe601 100644 --- a/DreamSim/scripts/plotScriptSidUpIntervals.plot +++ b/DreamSim/scripts/plotScriptSidUpIntervals.plot @@ -1,12 +1,13 @@ -set terminal pdf enhance fsize 16 size 4.2,3 +set terminal pdf fsize 14 size 4.2,3.6 + set pointsize 3 -set size ratio 0.4 +# set size ratio 0.4 -set style line 1 lw 3 lt 1 lc rgb 'orange' pt 1 -set style line 2 lw 3 lt 2 lc rgb 'black' pt 6 -set style line 3 lw 3 lt 3 lc rgb 'green' pt 8 -set style line 4 lw 3 lt 4 lc rgb 'blue' pt 3 -set style line 5 lw 3 lt 5 lc rgb 'red' pt 2 +set style line 1 lw 4 lt 1 lc rgb 'orange' pt 1 +set style line 2 lw 4 lt 2 lc rgb 'black' pt 6 +set style line 3 lw 4 lt 3 lc rgb 'green' pt 8 +set style line 4 lw 4 lt 4 lc rgb 'blue' pt 3 +set style line 5 lw 4 lt 5 lc rgb 'red' pt 2 # set style line 1 linecolor rgb "black" lw 3 dashtype 1 pt 1 # set style line 2 linecolor rgb "black" lw 3 dashtype 2 pt 6 @@ -14,7 +15,6 @@ set style line 5 lw 3 lt 5 lc rgb 'red' pt 2 # set style line 4 linecolor rgb "black" lw 3 dashtype 4 pt 3 # set style line 5 linecolor rgb "black" lw 3 dashtype 5 pt 2 -set key above set log y set format y "10^%T" @@ -22,6 +22,8 @@ set format y "10^%T" # LOCALITY # ############ +set key bottom left + set xlabel "Degree of locality" offset 0,0.2 set output "../graphs/localityDelay.pdf" @@ -56,10 +58,13 @@ plot "../resultsAvg/locality_causal_TrafficByte" u ($1):($8/1000):($15/1000) t " # NUMBER OF BROKERS # ##################### +set key top right + set xlabel "Number of brokers" offset 0,0.2 set output "../graphs/numBrokersDelay.pdf" set ylabel "Average Delay (ms)" offset 0.5,0 +set xtics 5 plot "../resultsAvg/numBrokers_causal_DelayAvg" u 1:2:3 t "Causal" w yerrorbars ls 1, \ "../resultsAvg/numBrokers_single_glitch_free_DelayAvg" u 1:2:3 t "Single" w yerrorbars ls 2, \ @@ -86,14 +91,19 @@ plot "../resultsAvg/numBrokers_causal_TrafficByte" u ($1):($8/1000):($15/1000) t "../resultsAvg/numBrokers_atomic_TrafficByte" u ($1):($8/1000) notitle w lines ls 4, \ "../resultsAvg/numBrokers_sid_up_TrafficByte" u ($1):($8/1000) notitle w lines ls 5 +set xtics auto + ################## # NUMBER OF VARS # ################## +set key center right + set xlabel "Number of sources (vars) in the graph" offset 0,0.2 set output "../graphs/numVarsDelay.pdf" set ylabel "Average Delay (ms)" offset 0.5,0 +set xtics 20 plot "../resultsAvg/numVars_causal_DelayAvg" u 1:2:3 t "Causal" w yerrorbars ls 1, \ "../resultsAvg/numVars_single_glitch_free_DelayAvg" u 1:2:3 t "Single" w yerrorbars ls 2, \ @@ -106,6 +116,8 @@ plot "../resultsAvg/numVars_causal_DelayAvg" u 1:2:3 t "Causal" w yerrorbars ls "../resultsAvg/numVars_atomic_DelayAvg" u 1:2 notitle w lines ls 4, \ "../resultsAvg/numVars_sid_up_DelayAvg" u 1:2 notitle w lines ls 5 +set key bottom right + set output "../graphs/numVarsTraffic.pdf" set ylabel "Overall Traffic (KB/s)" offset 0.5,0 @@ -120,10 +132,14 @@ plot "../resultsAvg/numVars_causal_TrafficByte" u ($1):($8/1000):($15/1000) t "C "../resultsAvg/numVars_atomic_TrafficByte" u ($1):($8/1000) notitle w lines ls 4, \ "../resultsAvg/numVars_sid_up_TrafficByte" u ($1):($8/1000) notitle w lines ls 5 +set xtics auto + ############### # GRAPH DEPTH # ############### +set key bottom right + set xlabel "Depth of the dependency graph" offset 0,0.2 set output "../graphs/graphDepthDelay.pdf" @@ -158,6 +174,8 @@ plot "../resultsAvg/graphDepth_causal_TrafficByte" u ($1):($8/1000):($15/1000) t # NUMBER OF DEPENDENCIES PER SIGNAL # ##################################### +set key bottom right + set xlabel "Number of dependencies per signal" offset 0,0.2 set output "../graphs/numGraphDependenciesDelay.pdf" @@ -192,6 +210,8 @@ plot "../resultsAvg/numGraphDependencies_causal_TrafficByte" u ($1):($8/1000):($ # GRAPH SHARE PROBABILITY # ########################### +set key top left + set xlabel "Degree of nodes sharing" offset 0,0.2 set output "../graphs/graphShareDelay.pdf" @@ -226,6 +246,8 @@ plot "../resultsAvg/graphShare_causal_TrafficByte" u ($1):($8/1000):($15/1000) t # TIME BETWEEN EVENTS # ####################### +set key top left + set xlabel "Publication frequency (k events/s)" offset 0,0.2 set log x @@ -243,6 +265,8 @@ plot "../resultsAvg/timeBetweenEvents_causal_DelayAvg" u (1000/$1):($2):($3) t " "../resultsAvg/timeBetweenEvents_atomic_DelayAvg" u (1000/$1):($2) notitle w lines ls 4, \ "../resultsAvg/timeBetweenEvents_sid_up_DelayAvg" u (1000/$1):($2) notitle w lines ls 5 +set key bottom right + set output "../graphs/pubFrequencyTraffic.pdf" set ylabel "Overall Traffic (KB/s)" offset 0.5,0 @@ -261,6 +285,8 @@ plot "../resultsAvg/timeBetweenEvents_causal_TrafficByte" u (1000/$1):($8/1000): # TIME BETWEEN READS # ###################### +set key top left + set xlabel "Signal access frequency (k reads/s)" offset 0,0.2 set log x set xr [0.1:2.5] @@ -306,7 +332,7 @@ set style line 4 lw 2 lt rgb 'blue' pt 3 set style line 5 lw 2 lt rgb 'red' pt 2 set style data histogram -set style histogram cluster gap 5 +set style histogram cluster gap 1 set style fill solid border 0 set key above @@ -314,8 +340,8 @@ set log y set format y "10^%T" set log y -unset xlabel -set xtics rotate by 35 offset -0.5,-2 +set xtics rotate by 35 offset -1.2,-1.0 +set xtics font ", 14" set output "../graphs/defaultTraffic.pdf" diff --git a/DreamSim/scripts/summarize.py b/DreamSim/scripts/summarize.py index 643d321..3765fd8 100755 --- a/DreamSim/scripts/summarize.py +++ b/DreamSim/scripts/summarize.py @@ -213,6 +213,14 @@ def avgDelay(filename, suffix, numRepetitions): "\t" + str(deltaDelay) + \ "\n") +def avgAll(filename, values): + global numSeeds + global protocols + for protocol in protocols: + avgTraffic(filename, protocol + "_Traffic", numSeeds) + avgTraffic(filename, protocol + "_TrafficByte", numSeeds) + avgDelay(filename, protocol + "_DelayAvg", numSeeds) + def prepareDefaultTraffic(): causal = open("../resultsAvg/default_causal_TrafficByte", "r").readlines()[0].replace("\n", "").split("\t") single = open("../resultsAvg/default_single_glitch_free_TrafficByte", "r").readlines()[0].replace("\n", "").split("\t") @@ -222,7 +230,7 @@ def prepareDefaultTraffic(): result = open("../resultsAvg/defaultTraffic", "w") - result.write("Events\t" + causal[1] + "\t" + single[1] + "\t" + complete[1] + "\t" + atomic[1] + "\t" + sidup[1] + "\n") + result.write("Ev.\t" + causal[1] + "\t" + single[1] + "\t" + complete[1] + "\t" + atomic[1] + "\t" + sidup[1] + "\n") result.write("Adv.\t" + causal[3] + "\t" + single[3] + "\t" + complete[3] + "\t" + atomic[3] + "\t" + sidup[3] + "\n") result.write("Subs.\t" + causal[2] + "\t" + single[2] + "\t" + complete[2] + "\t" + atomic[2] + "\t" + sidup[2] + "\n") result.write("Req.\t" + causal[5] + "\t" + single[5] + "\t" + complete[5] + "\t" + atomic[5] + "\t" + sidup[5] + "\n") @@ -243,14 +251,6 @@ def prepareDefaultDelay(): result.write("Complete\t" + complete[1] + "\t" + complete[2] + "\n") result.write("Atomic \t" + atomic[1] + "\t" + atomic[2] + "\n") result.write("Sid Up\t" + sidup[1] + "\t" + sidup[2] + "\n") - -def avgAll(filename, values): - global numSeeds - global protocols - for protocol in protocols: - avgTraffic(filename, protocol + "_Traffic", numSeeds) - avgTraffic(filename, protocol + "_TrafficByte", numSeeds) - avgDelay(filename, protocol + "_DelayAvg", numSeeds) # Values default = [0] diff --git a/DreamSim/scripts/summarizeSingleRun.py b/DreamSim/scripts/summarizeSingleRun.py index 7db4bf2..3927faf 100644 --- a/DreamSim/scripts/summarizeSingleRun.py +++ b/DreamSim/scripts/summarizeSingleRun.py @@ -238,35 +238,20 @@ def prepareDefaultTraffic(): result = open("../resultsAvg/defaultTraffic", "w") - result.write("Events\t" + causal[1] + "\t" + single[1] + "\t" + complete[1] + "\t" + atomic[1] + "\t" + sidup[1] + "\n") + result.write("Ev.\t" + causal[1] + "\t" + single[1] + "\t" + complete[1] + "\t" + atomic[1] + "\t" + sidup[1] + "\n") result.write("Adv.\t" + causal[3] + "\t" + single[3] + "\t" + complete[3] + "\t" + atomic[3] + "\t" + sidup[3] + "\n") result.write("Subs.\t" + causal[2] + "\t" + single[2] + "\t" + complete[2] + "\t" + atomic[2] + "\t" + sidup[2] + "\n") result.write("Req.\t" + causal[5] + "\t" + single[5] + "\t" + complete[5] + "\t" + atomic[5] + "\t" + sidup[5] + "\n") result.write("Grant\t" + causal[6] + "\t" + single[6] + "\t" + complete[6] + "\t" + atomic[6] + "\t" + sidup[6] + "\n") result.write("Rel.\t" + causal[4] + "\t" + single[4] + "\t" + complete[4] + "\t" + atomic[4] + "\t" + sidup[4] + "\n") -def prepareDefaultDelay(): - causal = open("../resultsAvg/default_causal_DelayAvg", "r").readlines()[0].replace("\n", "").split("\t") - single = open("../resultsAvg/default_single_glitch_free_DelayAvg", "r").readlines()[0].replace("\n", "").split("\t") - complete = open("../resultsAvg/default_complete_glitch_free_DelayAvg", "r").readlines()[0].replace("\n", "").split("\t") - atomic = open("../resultsAvg/default_atomic_DelayAvg", "r").readlines()[0].replace("\n", "").split("\t") - sidup = open("../resultsAvg/default_sid_up_DelayAvg", "r").readlines()[0].replace("\n", "").split("\t") - - result = open("../resultsAvg/defaultDelay", "w") - - result.write("Causal\t" + causal[1] + "\t" + causal[2] + "\n") - result.write("Single\t" + single[1] + "\t" + single[2] + "\n") - result.write("Complete\t" + complete[1] + "\t" + complete[2] + "\n") - result.write("Atomic \t" + atomic[1] + "\t" + atomic[2] + "\n") - result.write("Sid Up\t" + sidup[1] + "\t" + sidup[2] + "\n") - # Values default = [0] locality = [0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0] numBrokers = [1, 3, 5, 10, 15, 20] numVars = [1, 4, 7, 10, 40, 70, 100] graphDepth = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] -numGraphDependencies = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] +numGraphDependencies = [1, 2, 3, 4, 5, 6, 7, 8, 9] graphShare = [0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0] timeBetweenEvents = [100, 400, 700, 1000, 4000, 7000, 10000] # timeBetweenReads = [100, 400, 700, 1000, 4000, 7000, 10000] @@ -294,4 +279,3 @@ def prepareDefaultDelay(): avgAll("timeBetweenReads", timeBetweenReads) prepareDefaultTraffic() -prepareDefaultDelay() From 6568548f48834506f1a6e100be5ed7a3b5666134 Mon Sep 17 00:00:00 2001 From: Alessandro Margara Date: Tue, 15 Nov 2016 16:58:11 +0100 Subject: [PATCH 153/161] Update plot script --- .../scripts/plotScriptSidUpIntervals.plot | 26 ++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/DreamSim/scripts/plotScriptSidUpIntervals.plot b/DreamSim/scripts/plotScriptSidUpIntervals.plot index a2fe601..0598db0 100644 --- a/DreamSim/scripts/plotScriptSidUpIntervals.plot +++ b/DreamSim/scripts/plotScriptSidUpIntervals.plot @@ -1,4 +1,4 @@ -set terminal pdf fsize 14 size 4.2,3.6 +set terminal pdf fsize 15 size 4.2,3.6 set pointsize 3 # set size ratio 0.4 @@ -28,6 +28,7 @@ set xlabel "Degree of locality" offset 0,0.2 set output "../graphs/localityDelay.pdf" set ylabel "Average Delay (ms)" offset 0.5,0 +unset yr plot "../resultsAvg/locality_causal_DelayAvg" u 1:2:3 t "Causal" w yerrorbars ls 1, \ "../resultsAvg/locality_single_glitch_free_DelayAvg" u 1:2:3 t "Single" w yerrorbars ls 2, \ @@ -42,6 +43,7 @@ plot "../resultsAvg/locality_causal_DelayAvg" u 1:2:3 t "Causal" w yerrorbars ls set output "../graphs/localityTraffic.pdf" set ylabel "Overall Traffic (KB/s)" offset 0.5,0 +unset yr plot "../resultsAvg/locality_causal_TrafficByte" u ($1):($8/1000):($15/1000) t "Causal" w yerrorbars ls 1, \ "../resultsAvg/locality_single_glitch_free_TrafficByte" u ($1):($8/1000):($15/1000) t "Single" w yerrorbars ls 2, \ @@ -64,6 +66,7 @@ set xlabel "Number of brokers" offset 0,0.2 set output "../graphs/numBrokersDelay.pdf" set ylabel "Average Delay (ms)" offset 0.5,0 +unset yr set xtics 5 plot "../resultsAvg/numBrokers_causal_DelayAvg" u 1:2:3 t "Causal" w yerrorbars ls 1, \ @@ -79,6 +82,7 @@ plot "../resultsAvg/numBrokers_causal_DelayAvg" u 1:2:3 t "Causal" w yerrorbars set output "../graphs/numBrokersTraffic.pdf" set ylabel "Overall Traffic (KB/s)" offset 0.5,0 +unset yr plot "../resultsAvg/numBrokers_causal_TrafficByte" u ($1):($8/1000):($15/1000) t "Causal" w yerrorbars ls 1, \ "../resultsAvg/numBrokers_single_glitch_free_TrafficByte" u ($1):($8/1000):($15/1000) t "Single" w yerrorbars ls 2, \ @@ -103,6 +107,7 @@ set xlabel "Number of sources (vars) in the graph" offset 0,0.2 set output "../graphs/numVarsDelay.pdf" set ylabel "Average Delay (ms)" offset 0.5,0 +unset yr set xtics 20 plot "../resultsAvg/numVars_causal_DelayAvg" u 1:2:3 t "Causal" w yerrorbars ls 1, \ @@ -120,6 +125,7 @@ set key bottom right set output "../graphs/numVarsTraffic.pdf" set ylabel "Overall Traffic (KB/s)" offset 0.5,0 +unset yr plot "../resultsAvg/numVars_causal_TrafficByte" u ($1):($8/1000):($15/1000) t "Causal" w yerrorbars ls 1, \ "../resultsAvg/numVars_single_glitch_free_TrafficByte" u ($1):($8/1000):($15/1000) t "Single" w yerrorbars ls 2, \ @@ -144,6 +150,7 @@ set xlabel "Depth of the dependency graph" offset 0,0.2 set output "../graphs/graphDepthDelay.pdf" set ylabel "Average Delay (ms)" offset 0.5,0 +unset yr plot "../resultsAvg/graphDepth_causal_DelayAvg" u 1:2:3 t "Causal" w yerrorbars ls 1, \ "../resultsAvg/graphDepth_single_glitch_free_DelayAvg" u 1:2:3 t "Single" w yerrorbars ls 2, \ @@ -158,6 +165,7 @@ plot "../resultsAvg/graphDepth_causal_DelayAvg" u 1:2:3 t "Causal" w yerrorbars set output "../graphs/graphDepthTraffic.pdf" set ylabel "Overall Traffic (KB/s)" offset 0.5,0 +unset yr plot "../resultsAvg/graphDepth_causal_TrafficByte" u ($1):($8/1000):($15/1000) t "Causal" w yerrorbars ls 1, \ "../resultsAvg/graphDepth_single_glitch_free_TrafficByte" u ($1):($8/1000):($15/1000) t "Single" w yerrorbars ls 2, \ @@ -180,6 +188,8 @@ set xlabel "Number of dependencies per signal" offset 0,0.2 set output "../graphs/numGraphDependenciesDelay.pdf" set ylabel "Average Delay (ms)" offset 0.5,0 +unset yr +set yr[1:] plot "../resultsAvg/numGraphDependencies_causal_DelayAvg" u 1:2:3 t "Causal" w yerrorbars ls 1, \ "../resultsAvg/numGraphDependencies_single_glitch_free_DelayAvg" u 1:2:3 t "Single" w yerrorbars ls 2, \ @@ -194,6 +204,8 @@ plot "../resultsAvg/numGraphDependencies_causal_DelayAvg" u 1:2:3 t "Causal" w y set output "../graphs/numGraphDependenciesTraffic.pdf" set ylabel "Overall Traffic (KB/s)" offset 0.5,0 +unset yr +set yr[1:] plot "../resultsAvg/numGraphDependencies_causal_TrafficByte" u ($1):($8/1000):($15/1000) t "Causal" w yerrorbars ls 1, \ "../resultsAvg/numGraphDependencies_single_glitch_free_TrafficByte" u ($1):($8/1000):($15/1000) t "Single" w yerrorbars ls 2, \ @@ -216,6 +228,7 @@ set xlabel "Degree of nodes sharing" offset 0,0.2 set output "../graphs/graphShareDelay.pdf" set ylabel "Average Delay (ms)" offset 0.5,0 +unset yr plot "../resultsAvg/graphShare_causal_DelayAvg" u 1:2:3 t "Causal" w yerrorbars ls 1, \ "../resultsAvg/graphShare_single_glitch_free_DelayAvg" u 1:2:3 t "Single" w yerrorbars ls 2, \ @@ -230,6 +243,8 @@ plot "../resultsAvg/graphShare_causal_DelayAvg" u 1:2:3 t "Causal" w yerrorbars set output "../graphs/graphShareTraffic.pdf" set ylabel "Overall Traffic (KB/s)" offset 0.5,0 +unset yr +set yr[:10000] plot "../resultsAvg/graphShare_causal_TrafficByte" u ($1):($8/1000):($15/1000) t "Causal" w yerrorbars ls 1, \ "../resultsAvg/graphShare_single_glitch_free_TrafficByte" u ($1):($8/1000):($15/1000) t "Single" w yerrorbars ls 2, \ @@ -253,6 +268,8 @@ set log x set output "../graphs/pubFrequencyDelay.pdf" set ylabel "Average Delay (ms)" offset 0.5,0 +unset yr +set yr[:10000000] plot "../resultsAvg/timeBetweenEvents_causal_DelayAvg" u (1000/$1):($2):($3) t "Causal" w yerrorbars ls 1, \ "../resultsAvg/timeBetweenEvents_single_glitch_free_DelayAvg" u (1000/$1):($2):($3) t "Single" w yerrorbars ls 2, \ @@ -269,6 +286,7 @@ set key bottom right set output "../graphs/pubFrequencyTraffic.pdf" set ylabel "Overall Traffic (KB/s)" offset 0.5,0 +unset yr plot "../resultsAvg/timeBetweenEvents_causal_TrafficByte" u (1000/$1):($8/1000):($15/1000) t "Causal" w yerrorbars ls 1, \ "../resultsAvg/timeBetweenEvents_single_glitch_free_TrafficByte" u (1000/$1):($8/1000):($15/1000) t "Single" w yerrorbars ls 2, \ @@ -293,6 +311,7 @@ set xr [0.1:2.5] set output "../graphs/readFrequencyDelay.pdf" set ylabel "Average Delay (ms)" offset 0.5,0 +unset yr plot "../resultsAvg/timeBetweenReads_causal_DelayAvg" u (1000/$1):($2):($3) t "Causal" w yerrorbars ls 1, \ "../resultsAvg/timeBetweenReads_single_glitch_free_DelayAvg" u (1000/$1):($2):($3) t "Single" w yerrorbars ls 2, \ @@ -307,6 +326,7 @@ plot "../resultsAvg/timeBetweenReads_causal_DelayAvg" u (1000/$1):($2):($3) t "C set output "../graphs/readFrequencyTraffic.pdf" set ylabel "Overall Traffic (KB/s)" offset 0.5,0 +unset yr plot "../resultsAvg/timeBetweenReads_causal_TrafficByte" u (1000/$1):($8/1000):($15/1000) t "Causal" w yerrorbars ls 1, \ "../resultsAvg/timeBetweenReads_single_glitch_free_TrafficByte" u (1000/$1):($8/1000):($15/1000) t "Single" w yerrorbars ls 2, \ @@ -324,6 +344,7 @@ plot "../resultsAvg/timeBetweenReads_causal_TrafficByte" u (1000/$1):($8/1000):( ########### reset +set terminal pdf fsize 12 size 4.2,3.0 set style line 1 lw 2 lt rgb 'orange' pt 1 set style line 2 lw 2 lt rgb 'black' pt 6 @@ -340,8 +361,7 @@ set log y set format y "10^%T" set log y -set xtics rotate by 35 offset -1.2,-1.0 -set xtics font ", 14" +set xtics rotate by 35 offset -1.2,-1.6 set output "../graphs/defaultTraffic.pdf" From 655ff289e6298e6faa51690666840b94fcd4a961 Mon Sep 17 00:00:00 2001 From: Alessandro Margara Date: Mon, 28 Nov 2016 09:34:37 +0100 Subject: [PATCH 154/161] Changes to the graph script --- .../scripts/plotScriptSidUpIntervals.plot | 20 +++++++++---------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/DreamSim/scripts/plotScriptSidUpIntervals.plot b/DreamSim/scripts/plotScriptSidUpIntervals.plot index 0598db0..05879e5 100644 --- a/DreamSim/scripts/plotScriptSidUpIntervals.plot +++ b/DreamSim/scripts/plotScriptSidUpIntervals.plot @@ -346,15 +346,11 @@ plot "../resultsAvg/timeBetweenReads_causal_TrafficByte" u (1000/$1):($8/1000):( reset set terminal pdf fsize 12 size 4.2,3.0 -set style line 1 lw 2 lt rgb 'orange' pt 1 -set style line 2 lw 2 lt rgb 'black' pt 6 -set style line 3 lw 2 lt rgb 'green' pt 8 -set style line 4 lw 2 lt rgb 'blue' pt 3 -set style line 5 lw 2 lt rgb 'red' pt 2 +set style line 1 lw 2 lt 1 lc rgb 'black' pt 1 set style data histogram set style histogram cluster gap 1 -set style fill solid border 0 +# set style fill solid border 0 set key above set log y @@ -365,8 +361,10 @@ set xtics rotate by 35 offset -1.2,-1.6 set output "../graphs/defaultTraffic.pdf" -plot "../resultsAvg/defaultTraffic" u 2:xticlabel(1) ls 1 title "Causal", \ -"../resultsAvg/defaultTraffic" u 3:xticlabel(1) ls 2 title "Single", \ -"../resultsAvg/defaultTraffic" u 4:xticlabel(1) ls 3 title "Complete", \ -"../resultsAvg/defaultTraffic" u 5:xticlabel(1) ls 4 title "Atomic", \ -"../resultsAvg/defaultTraffic" u 6:xticlabel(1) ls 5 title "Sid Up" \ No newline at end of file +set ylabel "Average Delay (ms)" offset 0.5,0 + +plot "../resultsAvg/defaultTraffic" u 2:xticlabel(1) ls 1 fs pattern 1 title "Causal", \ +"../resultsAvg/defaultTraffic" u 3:xticlabel(1) ls 1 fs pattern 2 title "Single", \ +"../resultsAvg/defaultTraffic" u 4:xticlabel(1) ls 1 fs pattern 3 title "Complete", \ +"../resultsAvg/defaultTraffic" u 5:xticlabel(1) ls 1 fs pattern 4 title "Atomic", \ +"../resultsAvg/defaultTraffic" u 6:xticlabel(1) ls 1 fs pattern 5 title "Sid Up" \ No newline at end of file From ed0cfd56bf2c2573a49e61476b5457e2073c2e61 Mon Sep 17 00:00:00 2001 From: Alessandro Margara Date: Fri, 2 Dec 2016 12:39:03 +0100 Subject: [PATCH 155/161] Fixed a problem in the script --- DreamSim/scripts/plotScriptSidUpIntervals.plot | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/DreamSim/scripts/plotScriptSidUpIntervals.plot b/DreamSim/scripts/plotScriptSidUpIntervals.plot index 05879e5..048c7b5 100644 --- a/DreamSim/scripts/plotScriptSidUpIntervals.plot +++ b/DreamSim/scripts/plotScriptSidUpIntervals.plot @@ -361,10 +361,10 @@ set xtics rotate by 35 offset -1.2,-1.6 set output "../graphs/defaultTraffic.pdf" -set ylabel "Average Delay (ms)" offset 0.5,0 +set ylabel "Overall Traffic (KB/s)" offset 0.5,0 -plot "../resultsAvg/defaultTraffic" u 2:xticlabel(1) ls 1 fs pattern 1 title "Causal", \ -"../resultsAvg/defaultTraffic" u 3:xticlabel(1) ls 1 fs pattern 2 title "Single", \ -"../resultsAvg/defaultTraffic" u 4:xticlabel(1) ls 1 fs pattern 3 title "Complete", \ -"../resultsAvg/defaultTraffic" u 5:xticlabel(1) ls 1 fs pattern 4 title "Atomic", \ -"../resultsAvg/defaultTraffic" u 6:xticlabel(1) ls 1 fs pattern 5 title "Sid Up" \ No newline at end of file +plot "../resultsAvg/defaultTraffic" u ($2/1000):xticlabel(1) ls 1 fs pattern 1 title "Causal", \ +"../resultsAvg/defaultTraffic" u ($3/1000):xticlabel(1) ls 1 fs pattern 2 title "Single", \ +"../resultsAvg/defaultTraffic" u ($4/1000):xticlabel(1) ls 1 fs pattern 3 title "Complete", \ +"../resultsAvg/defaultTraffic" u ($5/1000):xticlabel(1) ls 1 fs pattern 4 title "Atomic", \ +"../resultsAvg/defaultTraffic" u ($6/1000):xticlabel(1) ls 1 fs pattern 5 title "Sid Up" \ No newline at end of file From 0b91b926d79e5b87e0e3efa389ca66a0f92a9380 Mon Sep 17 00:00:00 2001 From: guidosalva Date: Mon, 7 May 2018 15:07:42 +0200 Subject: [PATCH 156/161] Set theme jekyll-theme-slate --- _config.yml | 1 + 1 file changed, 1 insertion(+) create mode 100644 _config.yml diff --git a/_config.yml b/_config.yml new file mode 100644 index 0000000..c741881 --- /dev/null +++ b/_config.yml @@ -0,0 +1 @@ +theme: jekyll-theme-slate \ No newline at end of file From 5dc4530ebaa791a885e61c05a5dbacf0e2da1d5e Mon Sep 17 00:00:00 2001 From: guidosalva Date: Mon, 7 May 2018 15:15:06 +0200 Subject: [PATCH 157/161] Update README.md --- README.md | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index ff41c14..88e5f96 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,33 @@ -# dream-java +# DREAM Reactive Programming Framework + +DREAM - distributed reactive programming +middleware with flexible consistency guarantees. + +## Why DREAM? + +Different applications require different levels of consistency +and that manually implementing the required level on a middleware that +provides a lower one annuls the abstraction improvements of reactive +programming. DREAM enables the developers to select +the best trade-off between consistency and overhead for the problem at +hand. + +The reactive programming paradigm aims to simplify the development of reactive +systems. It provides abstractions to define time-changing values that are +automatically updated by the runtime according to their dependencies. + +The benefits of reactive programming in distributed settings have been +recognized for long. Most solutions for distributed reactive +programming enforce the same semantics as in single processes, introducing +communication and synchronization costs that hamper scalability. + +DREAM defines precise propagation +semantics in terms of consistency guarantees that constrain the order and +isolation of value updates. + + + +## Getting Started The relevant source code is in the Dream2 folder. From 38d89816cb7d31092e9a157da4a8635adc27225a Mon Sep 17 00:00:00 2001 From: guidosalva Date: Mon, 7 May 2018 15:19:04 +0200 Subject: [PATCH 158/161] Update README.md --- README.md | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 88e5f96..ae8b118 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,11 @@ semantics in terms of consistency guarantees that constrain the order and isolation of value updates. +## Accademic Publications + +A. Margara, G. Salvaneschi, On the Semantics of Distributed Reactive Programming: the Cost of Consistency, IEEE Transactions on Software Engineering, 2018. + +A. Margara and G. Salvaneschi, We have a DREAM: Distributed Reactive Programming with Consistency Guarantees, In Proceedings of the 8th International Conference on Distributed Event-Based Systems (DEBS ’14). Mumbay, India, May 26–29, 2014. ## Getting Started @@ -131,9 +136,15 @@ public class OtherClass extends Client { Alessandro Margara +Guido Salvaneschi + Tobias Becker -Kim Berninger and Michael Raulf - IMPL Project +Kim Berninger + +Michael Raulf + +Ram Kamath From 120d946acd14131b995a5a91e2dd8dbbbad8f018 Mon Sep 17 00:00:00 2001 From: guidosalva Date: Mon, 7 May 2018 15:28:50 +0200 Subject: [PATCH 159/161] Update README.md --- README.md | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index ae8b118..4077379 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,21 @@ DREAM defines precise propagation semantics in terms of consistency guarantees that constrain the order and isolation of value updates. +## Consistency Levels + +Consistency levels supported by DREAM explained in a nutshell. For a precise definition see the academic publications. + +**FIFO Consistency** Ensures that a signal reflects the changes to a single variable in the order in which they occur. + +**Causal Consistency** Analogous to causal consistency in replicated data stores, which guarantees that operations that are causally related with each other take place in the same order in all the replicas, and this order reflects the causal dependency. + +**Single-source Glitch Freedom** All the effects caused by an update on a node become visible at the same time. This means that a reader cannot observe the effects of an update on a node in an order that violates causality. + +**Complete Glitch Freedom** Ensures that the results of two propagations are the same as if the propagations took place in some sequential order, without interleaving at any node. + +**Atomic Consistency** Complete Glitch Freedom plus a read operation cannot observe only *some* of the effects of the change in a source: either it observes all of them, or none. + + ## Accademic Publications @@ -134,17 +149,7 @@ public class OtherClass extends Client { ## Contributors -Alessandro Margara - -Guido Salvaneschi - -Tobias Becker - -Kim Berninger - -Michael Raulf - -Ram Kamath +Alessandro Margara, Guido Salvaneschi, Tobias Becker, Kim Berninger, Michael Raulf, Ram Kamath. From 5294b04af231d32251d39faff8bc41dbdc6b6971 Mon Sep 17 00:00:00 2001 From: guidosalva Date: Mon, 7 May 2018 15:32:19 +0200 Subject: [PATCH 160/161] Update README.md --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 4077379..930074a 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,7 @@ A. Margara, G. Salvaneschi, On the Semantics of Distributed Reactive Programming A. Margara and G. Salvaneschi, We have a DREAM: Distributed Reactive Programming with Consistency Guarantees, In Proceedings of the 8th International Conference on Distributed Event-Based Systems (DEBS ’14). Mumbay, India, May 26–29, 2014. -## Getting Started +## Examples The relevant source code is in the Dream2 folder. @@ -99,7 +99,7 @@ Signal n = }); ``` -## Locks +### Locks When using Atomic_Consistency it is required to lock a Var before reading its value. ``` LockToken lock = DreamClient.instance.readLock(new HashSet<>("exVar@Host1")); @@ -107,7 +107,7 @@ When using Atomic_Consistency it is required to lock a Var before reading its va DreamClient.instance.unlock(lock); ``` -## Utility Class +### Getting Started In the dream.examples.util-package you can find the class Client which does all the needed setup for Dream. It starts the DreamServer if it is not already running, sets the Client Name and connects to the Dependency Graph. It also provides some additional utilities. From ac969df2aea84303af560968cd763f7879462857 Mon Sep 17 00:00:00 2001 From: guidosalva Date: Mon, 7 May 2018 15:33:00 +0200 Subject: [PATCH 161/161] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 930074a..8f8f71f 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ Consistency levels supported by DREAM explained in a nutshell. For a precise def -## Accademic Publications +## Academic Publications A. Margara, G. Salvaneschi, On the Semantics of Distributed Reactive Programming: the Cost of Consistency, IEEE Transactions on Software Engineering, 2018.