diff --git a/react-social/src/user/login/Login.js b/react-social/src/user/login/Login.js index 11ccd17..d4d88f4 100644 --- a/react-social/src/user/login/Login.js +++ b/react-social/src/user/login/Login.js @@ -10,6 +10,8 @@ import Alert from 'react-s-alert'; class Login extends Component { componentDidMount() { + // If the OAuth2 login encounters an error, the user is redirected to the /login page with an error. + // Here we display the error and then remove the error query parameter from the location. if(this.props.location.state && this.props.location.state.error) { setTimeout(() => { Alert.error(this.props.location.state.error, { @@ -121,4 +123,4 @@ class LoginForm extends Component { } } -export default Login \ No newline at end of file +export default Login diff --git a/spring-social/src/main/java/com/example/springsocial/controller/SequenceGenerator.java b/spring-social/src/main/java/com/example/springsocial/controller/SequenceGenerator.java new file mode 100644 index 0000000..2f48cb3 --- /dev/null +++ b/spring-social/src/main/java/com/example/springsocial/controller/SequenceGenerator.java @@ -0,0 +1,110 @@ +package com.example.springsocial.controller; + + +import java.net.NetworkInterface; +import java.security.SecureRandom; +import java.time.Instant; +import java.util.Enumeration; + +/** + * Distributed Sequence Generator. + * Inspired by Twitter snowflake: https://github.com/twitter/snowflake/tree/snowflake-2010 + * + * This class should be used as a Singleton. + * Make sure that you create and reuse a Single instance of SequenceGenerator per machine in your distributed system cluster. + */ +public class SequenceGenerator { + private static final int TOTAL_BITS = 64; + private static final int EPOCH_BITS = 42; + private static final int MACHINE_ID_BITS = 10; + private static final int SEQUENCE_BITS = 12; + + private static final int maxMachineId = (int)(Math.pow(2, MACHINE_ID_BITS) - 1); + private static final int maxSequence = (int)(Math.pow(2, SEQUENCE_BITS) - 1); + + // Custom Epoch (January 1, 2015 Midnight UTC = 2015-01-01T00:00:00Z) + private static final long CUSTOM_EPOCH = 1420070400000L; + + private final int machineId; + + private long lastTimestamp = -1L; + private long sequence = 0L; + + // Create Snowflake with a machineId + public SequenceGenerator(int machineId) { + if(machineId < 0 || machineId > maxMachineId) { + throw new IllegalArgumentException(String.format("MachineId must be between %d and %d", 0, maxMachineId)); + } + this.machineId = machineId; + } + + // Let Snowflake generate a machineId + public SequenceGenerator() { + this.machineId = createMachineId(); + } + + + public long nextId() { + long currentTimestamp = timestamp(); + + synchronized (this) { + if(currentTimestamp < lastTimestamp) { + throw new IllegalStateException("Invalid System Clock!"); + } + + if (currentTimestamp == lastTimestamp) { + sequence = (sequence + 1) & maxSequence; + if(sequence == 0) { + // Sequence Exhausted, wait till next millisecond. + currentTimestamp = waitNextMillis(currentTimestamp); + } + } else { + // reset sequence for next millisecond + sequence = 0; + } + + lastTimestamp = currentTimestamp; + } + + long id = currentTimestamp << (TOTAL_BITS - EPOCH_BITS); + id |= (machineId << (TOTAL_BITS - EPOCH_BITS - MACHINE_ID_BITS)); + id |= sequence; + return id; + } + + + // Get current timestamp in milliseconds, adjust for the custom epoch. + private static long timestamp() { + return Instant.now().toEpochMilli() - CUSTOM_EPOCH; + } + + // Block and wait till next millisecond + private long waitNextMillis(long currentTimestamp) { + while (currentTimestamp == lastTimestamp) { + currentTimestamp = timestamp(); + } + return currentTimestamp; + } + + private int createMachineId() { + int machineId; + try { + StringBuilder sb = new StringBuilder(); + Enumeration networkInterfaces = NetworkInterface.getNetworkInterfaces(); + while (networkInterfaces.hasMoreElements()) { + NetworkInterface networkInterface = networkInterfaces.nextElement(); + byte[] mac = networkInterface.getHardwareAddress(); + if (mac != null) { + for(int i = 0; i < mac.length; i++) { + sb.append(String.format("%02X", mac[i])); + } + } + } + machineId = sb.toString().hashCode(); + } catch (Exception ex) { + machineId = (new SecureRandom().nextInt()); + } + machineId = machineId & maxMachineId; + return machineId; + } +} \ No newline at end of file