diff --git a/tools/ExperimentSynchronizer/lib/jdatepicker-1.3.4.jar b/tools/ExperimentSynchronizer/lib/jdatepicker-1.3.4.jar
new file mode 100644
index 0000000000000000000000000000000000000000..a71625968ac3da67a3ec30b5d9f0e6fb344ca59e
Binary files /dev/null and b/tools/ExperimentSynchronizer/lib/jdatepicker-1.3.4.jar differ
diff --git a/tools/ExperimentSynchronizer/src/gov/anl/dm/esafsync/ExperimentList.java b/tools/ExperimentSynchronizer/src/gov/anl/dm/esafsync/ExperimentList.java
new file mode 100644
index 0000000000000000000000000000000000000000..c65538bd008764f9b2883124cb631d0ab0028309
--- /dev/null
+++ b/tools/ExperimentSynchronizer/src/gov/anl/dm/esafsync/ExperimentList.java
@@ -0,0 +1,324 @@
+package gov.anl.dm.esafsync;
+
+import gov.anl.dm.esafsync.serviceconn.DaqServiceConnection;
+import gov.anl.dm.esafsync.serviceconn.ServiceConnection;
+import gov.anl.dm.esafsync.serviceconn.ServiceConnection.ServiceConnectionStatus;
+import gov.anl.dm.esafsync.serviceconn.StorageServiceConnection;
+
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.FlowLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.sql.SQLException;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.List;
+import java.util.Properties;
+
+import javax.swing.BorderFactory;
+import javax.swing.JButton;
+import javax.swing.JFormattedTextField.AbstractFormatter;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTable;
+import javax.swing.table.AbstractTableModel;
+
+import org.jdatepicker.impl.JDatePanelImpl;
+import org.jdatepicker.impl.JDatePickerImpl;
+import org.jdatepicker.impl.UtilDateModel;
+
+
+class ExperimentList extends JFrame
+{
+	private static final long serialVersionUID = 1L;
+	
+	public class DateLabelFormatter extends AbstractFormatter {
+		private static final long serialVersionUID = 1L;
+		private String datePattern = "MM-dd-yyyy";
+	    private SimpleDateFormat dateFormatter = new SimpleDateFormat(datePattern);
+
+	    @Override
+	    public Object stringToValue(String text) throws ParseException {
+	        return dateFormatter.parseObject(text);
+	    }
+
+	    @Override
+	    public String valueToString(Object value) throws ParseException {
+	        if (value != null) {
+	            Calendar cal = (Calendar) value;
+	            return dateFormatter.format(cal.getTime());
+	        }
+	        return "";
+	    }
+
+	    public String valueToString(Date value) throws ParseException {
+	        if (value != null) {
+	            Date cal = (Date) value;
+	            return dateFormatter.format(cal.getTime());
+	        }
+	        return "";
+	    }
+
+	}
+	
+	enum Column {
+		NAME("Experiment name"),
+		DESCRIPTION("Description"),
+		START_DATE("Start Date"),
+		END_DATE("End Date"),
+		MANAGER("Manager(s)"),
+		PI("Principal Investigator(s)"),
+		USER("Users");
+
+		private final String column;
+
+		private Column(String column) {
+			this.column = column;
+		}
+
+		@Override
+		public String toString() {
+			return column;
+		}
+	}
+
+	enum Role {
+		MANAGER("Manager"),
+		PI("Principal Investigator"),
+		USER("User");
+		
+		private final String role;
+		
+		private Role(String role) {
+			this.role = role;
+		}
+
+		@Override
+		public String toString() {
+			return role;
+		}
+	}
+	
+	class ExperimentTableModel extends AbstractTableModel {
+		private static final long serialVersionUID = 1L;
+
+		String dataValues [][];
+
+		@Override
+		public int getRowCount() {
+			return dataValues.length;
+		}
+
+		@Override
+		public int getColumnCount() {
+			return Column.values().length;
+		}
+
+		@Override
+		public Object getValueAt(int rowIndex, int columnIndex) {
+			return dataValues[rowIndex][columnIndex];
+		}
+
+		public String getColumnName(int col) {
+			return Column.values()[col].toString();
+		}
+
+		public Class getColumnClass(int c) {
+			return getValueAt(0, c).getClass();
+		}
+
+		void setTable(String list) {
+			List<String[]> expTable = new ArrayList<>();
+			String [] rows = list.split("EXP");
+			for (int i=0; i<rows.length; i++) {
+				String [] cells = rows[i].split(",");
+				boolean goodRow = false;
+				int j = 0;
+				for (j = 0; j < cells[0].length(); j++) {
+					if ( Character.isDigit(cells[0].charAt(j)) ) {
+						goodRow = true;
+						break;
+					}				
+				}
+				if (!goodRow) {
+					continue;
+				} else {
+					// parse experiment data
+					String[] experiment = new String[getColumnCount()+1];
+					experiment[Column.NAME.ordinal()] = cells[0].substring(j);
+					experiment[Column.DESCRIPTION.ordinal()] = cells[1];
+					experiment[Column.START_DATE.ordinal()] = cells[3];
+					experiment[Column.END_DATE.ordinal()] = cells[4];
+					experiment[Column.MANAGER.ordinal()] = parseUsers(cells[5]);
+					experiment[Column.PI.ordinal()] = parseUsers(cells[6]);
+					int lastIndex = cells[7].indexOf("\"");
+					experiment[Column.USER.ordinal()] = parseUsers(cells[7].substring(0, lastIndex));
+					expTable.add(experiment);
+				}
+			}
+			dataValues = new String [expTable.size()][getColumnCount()];
+			for (int i=0; i < expTable.size(); i++) {
+				for (int j=0; j < getColumnCount(); j++) {
+					dataValues[i][j] = expTable.get(i)[j];
+				}
+			}
+		}
+
+		private String parseUsers(String userList) {
+			if (userList.equals("00000")) {
+				return "";
+			}
+			String parsedUsers = "";
+			String [] users = userList.split(":");
+			for (int i=0; i < users.length; i++) {
+				parsedUsers = parsedUsers + "d" + users[i] + " ";
+			}
+			return parsedUsers;
+		}
+	}
+
+	StorageServiceConnection sconnection;
+	private	JTable table = null;
+	private ExperimentTableModel tableModel = null;
+	private	JScrollPane scrollPane;
+
+	ExperimentList(String sector, OracleConnection oconnection, StorageServiceConnection sconnection, DaqServiceConnection dconnection) {
+		this.sconnection = sconnection;
+		setTitle("Experiment Import"); 
+		setSize(1000, 500);
+		setBackground(Color.gray);
+
+		JPanel topPanel = new JPanel();
+		topPanel.setBorder(BorderFactory.createLoweredSoftBevelBorder());
+		topPanel.setLayout( new BorderLayout() );
+		getContentPane().add( topPanel );
+
+		JPanel entryPanel = new JPanel(new FlowLayout());
+		topPanel.add(entryPanel, BorderLayout.NORTH);
+
+		JLabel entryLabel = new JLabel("Enter date range of experiment start date");
+		entryPanel.add(entryLabel);
+
+		UtilDateModel startModel = new UtilDateModel();
+		Properties p = new Properties();
+		p.put("text.today", "Today");
+		p.put("text.month", "Month");
+		p.put("text.year", "Year");
+		JDatePanelImpl startDatePanel = new JDatePanelImpl(startModel, p);
+		JDatePickerImpl startDatePicker = new JDatePickerImpl(startDatePanel, new DateLabelFormatter());
+		entryPanel.add(startDatePicker);
+
+		UtilDateModel endModel = new UtilDateModel();
+		JDatePanelImpl endDatePanel = new JDatePanelImpl(endModel, p);
+		JDatePickerImpl endDatePicker = new JDatePickerImpl(endDatePanel, new DateLabelFormatter());
+		entryPanel.add(endDatePicker);
+
+		JButton submitDatesBtn = new JButton("click to get experiments list");
+		entryPanel.add(submitDatesBtn);
+
+		JPanel selectPanel = new JPanel(new FlowLayout());
+		topPanel.add(selectPanel, BorderLayout.SOUTH);
+
+		JButton submitSelectionBtn = new JButton("select experiments and click the button to import");
+		selectPanel.add(submitSelectionBtn);
+		submitSelectionBtn.setVisible(false);
+
+		JButton submitSelectionStartExpBtn = new JButton("select experiments and click the button to import and start");
+		selectPanel.add(submitSelectionStartExpBtn);
+		submitSelectionStartExpBtn.setVisible(false);
+
+		submitDatesBtn.addActionListener(new ActionListener() {
+			public void actionPerformed(ActionEvent e)
+			{
+				try {
+					entryLabel.setVisible(true);
+					submitDatesBtn.setVisible(true);
+					submitSelectionBtn.setVisible(true);
+					submitSelectionStartExpBtn.setVisible(true);
+
+					DateLabelFormatter df = new DateLabelFormatter();
+					Date startSelectedDate = (Date) startDatePicker.getModel().getValue();
+		            String start = df.valueToString(startSelectedDate);
+					Date endSelectedDate = (Date) endDatePicker.getModel().getValue();
+		            String end = df.valueToString(endSelectedDate);
+					String list = oconnection.getExperiments(sector, start, end);
+					if (table == null) {
+						tableModel = new ExperimentTableModel();
+						tableModel.setTable(list);
+						table = new JTable(tableModel);
+						scrollPane = new JScrollPane( table );
+						topPanel.add( scrollPane, BorderLayout.CENTER );
+						table.setRowSelectionAllowed( true );
+						pack();
+						setVisible(true);
+						table.setVisible(true);
+					} else {
+						tableModel.setTable(list);
+						tableModel.fireTableStructureChanged();
+					}
+				} catch (SQLException | ParseException e1) {
+					JOptionPane.showMessageDialog(null, e1.getMessage());
+				}
+			}			
+		});
+
+		submitSelectionBtn.addActionListener(new ActionListener() {
+			public void actionPerformed(ActionEvent e)
+			{
+				int[] selection = table.getSelectedRows();
+				for (int i = 0; i < selection.length; i++) {
+					String experimentName = (String)table.getModel().getValueAt(selection[i], Column.NAME.ordinal());
+					String description = (String)table.getModel().getValueAt(selection[i], Column.DESCRIPTION.ordinal());
+					if (sconnection.addExperiment(experimentName, description) != ServiceConnection.ServiceConnectionStatus.SUCCESS) {
+						continue;
+					}
+					addRole((String)table.getModel().getValueAt(selection[i], Column.MANAGER.ordinal()), experimentName, Role.MANAGER);
+//					addRole((String)table.getModel().getValueAt(selection[i], Column.PI.ordinal()), experimentName, Role.PI);
+					addRole((String)table.getModel().getValueAt(selection[i], Column.USER.ordinal()), experimentName, Role.USER);
+				}
+				table.clearSelection();
+			}			
+		});
+
+		submitSelectionStartExpBtn.addActionListener(new ActionListener() {
+			public void actionPerformed(ActionEvent e)
+			{
+				int[] selection = table.getSelectedRows();
+				if ((selection.length > 0) && (dconnection.init() == ServiceConnectionStatus.SUCCESS) && (dconnection.login() == ServiceConnectionStatus.SUCCESS)) {
+					for (int i = 0; i < selection.length; i++) {
+						String experimentName = (String)table.getModel().getValueAt(selection[i], Column.NAME.ordinal());
+						String description = (String)table.getModel().getValueAt(selection[i], Column.DESCRIPTION.ordinal());
+						if (sconnection.addExperiment(experimentName, description) != ServiceConnection.ServiceConnectionStatus.SUCCESS) {
+							continue;
+						}
+						addRole((String)table.getModel().getValueAt(selection[i], Column.MANAGER.ordinal()), experimentName, Role.MANAGER);
+						//					addRole((String)table.getModel().getValueAt(selection[i], Column.PI.ordinal()), experimentName, Role.PI);
+						addRole((String)table.getModel().getValueAt(selection[i], Column.USER.ordinal()), experimentName, Role.USER);
+						if (sconnection.startExperiment(experimentName) == ServiceConnection.ServiceConnectionStatus.SUCCESS) {
+							dconnection.startDaq(experimentName, experimentName);						
+						}
+					}
+				}
+				table.clearSelection();
+			}
+		});
+	}
+
+	private void addRole(String userList, String experimentName, Role role) {
+		if ((userList == null) || userList.isEmpty()) {
+			return;
+		}
+		String [] users = userList.split(" ");
+		for (int i = 0; i < users.length; i++) {
+			sconnection.addExperimentUser(users[i], experimentName, role.toString());
+		}
+	}
+
+}
diff --git a/tools/ExperimentSynchronizer/src/gov/anl/dm/esafsync/Gui.java b/tools/ExperimentSynchronizer/src/gov/anl/dm/esafsync/Gui.java
new file mode 100644
index 0000000000000000000000000000000000000000..acfa7e431ee51bf0c3a261c5e625e14bb37ab5fe
--- /dev/null
+++ b/tools/ExperimentSynchronizer/src/gov/anl/dm/esafsync/Gui.java
@@ -0,0 +1,92 @@
+package gov.anl.dm.esafsync;
+
+import gov.anl.dm.esafsync.serviceconn.DaqServiceConnection;
+import gov.anl.dm.esafsync.serviceconn.ServiceConnection;
+import gov.anl.dm.esafsync.serviceconn.StorageServiceConnection;
+
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.sql.SQLException;
+import java.util.Properties;
+
+import javax.swing.JFrame;
+import javax.swing.JOptionPane;
+import javax.swing.SwingUtilities;
+
+class Gui
+{
+	public static void main(String arg[])
+	{
+
+		String configFile = arg[0];
+		Properties configProperties = new Properties();
+		setConfig(configFile, configProperties);
+
+		SwingUtilities.invokeLater(new Runnable() {
+			public void run() {
+				new Gui(configProperties);
+			}
+		});			
+	}
+
+	private Gui(Properties configProperties) {		
+		OracleConnection oconnection = new OracleConnection();
+		try {
+			oconnection.connect(configProperties);
+		} catch (SQLException e1) {
+			JOptionPane.showMessageDialog(null, e1.getMessage());
+				System.exit(0);
+		}
+		StorageServiceConnection sconnection = new StorageServiceConnection();
+		if (sconnection.init(configProperties.getProperty("dm.storageServ.connection")) != ServiceConnection.ServiceConnectionStatus.SUCCESS) {
+			System.exit(0);
+		}
+		DaqServiceConnection dconnection = new DaqServiceConnection(configProperties.getProperty("dm.daqServ.connection"));
+		try
+		{
+			LoginWindow frame = new LoginWindow(sconnection, oconnection, dconnection);
+			frame.setSize(300,200);
+			frame.setVisible(true);
+			frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
+			frame.addWindowListener(new WindowAdapter() {
+				@Override	 
+				public void windowClosing(WindowEvent e) {	 
+					if (sconnection != null) {
+						sconnection.close();
+					}
+					if (oconnection != null) {
+						oconnection.close();
+					}
+					System.exit(0);	 
+				}	 
+			});
+		}
+		catch(Exception e) {
+			JOptionPane.showMessageDialog(null, e.getMessage());
+		}
+	}
+
+	private static void setConfig(String config, Properties configProperties) {
+		InputStream configInputStream = null;
+		try {
+			configInputStream = new FileInputStream(config);
+		} catch (Exception e) {
+			JOptionPane.showMessageDialog(null, e.getMessage());
+			System.exit(0);
+		}    
+
+		if (configInputStream != null) {
+			try {
+				configProperties.load(configInputStream);
+			} catch (IOException e1) {
+				JOptionPane.showMessageDialog(null, e1.getMessage());
+				System.exit(0);
+			}
+		} else {
+			System.exit(-1);
+		}
+	}
+}
\ No newline at end of file
diff --git a/tools/ExperimentSynchronizer/src/gov/anl/dm/esafsync/LoginWindow.java b/tools/ExperimentSynchronizer/src/gov/anl/dm/esafsync/LoginWindow.java
new file mode 100644
index 0000000000000000000000000000000000000000..0dd93486d977e05667436fa230971ac094b8f69b
--- /dev/null
+++ b/tools/ExperimentSynchronizer/src/gov/anl/dm/esafsync/LoginWindow.java
@@ -0,0 +1,102 @@
+package gov.anl.dm.esafsync;
+
+
+
+import gov.anl.dm.esafsync.serviceconn.DaqServiceConnection;
+import gov.anl.dm.esafsync.serviceconn.ServiceConnection;
+import gov.anl.dm.esafsync.serviceconn.StorageServiceConnection;
+
+import javax.swing.*;
+
+import java.awt.event.*;
+
+public final class LoginWindow extends JFrame {
+	private static final long serialVersionUID = 1L;
+	
+	StorageServiceConnection sconnection;
+	OracleConnection oconnection;
+	JPanel panel;
+
+	LoginWindow(StorageServiceConnection sconnection, OracleConnection oconnection, DaqServiceConnection dconnection) {
+		this.sconnection = sconnection;
+		this.oconnection = oconnection;
+
+		setTitle("Experiment Import Login");		
+		JPanel panel = new JPanel();
+		add(panel);
+		panel.setLayout(null);
+
+		JLabel userLabel = new JLabel("User");
+		userLabel.setBounds(10, 10, 80, 25);
+		panel.add(userLabel);
+
+		JTextField userText = new JTextField(20);
+		userText.setBounds(100, 10, 160, 25);
+		panel.add(userText);
+
+		JLabel passwordLabel = new JLabel("Password");
+		passwordLabel.setBounds(10, 40, 80, 25);
+		panel.add(passwordLabel);
+
+		JPasswordField passwordText = new JPasswordField(20);
+		passwordText.setBounds(100, 40, 160, 25);
+		panel.add(passwordText);
+
+		JLabel sectorLabel = new JLabel("Sector");
+		sectorLabel.setBounds(10, 70, 80, 25);
+		panel.add(sectorLabel);
+
+		JTextField sectorText = new JTextField(20);
+		sectorText.setBounds(100, 70, 160, 25);
+		panel.add(sectorText);
+
+		JButton submitButton = new JButton("submit");
+		submitButton.setBounds(10, 120, 80, 25);
+		panel.add(submitButton);
+
+		submitButton.addActionListener(new ActionListener() {
+			public void actionPerformed(ActionEvent e) {
+				String user = userText.getText().trim();
+				String pass = new String(passwordText.getPassword());
+				if ((user == null) || (user.isEmpty()) || (pass == null) || (pass.isEmpty())) {
+					JOptionPane.showMessageDialog(null,"enter login and password",
+							"Error",JOptionPane.ERROR_MESSAGE);					
+				} else {
+					sconnection.setLogin(user, pass);
+					dconnection.setLogin(user, pass);
+					int aaResult = sconnection.login();
+					switch (aaResult) {
+					case ServiceConnection.ServiceConnectionStatus.SUCCESS:
+						setVisible(false);
+						ExperimentList page=new ExperimentList(sectorText.getText().trim(), oconnection, sconnection, dconnection);
+						page.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
+						page.addWindowListener(new WindowAdapter() {
+							@Override	 
+							public void windowClosing(WindowEvent e) {	 
+								sconnection.close();
+								oconnection.close();
+								System.exit(0);	 
+							}	 
+						});
+						page.setVisible(true);
+						break;
+					case ServiceConnection.ServiceConnectionStatus.AUTHORIZATION_ERROR:
+						userText.setText("");
+						passwordText.setText("");
+						JOptionPane.showMessageDialog(null,"not authorized user",
+								"Error",JOptionPane.ERROR_MESSAGE);
+						break;
+					case ServiceConnection.ServiceConnectionStatus.AUTHENTICATION_ERROR:
+						userText.setText("");
+						passwordText.setText("");
+						JOptionPane.showMessageDialog(null,"Incorrect login or password",
+								"Error",JOptionPane.ERROR_MESSAGE);
+						break;
+					default:
+						// error message generated from exception
+					}
+				}
+			}
+		});
+	}
+}
\ No newline at end of file
diff --git a/tools/ExperimentSynchronizer/src/gov/anl/dm/esafsync/OracleConnection.java b/tools/ExperimentSynchronizer/src/gov/anl/dm/esafsync/OracleConnection.java
new file mode 100644
index 0000000000000000000000000000000000000000..f91bfe76abcf54d13e8d48d7801921965e919629
--- /dev/null
+++ b/tools/ExperimentSynchronizer/src/gov/anl/dm/esafsync/OracleConnection.java
@@ -0,0 +1,50 @@
+package gov.anl.dm.esafsync;
+
+import java.sql.CallableStatement;
+import java.sql.DriverManager;
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.sql.Types;
+import java.util.Properties;
+
+public class OracleConnection {
+    Connection connection = null;
+    Statement statement;
+
+    void connect(Properties config) throws SQLException {
+    	DriverManager.registerDriver(new oracle.jdbc.OracleDriver());
+    	connection = DriverManager.getConnection(
+    			config.getProperty("oracle.database.connection"),
+    			config.getProperty("oracle.database.username"),
+    			config.getProperty("oracle.database.password"));
+    }
+
+    String getExperiments(String sector, String lower, String upper) throws SQLException {
+    	CallableStatement callableStatement =
+    		    connection.prepareCall("{? = call safety.esaf_exp0001.get_exps(?,?,?,?)}");
+
+    	callableStatement.registerOutParameter(1, Types.VARCHAR);
+    	callableStatement.setObject(2, "");
+    	callableStatement.setString(3, sector);
+    	callableStatement.setString(4, lower);
+    	callableStatement.setString(5, upper);
+
+    	callableStatement.execute();
+    	
+    	return callableStatement.getString(1);
+     }
+    
+    void close() {
+        try {
+        	if (statement != null) {
+        		statement.close();
+        	}
+        	if (connection != null) {
+                connection.close();        		
+        	}
+        } catch (SQLException ex) {
+//            nothing to do here
+        }
+    }
+}
diff --git a/tools/ExperimentSynchronizer/src/gov/anl/dm/esafsync/PsqlConnection.java b/tools/ExperimentSynchronizer/src/gov/anl/dm/esafsync/PsqlConnection.java
new file mode 100644
index 0000000000000000000000000000000000000000..cdfc388953b0f2fdaa508f662127efc5d5ea6199
--- /dev/null
+++ b/tools/ExperimentSynchronizer/src/gov/anl/dm/esafsync/PsqlConnection.java
@@ -0,0 +1,91 @@
+package gov.anl.dm.esafsync;
+
+import java.sql.DriverManager;
+import java.sql.Connection;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.Properties;
+
+public class PsqlConnection {
+	public final class UserInfo {
+		private final String signature;
+		private final int id;
+		
+		UserInfo(String signature, int id) {
+			this.signature = signature;
+			this.id = id;
+		}
+		String getSignature() {
+			return signature;
+		}
+
+		int getId() {
+			return id;
+		}
+	}
+
+	final int ADMIN_ROLE_ID = 1;
+    Connection connection = null;
+
+    public void connect(Properties config) throws SQLException, ClassNotFoundException {
+            Class.forName("org.postgresql.Driver");
+            connection = DriverManager.getConnection(
+                    config.getProperty("dm.database.connection"),
+                    config.getProperty("dm.database.username"),
+                    config.getProperty("dm.database.password"));
+    }
+    
+    UserInfo getUserInfo(String user) throws SQLException {
+    	String signature = null;
+    	int id = -1;
+    	Statement statement = connection.createStatement();
+    	if (statement != null) {
+    		ResultSet results = statement.executeQuery("SELECT * FROM user_info WHERE username = '" + user + "';");
+    		if (results != null) {
+    			while (results.next()) {
+    				signature = results.getString("password");
+    				if (signature.length() > 0) {
+    					id = Integer.parseInt(results.getString("id"));
+    					break;
+    				}
+    			}
+        		results.close();
+    		}
+    		statement.close();
+    	}
+    	if (id < 0) {
+    		return null;
+    	} else {
+    		return new UserInfo(signature, id);
+    	}
+    }
+
+    boolean isAuthorized(int id) throws SQLException {
+    	boolean isAuthorized = false;
+    	Statement statement = connection.createStatement();
+    	if (statement != null) {
+    		ResultSet results = statement.executeQuery("SELECT * FROM user_system_role WHERE user_id = " + id + ";");
+    		if (results != null) {
+    			while (results.next()) {
+    				int roleId = Integer.parseInt(results.getString("role_type_id"));
+    				if (roleId == ADMIN_ROLE_ID) {
+    					isAuthorized = true;
+    					break;
+    				}
+    			}
+        		results.close();
+    		}
+    		statement.close();
+    	}
+		return isAuthorized;    	
+    }
+    
+    void close() {
+        try {
+            connection.close();
+        } catch (SQLException ex) {
+            // nothing to do here
+        }
+    }
+}
diff --git a/tools/ExperimentSynchronizer/src/gov/anl/dm/esafsync/serviceconn/DaqServiceConnection.java b/tools/ExperimentSynchronizer/src/gov/anl/dm/esafsync/serviceconn/DaqServiceConnection.java
new file mode 100644
index 0000000000000000000000000000000000000000..1975ee376acc43d5a9aba4caf6df8287575892c9
--- /dev/null
+++ b/tools/ExperimentSynchronizer/src/gov/anl/dm/esafsync/serviceconn/DaqServiceConnection.java
@@ -0,0 +1,30 @@
+package gov.anl.dm.esafsync.serviceconn;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class DaqServiceConnection extends ServiceConnection{
+
+	class Keyword {
+		static final String DAQ_EXPERIMENT_NAME = "experimentName";
+		static final String DAQ_DATA_DIRECTORY = "dataDirectory";
+	}
+	
+	private final String url;
+
+	public DaqServiceConnection(String url) {
+		this.url = url;
+	}
+	
+    public final int init() {
+		return super.init(url);
+	}
+
+	public void startDaq(String experimentName, String directory) {
+    	Map<String, String> data = new HashMap<>();
+    	data.put(Keyword.DAQ_EXPERIMENT_NAME, experimentName);
+    	data.put(Keyword.DAQ_DATA_DIRECTORY, directory);
+		invokeSessionPostRequest(StorageServUrl.EXPERIMENT, data);    	
+    }
+    
+}
diff --git a/tools/ExperimentSynchronizer/src/gov/anl/dm/esafsync/serviceconn/ServiceConnection.java b/tools/ExperimentSynchronizer/src/gov/anl/dm/esafsync/serviceconn/ServiceConnection.java
new file mode 100644
index 0000000000000000000000000000000000000000..bb6d0d151b30aaae32152c513eb71dc9076f2ac0
--- /dev/null
+++ b/tools/ExperimentSynchronizer/src/gov/anl/dm/esafsync/serviceconn/ServiceConnection.java
@@ -0,0 +1,513 @@
+package gov.anl.dm.esafsync.serviceconn;
+
+import java.io.BufferedReader;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.UnsupportedEncodingException;
+import java.net.ConnectException;
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.ProtocolException;
+import java.net.URL;
+import java.net.URLEncoder;
+import java.util.Base64;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.swing.JOptionPane;
+
+public class ServiceConnection {
+
+	class DmHttpHeaders {
+		static final String DM_SET_COOKIE_HEADER = "Set-Cookie";
+		final static String DM_SESSION_ROLE_HTTP_HEADER = "Dm-Session-Role";
+		final static String DM_STATUS_CODE_HTTP_HEADER = "Dm-Status-Code";
+		final static String DM_STATUS_MESSAGE_HTTP_HEADER = "Dm-Status-Message";
+		final static String DM_EXCEPTION_TYPE_HTTP_HEADER = "Dm-Exception-Type";
+	}
+
+	class DmHttpStatus {
+		static final int DM_HTTP_OK = 200;
+		static final int DM_HTTP_UNAUTHORIZED = 401;
+		static final int DM_HTTP_INTERNAL_ERROR = 500; 
+	}
+
+	class StorageServUrl {
+		static final String LOGIN_REQUEST = "/login";
+		static final String EXPERIMENT = "/experiments";
+		static final String EXPERIMENT_USER = "/usersByExperiment";
+		static final String START_EXPERIMENT = "/experiments/start";
+		static final String START_DAQ = "/experiments/startDaq";
+	}
+
+	class DmStatus {
+		static final int DM_OK = 0;
+		static final int DM_ERROR = 1;
+		static final int DM_INTERNAL_ERROR = 2;
+		static final int DM_COMMUNICATION_ERROR = 3; 
+		static final int DM_CONFIGURATION_ERROR = 4;
+		static final int DM_AUTHORIZATION_ERROR = 5;
+		static final int DM_AUTHENTICATION_ERROR = 6;
+		static final int DM_DB_ERROR = 7;
+		static final int DM_URL_ERROR = 8; 
+		static final int DM_TIMEOUT = 9;
+		static final int DM_INVALID_ARGUMENT = 10;
+		static final int DM_INVALID_REQUEST = 11;
+		static final int DM_INVALID_SESSION = 12;    
+		static final int DM_COMMAND_FAILED =13;
+		static final int DM_OBJECT_NOT_FOUND = 14;
+		static final int DM_OBJECT_ALREADY_EXISTS = 15;
+		static final int DM_INVALID_OBJECT_STATE = 16;
+	}
+	
+	class Keyword {
+		static final String USERNAME = "username";
+		static final String PASSWORD = "password";
+	}
+	
+	public class ServiceConnectionStatus {
+		public static final int SUCCESS = 0;
+		public static final int AUTHENTICATION_ERROR = -2;		
+		public static final int AUTHORIZATION_ERROR = -3;		
+		public static final int INVALID_OPERATION = -4;		
+		public static final int CONNECTION_ERROR = -5;		
+		public static final int PROTOCOL_ERROR = -6;		
+		public static final int ERROR = -1;		
+	}
+	
+	private final class Login {
+		private String username;
+		private String password;
+		
+		private Login(String username, String password) {
+			this.username = username;
+			this.password = password;
+		}
+		private void clear() {
+			this.username = null;
+			this.password = null;			
+		}
+		private int login() {
+			int result = ServiceConnection.this.login(username, password);
+			clear();
+			return result;
+		}
+	}
+	
+	static final String ESAF_EXPERIMENT_TYPE = "1";
+
+    protected URL serviceUrl;
+    private Session session = new Session();
+    private Login login;
+
+    public final int init(String serviceUrl) {
+        if (serviceUrl == null) {
+        	JOptionPane.showMessageDialog(null,"service url is not specified.", "Error",JOptionPane.ERROR_MESSAGE);
+        	return ServiceConnectionStatus.ERROR;
+        }
+        try {
+            this.serviceUrl = new URL(serviceUrl);
+        } catch (MalformedURLException ex) {
+        	JOptionPane.showMessageDialog(null,"Malformed service url: " + serviceUrl, "Error",JOptionPane.ERROR_MESSAGE);
+        	JOptionPane.showMessageDialog(null, ex.getMessage());
+        	return ServiceConnectionStatus.ERROR;
+        }
+
+        String protocol = this.serviceUrl.getProtocol();
+        System.out.println("protocol "+ protocol);
+        if (protocol == null) {
+        	JOptionPane.showMessageDialog(null,"Unsupported service protocol specified in " + serviceUrl, "Error",JOptionPane.ERROR_MESSAGE);
+        	return ServiceConnectionStatus.ERROR;
+        }
+    	return ServiceConnectionStatus.SUCCESS;
+    }
+
+    private URL getServiceUrl() {
+        return serviceUrl;
+    }
+
+    private int checkHttpResponse(HttpURLConnection connection)  {
+    	int code = 0;
+    	String exceptionType = connection.getHeaderField(DmHttpHeaders.DM_EXCEPTION_TYPE_HTTP_HEADER);
+        if (exceptionType != null) {
+            String statusMessage = connection.getHeaderField(DmHttpHeaders.DM_STATUS_MESSAGE_HTTP_HEADER);
+            String statusCode = connection.getHeaderField(DmHttpHeaders.DM_STATUS_CODE_HTTP_HEADER);
+            code = Integer.parseInt(statusCode);
+        	JOptionPane.showMessageDialog(null,exceptionType +" "+ code +" "+ statusMessage, "Error",JOptionPane.ERROR_MESSAGE);
+        }
+        switch (code) {
+        case DmStatus.DM_OK :
+        	return ServiceConnectionStatus.SUCCESS;
+        case DmStatus.DM_AUTHENTICATION_ERROR:
+        	return ServiceConnectionStatus.AUTHENTICATION_ERROR;
+        case DmStatus.DM_AUTHORIZATION_ERROR:
+        	return ServiceConnectionStatus.AUTHORIZATION_ERROR;
+        case DmStatus.DM_COMMUNICATION_ERROR:
+        case DmStatus.DM_CONFIGURATION_ERROR:
+        case DmStatus.DM_ERROR:
+        case DmStatus.DM_INVALID_SESSION:
+        case DmStatus.DM_TIMEOUT:
+        case DmStatus.DM_URL_ERROR:
+        	return ServiceConnectionStatus.ERROR;
+        	default:
+        		return ServiceConnectionStatus.INVALID_OPERATION;
+        }
+    }
+
+    private String getFullRequestUrl(String requestUrl) {
+        String url = serviceUrl + requestUrl;
+        return url;
+    }
+
+    private static String preparePostData(Map<String, String> data) {
+        try {
+            String postData = "";
+            String separator = "";
+            for (String key : data.keySet()) {
+                postData += separator + key + "=" + URLEncoder.encode(data.get(key), "UTF8");
+                separator = "&";
+            }
+            return postData;
+        } catch (UnsupportedEncodingException ex) {
+        	JOptionPane.showMessageDialog(null, ex.getMessage());
+            return null;
+        }
+    }
+
+    private void updateSessionCookie(HttpURLConnection connection) {
+        String cookie = connection.getHeaderField(DmHttpHeaders.DM_SET_COOKIE_HEADER);
+        if (cookie != null) {
+            session.setCookie(cookie);
+        }
+        String sessionRole = connection.getHeaderField(DmHttpHeaders.DM_SESSION_ROLE_HTTP_HEADER);
+        if (sessionRole != null) {
+            session.setRole(Session.Role.fromString(sessionRole));
+        }
+    }
+
+    private static int sendPostData(Map<String, String> data, HttpURLConnection connection) {
+        String postData = preparePostData(data);
+        try (DataOutputStream dos = new DataOutputStream(connection.getOutputStream())) {
+            dos.writeBytes(postData);
+            dos.flush();
+            return ServiceConnectionStatus.SUCCESS;
+        } catch (IOException ex) {
+        	JOptionPane.showMessageDialog(null, ex.getMessage());
+        	JOptionPane.showMessageDialog(null, "misconfigured service or closed ", "Error",JOptionPane.ERROR_MESSAGE);
+        	return ServiceConnectionStatus.CONNECTION_ERROR;
+        }
+    }
+
+    private static String readHttpResponse(HttpURLConnection connection) {
+        try {
+            BufferedReader br = new BufferedReader(new InputStreamReader(
+                    (connection.getInputStream())));
+            StringBuilder sb = new StringBuilder();
+            String output;
+            while ((output = br.readLine()) != null) {
+                sb.append(output);
+                sb.append('\n');
+            }
+            return sb.toString();
+        } catch (IOException ex) {
+        	JOptionPane.showMessageDialog(null, ex.getMessage());
+            return null;
+        }
+    }
+
+    private static void setCookieRequestHeader(HttpURLConnection connection, String sessionCookie) {
+        if (sessionCookie != null) {
+            connection.setRequestProperty("Cookie", sessionCookie);
+        }
+    }
+
+    private static int setPostRequestHeaders(HttpURLConnection connection, String sessionCookie)  {
+        int resp = setPostRequestHeaders(connection);
+        if (resp != ServiceConnectionStatus.SUCCESS) {
+        	return resp;
+        }
+        setCookieRequestHeader(connection, sessionCookie);
+        return resp;
+    }
+
+     private static int setPostRequestHeaders(HttpURLConnection connection)  {
+        try {
+            connection.setDoOutput(true);
+            connection.setRequestMethod("POST");
+            connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
+            return ServiceConnectionStatus.SUCCESS;
+        } catch (ProtocolException ex) {
+        	JOptionPane.showMessageDialog(null, ex.getMessage());
+            return ServiceConnectionStatus.PROTOCOL_ERROR;
+        }
+    }
+
+    protected int invokeSessionPostRequest(String requestUrl, Map<String, String> data) {
+        String urlString = getFullRequestUrl(requestUrl);
+        HttpURLConnection connection = null;
+        try {
+            String sessionCookie = session.verifyCookie();
+            URL url = new URL(urlString);
+            connection = (HttpURLConnection) url.openConnection();
+
+            int resp = setPostRequestHeaders(connection, sessionCookie);
+            if ( resp != ServiceConnectionStatus.SUCCESS) {
+            	return resp;
+            }
+            resp = sendPostData(data, connection);
+            if ( resp != ServiceConnectionStatus.SUCCESS) {
+            	return resp;
+            }
+            updateSessionCookie(connection);
+            resp = checkHttpResponse(connection);
+            readHttpResponse(connection);
+            return resp;
+        } catch (Exception ex) {
+            String errorMsg = "Cannot connect to " + getServiceUrl();
+        	JOptionPane.showMessageDialog(null,errorMsg, "Error",JOptionPane.ERROR_MESSAGE);
+            JOptionPane.showMessageDialog(null, ex.getMessage());
+            return ServiceConnectionStatus.ERROR;
+        } finally {
+            if (connection != null) {
+                connection.disconnect();
+            }
+        }
+    }
+
+    private static int setPutRequestHeaders(HttpURLConnection connection, String sessionCookie) {
+    	int resp = setPutRequestHeaders(connection);
+        if ( resp != ServiceConnectionStatus.SUCCESS) {
+        	return resp;
+        }
+    	setCookieRequestHeader(connection, sessionCookie);
+    	return resp;
+    }
+
+    private static int setPutRequestHeaders(HttpURLConnection connection) {
+    	try {
+    		connection.setDoOutput(true);
+    		connection.setRequestMethod("PUT");
+    		connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
+            return ServiceConnectionStatus.SUCCESS;
+    	} catch (ProtocolException ex) {
+    		JOptionPane.showMessageDialog(null, ex.getMessage());
+            return ServiceConnectionStatus.PROTOCOL_ERROR;
+    	}
+    }
+
+    protected int invokeSessionPutRequest(String requestUrl, Map<String, String> data) {
+        String urlString = getFullRequestUrl(requestUrl);
+        HttpURLConnection connection = null;
+        try {
+            String sessionCookie = session.verifyCookie();
+            URL url = new URL(urlString);
+            connection = (HttpURLConnection) url.openConnection();
+
+            int resp = setPutRequestHeaders(connection, sessionCookie);
+            if ( resp != ServiceConnectionStatus.SUCCESS) {
+            	return resp;
+            }
+            resp = sendPostData(data, connection);
+            if ( resp != ServiceConnectionStatus.SUCCESS) {
+            	return resp;
+            }
+            updateSessionCookie(connection);
+            resp = checkHttpResponse(connection);
+            readHttpResponse(connection);
+            return resp;
+        } catch (Exception ex) {
+            String errorMsg = "Cannot connect to " + getServiceUrl();
+        	JOptionPane.showMessageDialog(null,errorMsg, "Error",JOptionPane.ERROR_MESSAGE);
+            JOptionPane.showMessageDialog(null, ex.getMessage());
+            return ServiceConnectionStatus.ERROR;
+        } finally {
+            if (connection != null) {
+                connection.disconnect();
+            }
+        }
+    }
+
+    protected String encode(String s) {
+		byte[] encoded1 = Base64.getEncoder().encode(s.getBytes());
+		String encoded2 = Base64.getEncoder().encodeToString(encoded1);
+		return encoded2;    	
+    }
+    
+//    private String verifySessionCookie()  {
+//        return session.verifyCookie();
+//    }
+//
+//    private static String getResponseHeaders(HttpURLConnection connection) {
+//        String headerString = "";
+//        Map<String, List<String>> headerMap = connection.getHeaderFields();
+//        for (String key : headerMap.keySet()) {
+//            List<String> values = headerMap.get(key);
+//            headerString += key + ": " + values + "\n";
+//        }
+//        return headerString;
+//    }
+//
+//    private String invokeSessionGetRequest(String requestUrl) {
+//        String urlString = getFullRequestUrl(requestUrl);
+//        HttpURLConnection connection = null;
+//        try {
+//            String sessionCookie = session.verifyCookie();
+//            URL url = new URL(urlString);
+//            connection = (HttpURLConnection) url.openConnection();
+//
+//            setGetRequestHeaders(connection, sessionCookie);
+//            updateSessionCookie(connection);
+//            checkHttpResponse(connection);
+//            return readHttpResponse(connection);
+//        } catch (Exception ex) {
+//            String errorMsg = "Cannot connect to " + getServiceUrl();
+//        	JOptionPane.showMessageDialog(null,errorMsg, "Error",JOptionPane.ERROR_MESSAGE);
+//            JOptionPane.showMessageDialog(null, ex.getMessage());
+//            return null;
+//        } finally {
+//            if (connection != null) {
+//                connection.disconnect();
+//            }
+//        }
+//    }
+//
+//     private String invokeGetRequest(String requestUrl) {
+//        String urlString = getFullRequestUrl(requestUrl);
+//        HttpURLConnection connection = null;
+//        try {
+//            URL url = new URL(urlString);
+//            connection = (HttpURLConnection) url.openConnection();
+//            updateSessionCookie(connection);
+//            checkHttpResponse(connection);
+//            return readHttpResponse(connection);
+//        } catch (Exception ex) {
+//            String errorMsg = "Cannot connect to " + getServiceUrl();
+//        	JOptionPane.showMessageDialog(null,errorMsg, "Error",JOptionPane.ERROR_MESSAGE);
+//            JOptionPane.showMessageDialog(null, ex.getMessage());
+//            return null;
+//        } finally {
+//            if (connection != null) {
+//                connection.disconnect();
+//            }
+//        }
+//    }
+//
+//     private String invokeSessionDeleteRequest(String requestUrl) {
+//         String urlString = getFullRequestUrl(requestUrl);
+//         HttpURLConnection connection = null;
+//         try {
+//             String sessionCookie = session.verifyCookie();
+//             URL url = new URL(urlString);
+//             connection = (HttpURLConnection) url.openConnection();
+//
+//             setDeleteRequestHeaders(connection, sessionCookie);
+//             updateSessionCookie(connection);
+//             checkHttpResponse(connection);
+//             return readHttpResponse(connection);
+//         } catch (Exception ex) {
+//             String errorMsg = "Cannot connect to " + getServiceUrl();
+//         	JOptionPane.showMessageDialog(null,errorMsg, "Error",JOptionPane.ERROR_MESSAGE);
+//             JOptionPane.showMessageDialog(null, ex.getMessage());
+//             return null;
+//         } finally {
+//             if (connection != null) {
+//                 connection.disconnect();
+//             }
+//         }
+//     }
+//
+//    private static void setGetRequestHeaders(HttpURLConnection connection, String sessionCookie) {
+//        setGetRequestHeaders(connection);
+//        setCookieRequestHeader(connection, sessionCookie);
+//    }
+//
+//     private static void setGetRequestHeaders(HttpURLConnection connection) {
+//        try {
+//            connection.setRequestMethod("GET");
+//        } catch (ProtocolException ex) {
+//        	JOptionPane.showMessageDialog(null, ex.getMessage());
+//        }
+//    }
+//
+//    private static void setDeleteRequestHeaders(HttpURLConnection connection, String sessionCookie) {
+//        setDeleteRequestHeaders(connection);
+//        setCookieRequestHeader(connection, sessionCookie);
+//    }
+//
+//    private static void setDeleteRequestHeaders(HttpURLConnection connection) {
+//        try {
+//            connection.setRequestMethod("DELETE");
+//        } catch (ProtocolException ex) {
+//        	JOptionPane.showMessageDialog(null, ex.getMessage());
+//        }
+//    }
+//
+//
+//	private void getData(HttpURLConnection connection) {
+//		BufferedReader in;
+//		try {
+//			in = new BufferedReader(
+//					new InputStreamReader(connection.getInputStream()));
+//		String inputLine;
+//		StringBuffer response = new StringBuffer();
+//
+//		while ((inputLine = in.readLine()) != null) {
+//			response.append(inputLine);
+//			System.out.println(inputLine);
+//		}
+//		} catch (IOException e) {
+//			JOptionPane.showMessageDialog(null, e.getMessage());
+//		}
+//	}
+
+    public final void setLogin(String username, String password) {
+    	this.login = new Login(username, password);
+    }
+
+    public final int login() {
+    	return login.login();
+    }
+
+     private final int login(String username, String password) {
+        HttpURLConnection connection = null;
+        try {
+            String urlString = getFullRequestUrl(StorageServUrl.LOGIN_REQUEST);
+            URL url = new URL(urlString);
+            connection = (HttpURLConnection) url.openConnection();
+            setPostRequestHeaders(connection);
+             HashMap<String, String> loginData = new HashMap<>();
+            loginData.put(Keyword.USERNAME, username);
+            loginData.put(Keyword.PASSWORD, password);
+
+            int resp = sendPostData(loginData, connection);
+            if (resp != ServiceConnectionStatus.SUCCESS) {
+            	return resp;
+            }
+            resp = checkHttpResponse(connection);
+            if (resp != ServiceConnectionStatus.SUCCESS) {
+            	return resp;
+            }
+            session.setUsername(username);
+//            session.setId(sessionId);
+            updateSessionCookie(connection);
+            return ServiceConnectionStatus.SUCCESS;
+        } catch (ConnectException ex) {
+            String errorMsg = "Cannot connect to " + getServiceUrl();
+        	JOptionPane.showMessageDialog(null,errorMsg, "Error",JOptionPane.ERROR_MESSAGE);
+            return ServiceConnectionStatus.ERROR;
+        } catch (Exception ex) {
+        	JOptionPane.showMessageDialog(null, ex.getMessage());
+            return ServiceConnectionStatus.ERROR;
+        } finally {
+            if (connection != null) {
+                connection.disconnect();
+            }
+        }
+    }
+
+	public void close() {
+		session = null;		
+	}
+    
+}
diff --git a/tools/ExperimentSynchronizer/src/gov/anl/dm/esafsync/serviceconn/Session.java b/tools/ExperimentSynchronizer/src/gov/anl/dm/esafsync/serviceconn/Session.java
new file mode 100644
index 0000000000000000000000000000000000000000..2b72e2c3c369b5239b5f4fb7b4cff087d4eb3d6a
--- /dev/null
+++ b/tools/ExperimentSynchronizer/src/gov/anl/dm/esafsync/serviceconn/Session.java
@@ -0,0 +1,131 @@
+package gov.anl.dm.esafsync.serviceconn;
+
+import java.io.Serializable;
+import java.net.HttpCookie;
+
+import javax.swing.JOptionPane;
+
+/**
+ * CDB session class, used for keeping all session-related information (session
+ * id, username, role, etc.).
+ */
+public class Session implements Serializable {
+
+	public enum Role {
+
+	    USER("User"),
+	    ADMIN("Administrator");
+
+	    private final String type;
+
+	    private Role(String type) {
+	        this.type = type;
+	    }
+
+	    @Override
+	    public String toString() {
+	        return type;
+	    }
+
+	    public static Role fromString(String type) {
+	        Role role = null;
+	        switch (type) {
+	            case "User":
+	                role = USER;
+	                break;
+	            case "Administrator":
+	                role = ADMIN;
+	                break;
+	        }
+	        return role;
+	    }
+	}
+
+    private static final long serialVersionUID = 1L;
+
+    private String id = null;
+    private String username = null;
+    private String cookie = null;
+    
+    private Role role = null;
+
+    public Session() {
+    }
+
+    public String getUsername() {
+        return username;
+    }
+
+    public void setUsername(String username) {
+        this.username = username;
+    }
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public String getCookie() {
+        return cookie;
+    }
+
+    public void setCookie(String cookie) {
+        this.cookie = cookie;
+    }
+
+    public String verifyCookie() {
+        if (cookie == null) {
+        	JOptionPane.showMessageDialog(null, "Valid session has not been established.", "Info",JOptionPane.INFORMATION_MESSAGE);
+        } else {
+            HttpCookie httpCookie = HttpCookie.parse(cookie).get(0);
+            if (httpCookie.hasExpired()) {
+            	JOptionPane.showMessageDialog(null, "Session has expired.", "Info",JOptionPane.INFORMATION_MESSAGE);
+            }
+        }
+        return cookie;
+    }
+
+    public Role getRole() {
+        return role;
+    }
+
+    public void setRole(Role role) {
+        this.role = role;
+    }
+
+    public boolean isAdminRole() {
+        if (role != null) {
+            return role.equals(Role.ADMIN);
+        }
+        return false;
+    }
+
+    public boolean isUserRole() {
+        if (role != null) {
+            return role.equals(Role.USER);
+        }
+        return false;
+    }
+
+    @Override
+    public String toString() {
+        String result = "{ ";
+        String delimiter = "";
+        if (username != null) {
+            result += "username :" + username;
+            delimiter = "; ";
+        }
+        if (id != null) {
+            result += delimiter + "id : " + id;
+            delimiter = "; ";
+        }
+        if (cookie != null) {
+            result += delimiter + "cookie : " + cookie;
+        }
+        result += " }";
+        return result;
+    }
+}
diff --git a/tools/ExperimentSynchronizer/src/gov/anl/dm/esafsync/serviceconn/StorageServiceConnection.java b/tools/ExperimentSynchronizer/src/gov/anl/dm/esafsync/serviceconn/StorageServiceConnection.java
new file mode 100644
index 0000000000000000000000000000000000000000..915bae4f6f55d380ed59693aea6222c26eda6e56
--- /dev/null
+++ b/tools/ExperimentSynchronizer/src/gov/anl/dm/esafsync/serviceconn/StorageServiceConnection.java
@@ -0,0 +1,46 @@
+package gov.anl.dm.esafsync.serviceconn;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.swing.JOptionPane;
+
+public class StorageServiceConnection extends ServiceConnection{
+
+	class Keyword {
+		static final String EXPERIMENT_NAME = "name";
+		static final String EXPERIMENT_DESCRIPTION = "description";
+		static final String EXPERIMENT_TYPE_ID = "experimentTypeId";
+	}
+
+	public int addExperiment(String name, String description) {
+		Map<String, String> data = new HashMap<>();
+    	if (name == null) {
+    		JOptionPane.showMessageDialog(null, "The experiment name is null", "Error",JOptionPane.ERROR_MESSAGE);
+    		return ServiceConnection.ServiceConnectionStatus.ERROR;
+    	} else {
+    		data.put(Keyword.EXPERIMENT_NAME, encode(name));
+    	}
+    	if (description != null) {
+    		data.put(Keyword.EXPERIMENT_DESCRIPTION, encode(description));
+    	}
+		data.put(Keyword.EXPERIMENT_TYPE_ID, ESAF_EXPERIMENT_TYPE);
+		return invokeSessionPostRequest(StorageServUrl.EXPERIMENT, data);    	
+    }
+
+    public int addExperimentUser(String userName, String experimentName, String role) {
+		Map<String, String> data = new HashMap<>();
+		return invokeSessionPostRequest(StorageServUrl.EXPERIMENT_USER + "/" + userName + "/" + experimentName + "/" + role, data);
+    }
+
+    public int startExperiment(String name) {
+		Map<String, String> data = new HashMap<>();
+    	if (name == null) {
+    		JOptionPane.showMessageDialog(null, "The experiment name is null", "Error",JOptionPane.ERROR_MESSAGE);
+    		return ServiceConnection.ServiceConnectionStatus.ERROR;
+    	} else {
+    		data.put(Keyword.EXPERIMENT_NAME, encode(name));
+    	}
+		return invokeSessionPutRequest(StorageServUrl.START_EXPERIMENT, data);
+    }
+}