Merge pull request #109 from informatik-ag-ngl/f/invalid_login

Reacting to HandshakeRejectionEvents
This commit is contained in:
Kai S. K. Engelbart 2020-02-13 10:13:52 +01:00 committed by GitHub
commit 247ad73472
18 changed files with 295 additions and 205 deletions

View File

@ -28,7 +28,7 @@
<dependency> <dependency>
<groupId>com.github.informatik-ag-ngl</groupId> <groupId>com.github.informatik-ag-ngl</groupId>
<artifactId>envoy-common</artifactId> <artifactId>envoy-common</artifactId>
<version>develop-SNAPSHOT</version> <version>v0.2-alpha</version>
</dependency> </dependency>
</dependencies> </dependencies>

View File

@ -1,4 +1,4 @@
package envoy.client; package envoy.client.data;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
@ -6,7 +6,6 @@ import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.prefs.Preferences; import java.util.prefs.Preferences;
import envoy.client.data.Config;
import envoy.client.ui.Color; import envoy.client.ui.Color;
import envoy.client.ui.Theme; import envoy.client.ui.Theme;
import envoy.util.SerializationUtils; import envoy.util.SerializationUtils;

View File

@ -1,4 +1,4 @@
package envoy.client; package envoy.client.data;
import java.io.Serializable; import java.io.Serializable;
import java.util.HashMap; import java.util.HashMap;

View File

@ -0,0 +1,18 @@
package envoy.client.event;
import envoy.event.Event;
/**
* This {@link Event} indicates that a handshake was completed successfully.
*
* Project: <strong>envoy-client</strong><br>
* File: <strong>HandshakeSuccessfulEvent.java</strong><br>
* Created: <strong>8 Feb 2020</strong><br>
*
* @author Leon Hofmeister
* @since Envoy v0.3-alpha
*/
public class HandshakeSuccessfulEvent extends Event.Valueless {
private static final long serialVersionUID = -157972384126278855L;
}

View File

@ -42,6 +42,7 @@ public class Client implements Closeable {
// Asynchronously initialized during handshake // Asynchronously initialized during handshake
private volatile User sender; private volatile User sender;
private volatile Contacts contacts; private volatile Contacts contacts;
private volatile boolean rejected;
// Configuration and logging // Configuration and logging
private static final Config config = Config.getInstance(); private static final Config config = Config.getInstance();
@ -54,16 +55,19 @@ public class Client implements Closeable {
* an exception is thrown. * an exception is thrown.
* *
* @param credentials the login credentials of the user * @param credentials the login credentials of the user
* @param localDb the local database used to persist the current
* {@link IdGenerator}
* @param receivedMessageCache a message cache containing all unread messages * @param receivedMessageCache a message cache containing all unread messages
* from the server that can be relayed after * from the server that can be relayed after
* initialization * initialization
* @throws Exception if the online mode could not be entered or the request * @throws TimeLimitExceededException if the server could not be reached
* failed for some other reason * @throws IOException if the login credentials could not be
* @since Envoy v0.2-alpha * written
* @throws InterruptedException if the current thread is interrupted while
* waiting for the handshake response
*/ */
public void onlineInit(LoginCredentials credentials, LocalDb localDb, Cache<Message> receivedMessageCache) throws Exception { public void performHandshake(LoginCredentials credentials, Cache<Message> receivedMessageCache)
throws TimeLimitExceededException, IOException, InterruptedException {
if (online) throw new IllegalStateException("Handshake has already been performed successfully");
// Establish TCP connection // Establish TCP connection
logger.info(String.format("Attempting connection to server %s:%d...", config.getServer(), config.getPort())); logger.info(String.format("Attempting connection to server %s:%d...", config.getServer(), config.getPort()));
socket = new Socket(config.getServer(), config.getPort()); socket = new Socket(config.getServer(), config.getPort());
@ -76,6 +80,9 @@ public class Client implements Closeable {
receiver.registerProcessor(User.class, sender -> { logger.info("Acquired user object " + sender); this.sender = sender; }); receiver.registerProcessor(User.class, sender -> { logger.info("Acquired user object " + sender); this.sender = sender; });
receiver.registerProcessor(Contacts.class, contacts -> { logger.info("Acquired contacts object " + contacts); this.contacts = contacts; }); receiver.registerProcessor(Contacts.class, contacts -> { logger.info("Acquired contacts object " + contacts); this.contacts = contacts; });
receiver.registerProcessor(Message.class, receivedMessageCache); receiver.registerProcessor(Message.class, receivedMessageCache);
receiver.registerProcessor(HandshakeRejectionEvent.class, evt -> { rejected = true; EventBus.getInstance().dispatch(evt); });
rejected = false;
// Start receiver // Start receiver
new Thread(receiver).start(); new Thread(receiver).start();
@ -87,6 +94,15 @@ public class Client implements Closeable {
// Wait for a maximum of five seconds to acquire the sender object // Wait for a maximum of five seconds to acquire the sender object
long start = System.currentTimeMillis(); long start = System.currentTimeMillis();
while (sender == null || contacts == null) { while (sender == null || contacts == null) {
// Quit immediately after handshake rejection
// This method can then be called again
if (rejected) {
socket.close();
receiver.removeAllProcessors();
return;
}
if (System.currentTimeMillis() - start > 5000) throw new TimeLimitExceededException("Did not log in after 5 seconds"); if (System.currentTimeMillis() - start > 5000) throw new TimeLimitExceededException("Did not log in after 5 seconds");
Thread.sleep(500); Thread.sleep(500);
} }
@ -96,8 +112,23 @@ public class Client implements Closeable {
// Remove user creation processor // Remove user creation processor
receiver.removeAllProcessors(); receiver.removeAllProcessors();
}
// Register processors for message and status handling /**
* Initializes the {@link Receiver} used to process data sent from the server to
* this client.
*
* @param localDb the local database used to persist the current
* {@link IdGenerator}
* @param receivedMessageCache a message cache containing all unread messages
* from the server that can be relayed after
* initialization
* @throws IOException if no {@link IdGenerator} is present and none could be
* requested from the server
* @since Envoy v0.2-alpha
*/
public void initReceiver(LocalDb localDb, Cache<Message> receivedMessageCache) throws IOException {
checkOnline();
// Process incoming messages // Process incoming messages
final ReceivedMessageProcessor receivedMessageProcessor = new ReceivedMessageProcessor(); final ReceivedMessageProcessor receivedMessageProcessor = new ReceivedMessageProcessor();

View File

@ -51,7 +51,7 @@ public class Receiver implements Runnable {
try (ObjectInputStream oin = new ObjectInputStream(new ByteArrayInputStream(objBytes))) { try (ObjectInputStream oin = new ObjectInputStream(new ByteArrayInputStream(objBytes))) {
Object obj = oin.readObject(); Object obj = oin.readObject();
logger.finest("Received object " + obj); logger.info("Received object " + obj);
// Get appropriate processor // Get appropriate processor
@SuppressWarnings("rawtypes") @SuppressWarnings("rawtypes")

View File

@ -12,9 +12,9 @@ import javax.swing.border.EmptyBorder;
import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener; import javax.swing.event.DocumentListener;
import envoy.client.Settings;
import envoy.client.data.Chat; import envoy.client.data.Chat;
import envoy.client.data.LocalDb; import envoy.client.data.LocalDb;
import envoy.client.data.Settings;
import envoy.client.event.MessageCreationEvent; import envoy.client.event.MessageCreationEvent;
import envoy.client.event.ThemeChangeEvent; import envoy.client.event.ThemeChangeEvent;
import envoy.client.net.Client; import envoy.client.net.Client;

View File

@ -6,7 +6,7 @@ import java.awt.Font;
import javax.swing.*; import javax.swing.*;
import envoy.client.Settings; import envoy.client.data.Settings;
import envoy.client.event.SendEvent; import envoy.client.event.SendEvent;
import envoy.client.ui.list.ComponentList; import envoy.client.ui.list.ComponentList;
import envoy.client.ui.list.ComponentListCellRenderer; import envoy.client.ui.list.ComponentListCellRenderer;

View File

@ -2,40 +2,52 @@ package envoy.client.ui;
import java.awt.*; import java.awt.*;
import java.awt.event.ItemEvent; import java.awt.event.ItemEvent;
import java.awt.event.ItemListener; import java.io.IOException;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.util.Arrays; import java.util.Arrays;
import java.util.logging.Logger;
import javax.naming.TimeLimitExceededException;
import javax.swing.*; import javax.swing.*;
import javax.swing.border.EmptyBorder; import javax.swing.border.EmptyBorder;
import envoy.client.Settings; import envoy.client.data.*;
import envoy.client.event.HandshakeSuccessfulEvent;
import envoy.client.net.Client;
import envoy.client.util.EnvoyLog;
import envoy.data.LoginCredentials; import envoy.data.LoginCredentials;
import envoy.data.Message;
import envoy.data.User;
import envoy.event.EventBus;
import envoy.event.HandshakeRejectionEvent;
import envoy.exception.EnvoyException;
/** /**
* Project: <strong>envoy-client</strong><br> * Project: <strong>envoy-client</strong><br>
* File: <strong>LoginDialog.java</strong><br> * File: <strong>LoginDialog.java</strong><br>
* Created: <strong>01.01.2020</strong><br> * Created: <strong>01.01.2020</strong><br>
* *
* @author Kai S. K. Engelbart * @author Kai S. K. Engelbart
* @author Maximilian K&auml;fer * @author Maximilian K&auml;fer
* @since Envoy v0.3-alpha * @since Envoy v0.3-alpha
*/ */
public class LoginDialog extends JDialog { public class LoginDialog extends JDialog {
private final JPanel contentPanel; private JPanel contentPanel;
private JTextField textField; private JTextField textField;
private JPasswordField passwordField; private JPasswordField passwordField;
private JPasswordField repeatPasswordField; private JPasswordField repeatPasswordField;
private JLabel lblUserName; private JLabel lblUserName;
private JLabel lblPassword; private JLabel lblPassword;
private JLabel lblRepeatPassword; private JLabel lblRepeatPassword;
private JLabel errorMessage;
private GridBagConstraints gbc_lblRepeatPassword; private GridBagConstraints gbc_lblRepeatPassword;
private GridBagConstraints gbc_repeatPasswordField; private GridBagConstraints gbc_repeatPasswordField;
private GridBagConstraints gbc_errorMessage;
private JPanel buttonPane; private JPanel buttonPane;
private JTextPane registerText; private JTextPane registerText;
private JCheckBox registerCheckBox; private JCheckBox registerCheckBox;
private PrimaryButton okButton; private PrimaryButton okButton;
@ -43,15 +55,101 @@ public class LoginDialog extends JDialog {
private LoginCredentials credentials; private LoginCredentials credentials;
private static final long serialVersionUID = 352021600833907468L; private final Client client;
private final LocalDb localDb;
private final Cache<Message> receivedMessageCache;
private static final Config config = Config.getInstance();
private static final Logger logger = EnvoyLog.getLogger(LoginDialog.class.getSimpleName());
private static final long serialVersionUID = 352021600833907468L;
/** /**
* Displays a dialog enabling the user to enter their user name and password. * Displays a dialog enabling the user to enter their user name and password.
* *
* @param client the client used to perform the handshake
* @param localDb the local database in which data is persisted
* @param receivedMessageCache the cache that stored messages received during
* the handshake
* @since Envoy v0.3-alpha * @since Envoy v0.3-alpha
*/ */
public LoginDialog() { public LoginDialog(Client client, LocalDb localDb, Cache<Message> receivedMessageCache) {
this.client = client;
this.localDb = localDb;
this.receivedMessageCache = receivedMessageCache;
// Prepare handshake
localDb.loadIdGenerator();
initUi();
okButton.addActionListener((evt) -> {
try {
if (registerCheckBox.isSelected()) {
// Check password equality
if (Arrays.equals(passwordField.getPassword(), repeatPasswordField.getPassword()))
credentials = new LoginCredentials(textField.getText(), passwordField.getPassword(), true);
else {
JOptionPane.showMessageDialog(this, "The repeated password is not the origional password!");
clearPasswordFields();
}
} else credentials = new LoginCredentials(textField.getText(), passwordField.getPassword(), false);
performHandshake();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
});
// Listen to handshake rejections
EventBus.getInstance()
.register(HandshakeRejectionEvent.class,
evt -> { clearPasswordFields(); errorMessage.setVisible(true); errorMessage.setText(evt.get()); });
// Exit the application when the dialog is cancelled
cancelButton.addActionListener(evt -> { logger.info("The login process has been cancelled. Exiting..."); System.exit(0); });
// Log in directly if configured
if (config.hasLoginCredentials()) {
credentials = config.getLoginCredentials();
performHandshake();
return;
}
setVisible(true);
}
private void performHandshake() {
try {
client.performHandshake(credentials, receivedMessageCache);
if (client.isOnline()) {
client.initReceiver(localDb, receivedMessageCache);
dispose();
}
} catch (IOException | InterruptedException | TimeLimitExceededException e) {
logger.warning("Could not connect to server. Trying offline mode...");
e.printStackTrace();
try {
// Try entering offline mode
localDb.loadUsers();
User clientUser = localDb.getUsers().get(credentials.getIdentifier());
if (clientUser == null) throw new EnvoyException("Could not enter offline mode: user name unknown");
client.setSender(clientUser);
JOptionPane.showMessageDialog(null,
"A connection to the server could not be established. Starting in offline mode.\n" + e,
"Connection error",
JOptionPane.WARNING_MESSAGE);
dispose();
} catch (Exception e1) {
JOptionPane.showMessageDialog(null, e1, "Client error", JOptionPane.ERROR_MESSAGE);
System.exit(1);
return;
}
}
}
private void initUi() {
setSize(338, 123); setSize(338, 123);
setLocationRelativeTo(null); setLocationRelativeTo(null);
setResizable(false);
getContentPane().setLayout(new BorderLayout()); getContentPane().setLayout(new BorderLayout());
contentPanel = new JPanel(); contentPanel = new JPanel();
contentPanel.setBorder(new EmptyBorder(5, 5, 5, 5)); contentPanel.setBorder(new EmptyBorder(5, 5, 5, 5));
@ -62,137 +160,121 @@ public class LoginDialog extends JDialog {
gbl_contentPanel.columnWeights = new double[] { 0.0, 1.0, Double.MIN_VALUE }; gbl_contentPanel.columnWeights = new double[] { 0.0, 1.0, Double.MIN_VALUE };
gbl_contentPanel.rowWeights = new double[] { 0.0, 0.0, Double.MIN_VALUE }; gbl_contentPanel.rowWeights = new double[] { 0.0, 0.0, Double.MIN_VALUE };
contentPanel.setLayout(gbl_contentPanel); contentPanel.setLayout(gbl_contentPanel);
{
lblUserName = new JLabel("Username:");
GridBagConstraints gbc_lblUserName = new GridBagConstraints();
gbc_lblUserName.anchor = GridBagConstraints.EAST;
gbc_lblUserName.insets = new Insets(0, 0, 5, 5);
gbc_lblUserName.gridx = 0;
gbc_lblUserName.gridy = 0;
contentPanel.add(lblUserName, gbc_lblUserName);
}
{
textField = new JTextField();
textField.setBorder(null);
GridBagConstraints gbc_textField = new GridBagConstraints();
gbc_textField.insets = new Insets(0, 0, 5, 0);
gbc_textField.fill = GridBagConstraints.HORIZONTAL;
gbc_textField.gridx = 1;
gbc_textField.gridy = 0;
contentPanel.add(textField, gbc_textField);
textField.setColumns(10);
}
{
lblPassword = new JLabel("Password:");
GridBagConstraints gbc_lblPassword = new GridBagConstraints();
gbc_lblPassword.anchor = GridBagConstraints.EAST;
gbc_lblPassword.insets = new Insets(0, 0, 0, 5);
gbc_lblPassword.gridx = 0;
gbc_lblPassword.gridy = 1;
contentPanel.add(lblPassword, gbc_lblPassword);
}
{
passwordField = new JPasswordField();
passwordField.setBorder(null);
GridBagConstraints gbc_passwordField = new GridBagConstraints();
gbc_passwordField.fill = GridBagConstraints.HORIZONTAL;
gbc_passwordField.gridx = 1;
gbc_passwordField.gridy = 1;
contentPanel.add(passwordField, gbc_passwordField);
}
{
lblRepeatPassword = new JLabel("Repeat Password:");
gbc_lblRepeatPassword = new GridBagConstraints();
gbc_lblRepeatPassword.anchor = GridBagConstraints.EAST;
gbc_lblRepeatPassword.insets = new Insets(0, 0, 0, 5);
gbc_lblRepeatPassword.gridx = 0;
gbc_lblRepeatPassword.gridy = 2;
}
{
repeatPasswordField = new JPasswordField();
repeatPasswordField.setBorder(null);
gbc_repeatPasswordField = new GridBagConstraints();
gbc_repeatPasswordField.fill = GridBagConstraints.HORIZONTAL;
gbc_repeatPasswordField.gridx = 1;
gbc_repeatPasswordField.gridy = 2;
}
{
buttonPane = new JPanel();
registerText = new JTextPane(); lblUserName = new JLabel("Username:");
registerText.setEditable(false); GridBagConstraints gbc_lblUserName = new GridBagConstraints();
registerText.setText("Register?"); gbc_lblUserName.anchor = GridBagConstraints.EAST;
registerText.setFont(new Font("Arial", Font.BOLD, 12)); gbc_lblUserName.insets = new Insets(0, 0, 5, 5);
registerText.setAlignmentX(LEFT_ALIGNMENT); gbc_lblUserName.gridx = 0;
buttonPane.add(registerText); gbc_lblUserName.gridy = 0;
contentPanel.add(lblUserName, gbc_lblUserName);
registerCheckBox = new JCheckBox(); textField = new JTextField();
registerCheckBox.setAlignmentX(LEFT_ALIGNMENT); textField.setBorder(null);
registerCheckBox.addItemListener(new ItemListener() { GridBagConstraints gbc_textField = new GridBagConstraints();
gbc_textField.insets = new Insets(0, 0, 5, 0);
gbc_textField.fill = GridBagConstraints.HORIZONTAL;
gbc_textField.gridx = 1;
gbc_textField.gridy = 0;
contentPanel.add(textField, gbc_textField);
textField.setColumns(10);
@Override lblPassword = new JLabel("Password:");
public void itemStateChanged(ItemEvent e) { GridBagConstraints gbc_lblPassword = new GridBagConstraints();
switch (e.getStateChange()) { gbc_lblPassword.anchor = GridBagConstraints.EAST;
case ItemEvent.SELECTED: gbc_lblPassword.insets = new Insets(0, 0, 0, 5);
contentPanel.add(lblRepeatPassword, gbc_lblRepeatPassword); gbc_lblPassword.gridx = 0;
contentPanel.add(repeatPasswordField, gbc_repeatPasswordField); gbc_lblPassword.gridy = 1;
setSize(338, 148); contentPanel.add(lblPassword, gbc_lblPassword);
contentPanel.revalidate();
contentPanel.repaint();
break;
case ItemEvent.DESELECTED: passwordField = new JPasswordField();
if (repeatPasswordField.getParent() == contentPanel) { passwordField.setBorder(null);
contentPanel.remove(lblRepeatPassword); GridBagConstraints gbc_passwordField = new GridBagConstraints();
contentPanel.remove(repeatPasswordField); gbc_passwordField.fill = GridBagConstraints.HORIZONTAL;
setSize(338, 123); gbc_passwordField.gridx = 1;
contentPanel.revalidate(); gbc_passwordField.gridy = 1;
contentPanel.repaint(); contentPanel.add(passwordField, gbc_passwordField);
}
break; lblRepeatPassword = new JLabel("Repeat Password:");
gbc_lblRepeatPassword = new GridBagConstraints();
gbc_lblRepeatPassword.anchor = GridBagConstraints.EAST;
gbc_lblRepeatPassword.insets = new Insets(0, 0, 0, 5);
gbc_lblRepeatPassword.gridx = 0;
gbc_lblRepeatPassword.gridy = 2;
repeatPasswordField = new JPasswordField();
gbc_repeatPasswordField = new GridBagConstraints();
gbc_repeatPasswordField.fill = GridBagConstraints.HORIZONTAL;
gbc_repeatPasswordField.gridx = 1;
gbc_repeatPasswordField.gridy = 2;
errorMessage = new JLabel();
gbc_errorMessage = new GridBagConstraints();
gbc_errorMessage.gridx = 1;
gbc_errorMessage.gridy = 3;
gbc_errorMessage.fill = GridBagConstraints.HORIZONTAL;
gbc_errorMessage.insets = new Insets(5, 5, 5, 5);
errorMessage.setForeground(Color.RED);
errorMessage.setVisible(false);
contentPanel.add(errorMessage, gbc_errorMessage);
buttonPane = new JPanel();
registerText = new JTextPane();
registerText.setEditable(false);
registerText.setText("Register?");
registerText.setFont(new Font("Arial", Font.BOLD, 12));
registerText.setAlignmentX(LEFT_ALIGNMENT);
buttonPane.add(registerText);
registerCheckBox = new JCheckBox();
registerCheckBox.setAlignmentX(LEFT_ALIGNMENT);
registerCheckBox.addItemListener(e -> {
switch (e.getStateChange()) {
case ItemEvent.SELECTED:
contentPanel.add(lblRepeatPassword, gbc_lblRepeatPassword);
contentPanel.add(repeatPasswordField, gbc_repeatPasswordField);
setSize(338, 173);
break;
case ItemEvent.DESELECTED:
if (repeatPasswordField.getParent() == contentPanel) {
contentPanel.remove(lblRepeatPassword);
contentPanel.remove(repeatPasswordField);
setSize(338, 148);
} }
} break;
}); }
buttonPane.add(registerCheckBox); contentPanel.revalidate();
contentPanel.repaint();
});
buttonPane.add(registerCheckBox);
buttonPane.setLayout(new FlowLayout(FlowLayout.RIGHT)); buttonPane.setLayout(new FlowLayout(FlowLayout.RIGHT));
getContentPane().add(buttonPane, BorderLayout.SOUTH); getContentPane().add(buttonPane, BorderLayout.SOUTH);
{ okButton = new PrimaryButton("OK");
okButton = new PrimaryButton("OK"); okButton.setActionCommand("OK");
okButton.addActionListener((evt) -> { buttonPane.add(okButton);
try { getRootPane().setDefaultButton(okButton);
if (registerCheckBox.isSelected()) {
if (Arrays.equals(passwordField.getPassword(), repeatPasswordField.getPassword())) { cancelButton = new PrimaryButton("Cancel");
credentials = new LoginCredentials(textField.getText(), passwordField.getPassword(), true); cancelButton.setActionCommand("Cancel");
dispose(); buttonPane.add(cancelButton);
} else {
JOptionPane.showMessageDialog(this, "The repeated password is unequal to the origional password!");
passwordField.setText(null);
repeatPasswordField.setText(null);
}
} else {
credentials = new LoginCredentials(textField.getText(), passwordField.getPassword(), false);
dispose();
}
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
});
okButton.setActionCommand("OK");
buttonPane.add(okButton);
getRootPane().setDefaultButton(okButton);
}
{
cancelButton = new PrimaryButton("Cancel");
cancelButton.addActionListener((evt) -> dispose());
cancelButton.setActionCommand("Cancel");
buttonPane.add(cancelButton);
}
}
setTheme(); setTheme();
setModal(true); setModalityType(Dialog.DEFAULT_MODALITY_TYPE);
setVisible(true);
EventBus.getInstance().register(HandshakeSuccessfulEvent.class, evt -> dispose());
}
/**
* Resets the text stored in the password fields.
*
* @since Envoy v0.3-alpha
*/
private void clearPasswordFields() {
passwordField.setText(null);
repeatPasswordField.setText(null);
} }
private void setTheme() { private void setTheme() {
@ -238,11 +320,4 @@ public class LoginDialog extends JDialog {
cancelButton.setBackground(theme.getInteractableBackgroundColor()); cancelButton.setBackground(theme.getInteractableBackgroundColor());
cancelButton.setForeground(theme.getInteractableForegroundColor()); cancelButton.setForeground(theme.getInteractableForegroundColor());
} }
/**
* @return the {@link LoginCredentials} entered by the user, or {@code null} if
* the dialog has been cancelled
* @since Envoy v0.3-alpha
*/
public LoginCredentials getCredentials() { return credentials; }
} }

View File

@ -5,7 +5,7 @@ import java.text.SimpleDateFormat;
import javax.swing.*; import javax.swing.*;
import envoy.client.Settings; import envoy.client.data.Settings;
import envoy.client.ui.list.ComponentList; import envoy.client.ui.list.ComponentList;
import envoy.client.ui.list.ComponentListCellRenderer; import envoy.client.ui.list.ComponentListCellRenderer;
import envoy.data.Message; import envoy.data.Message;

View File

@ -12,7 +12,7 @@ import javax.swing.JComponent;
import javax.swing.JScrollBar; import javax.swing.JScrollBar;
import javax.swing.plaf.basic.BasicScrollBarUI; import javax.swing.plaf.basic.BasicScrollBarUI;
import envoy.client.Settings; import envoy.client.data.Settings;
/** /**
* Project: <strong>envoy-client</strong><br> * Project: <strong>envoy-client</strong><br>

View File

@ -5,8 +5,8 @@ import java.awt.Graphics;
import javax.swing.JButton; import javax.swing.JButton;
import envoy.client.Settings; import envoy.client.data.Settings;
import envoy.client.SettingsItem; import envoy.client.data.SettingsItem;
/** /**
* This component can be used to toggle between two options. This will change * This component can be used to toggle between two options. This will change

View File

@ -11,14 +11,11 @@ import javax.swing.JFrame;
import javax.swing.JOptionPane; import javax.swing.JOptionPane;
import javax.swing.SwingUtilities; import javax.swing.SwingUtilities;
import envoy.client.Settings;
import envoy.client.data.*; import envoy.client.data.*;
import envoy.client.net.Client; import envoy.client.net.Client;
import envoy.client.net.WriteProxy; import envoy.client.net.WriteProxy;
import envoy.client.util.EnvoyLog; import envoy.client.util.EnvoyLog;
import envoy.data.LoginCredentials;
import envoy.data.Message; import envoy.data.Message;
import envoy.data.User;
import envoy.exception.EnvoyException; import envoy.exception.EnvoyException;
/** /**
@ -63,7 +60,7 @@ public class Startup {
if (args.length > 0) config.load(args); if (args.length > 0) config.load(args);
// Check if all mandatory configuration values have been initialized // Check if all mandatory configuration values have been initialized
if (!config.isInitialized()) throw new EnvoyException("Server or port are not defined"); if (!config.isInitialized()) throw new EnvoyException("Configuration is not fully initialized");
} catch (Exception e) { } catch (Exception e) {
JOptionPane JOptionPane
.showMessageDialog(null, "Error loading configuration values:\n" + e.toString(), "Configuration error", JOptionPane.ERROR_MESSAGE); .showMessageDialog(null, "Error loading configuration values:\n" + e.toString(), "Configuration error", JOptionPane.ERROR_MESSAGE);
@ -75,14 +72,6 @@ public class Startup {
EnvoyLog.setFileLevelBarrier(config.getFileLevelBarrier()); EnvoyLog.setFileLevelBarrier(config.getFileLevelBarrier());
EnvoyLog.setConsoleLevelBarrier(config.getConsoleLevelBarrier()); EnvoyLog.setConsoleLevelBarrier(config.getConsoleLevelBarrier());
// Acquire login credentials
LoginCredentials credentials = config.hasLoginCredentials() ? config.getLoginCredentials() : new LoginDialog().getCredentials();
if (credentials == null) {
logger.info("The login process has been aborted by the user. Exiting...");
System.exit(0);
}
// Initialize the local database // Initialize the local database
LocalDb localDb; LocalDb localDb;
if (config.isIgnoreLocalDB()) { if (config.isIgnoreLocalDB()) {
@ -96,40 +85,18 @@ public class Startup {
} catch (IOException e3) { } catch (IOException e3) {
logger.log(Level.SEVERE, "Could not initialize local database", e3); logger.log(Level.SEVERE, "Could not initialize local database", e3);
JOptionPane JOptionPane
.showMessageDialog(null, "Could not initialize local database!\n" + e3.toString(), "Local database error", JOptionPane.ERROR_MESSAGE); .showMessageDialog(null, "Could not initialize local database!\n" + e3, "Local database error", JOptionPane.ERROR_MESSAGE);
System.exit(1); System.exit(1);
return; return;
} }
SwingUtilities.invokeLater(() -> chatWindow.setVisible(true)); // Initialize client and unread message cache
// Acquire the client user (with ID) either from the server or from the local
// database, which triggers offline mode
Client client = new Client(); Client client = new Client();
Cache<Message> cache = new Cache<>(); Cache<Message> cache = new Cache<>();
try {
// Try entering online mode first // Try to connect to the server
localDb.loadIdGenerator(); new LoginDialog(client, localDb, cache);
client.onlineInit(credentials, localDb, cache); SwingUtilities.invokeLater(() -> chatWindow.setVisible(true));
} catch (Exception e1) {
logger.warning("Could not connect to server. Trying offline mode...");
e1.printStackTrace();
try {
// Try entering offline mode
localDb.loadUsers();
User clientUser = localDb.getUsers().get(credentials.getName());
if (clientUser == null) throw new EnvoyException("Could not enter offline mode: user name unknown");
client.setSender(clientUser);
JOptionPane.showMessageDialog(null,
"A connection to the server could not be established. Starting in offline mode.\n" + e1,
"Connection error",
JOptionPane.WARNING_MESSAGE);
} catch (Exception e2) {
JOptionPane.showMessageDialog(null, e2.toString(), "Client error", JOptionPane.ERROR_MESSAGE);
System.exit(1);
return;
}
}
// Set client user in local database // Set client user in local database
localDb.setUser(client.getSender()); localDb.setUser(client.getSender());

View File

@ -7,7 +7,7 @@ import javax.swing.JLabel;
import javax.swing.JList; import javax.swing.JList;
import javax.swing.ListCellRenderer; import javax.swing.ListCellRenderer;
import envoy.client.Settings; import envoy.client.data.Settings;
import envoy.data.User; import envoy.data.User;
import envoy.data.User.UserStatus; import envoy.data.User.UserStatus;

View File

@ -10,8 +10,8 @@ import java.util.logging.Logger;
import javax.swing.JComponent; import javax.swing.JComponent;
import javax.swing.JTextPane; import javax.swing.JTextPane;
import envoy.client.Settings; import envoy.client.data.Settings;
import envoy.client.SettingsItem; import envoy.client.data.SettingsItem;
import envoy.client.ui.Theme; import envoy.client.ui.Theme;
import envoy.client.util.EnvoyLog; import envoy.client.util.EnvoyLog;

View File

@ -7,7 +7,7 @@ import javax.swing.JDialog;
import javax.swing.JPanel; import javax.swing.JPanel;
import javax.swing.JTextPane; import javax.swing.JTextPane;
import envoy.client.Settings; import envoy.client.data.Settings;
import envoy.client.ui.PrimaryButton; import envoy.client.ui.PrimaryButton;
import envoy.client.ui.PrimaryTextArea; import envoy.client.ui.PrimaryTextArea;
import envoy.client.ui.Theme; import envoy.client.ui.Theme;

View File

@ -9,7 +9,7 @@ import java.util.logging.Logger;
import javax.swing.*; import javax.swing.*;
import envoy.client.Settings; import envoy.client.data.Settings;
import envoy.client.event.ThemeChangeEvent; import envoy.client.event.ThemeChangeEvent;
import envoy.client.ui.PrimaryButton; import envoy.client.ui.PrimaryButton;
import envoy.client.ui.Theme; import envoy.client.ui.Theme;

View File

@ -9,7 +9,7 @@ import java.util.logging.Logger;
import javax.swing.*; import javax.swing.*;
import envoy.client.Settings; import envoy.client.data.Settings;
import envoy.client.event.ThemeChangeEvent; import envoy.client.event.ThemeChangeEvent;
import envoy.client.ui.Color; import envoy.client.ui.Color;
import envoy.client.ui.Theme; import envoy.client.ui.Theme;