com.erudika.para.utils.Utils Maven / Gradle / Ivy
/*
* Copyright 2013-2017 Erudika. https://erudika.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* For issues and patches go to: https://github.com/erudika
*/
package com.erudika.para.utils;
import com.erudika.para.annotations.Email;
import com.erudika.para.core.ParaObject;
import com.github.rjeschke.txtmark.Configuration;
import com.github.rjeschke.txtmark.Processor;
import com.samskivert.mustache.Mustache;
import java.io.IOException;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.text.DateFormatSymbols;
import java.text.MessageFormat;
import java.text.NumberFormat;
import java.util.Calendar;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Random;
import java.util.regex.Pattern;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.LocaleUtils;
import org.apache.commons.lang3.StringEscapeUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.jsoup.Jsoup;
import org.mindrot.jbcrypt.BCrypt;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Miscellaneous Para utilities.
* @author Alex Bogdanovski [[email protected]]
*/
@SuppressWarnings("unchecked")
public final class Utils {
private static final Logger logger = LoggerFactory.getLogger(Utils.class);
// maps lowercase simple names to class objects
private static final Pattern EMAIL_PATTERN = Pattern.compile(Email.EMAIL_PATTERN);
private static final NumberFormat NUMBER_FORMAT = NumberFormat.getInstance();
private static HumanTime humantime;
private static Utils instance;
////////// ID GEN VARS //////////////
private static final long TIMER_OFFSET = 1310084584692L; // ~July 2011
private static final long WORKER_ID_BITS = 5L;
private static final long DATACENTER_ID_BITS = 5L;
private static final long MAX_WORKER_ID = -1L ^ (-1L << WORKER_ID_BITS);
private static final long MAX_DATACENTER_ID = -1L ^ (-1L << DATACENTER_ID_BITS);
private static final long SEQUENCE_BITS = 12L;
private static final long WORKER_ID_SHIFT = SEQUENCE_BITS;
private static final long DATACENTER_ID_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS;
private static final long TIMESTAMP_LEFT_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS + DATACENTER_ID_BITS;
private static final long SEQUENCE_MASK = -1L ^ (-1L << SEQUENCE_BITS);
private static long lastTimestamp = -1L;
private static long dataCenterId = 0L; // only one datacenter atm
private static long workerId; // max 1024
private static long sequence = 0L;
static {
initIdGenerator();
NUMBER_FORMAT.setMinimumFractionDigits(2);
NUMBER_FORMAT.setMaximumFractionDigits(2);
}
private Utils() { }
/**
* Returns an instance of this class.
* @return an instance
*/
public static Utils getInstance() {
if (instance == null) {
instance = new Utils();
}
return instance;
}
/**
* HumanTime - a relative time formatter.
* @return humantime instance
*/
public static HumanTime getHumanTime() {
if (humantime == null) {
humantime = new HumanTime();
}
return humantime;
}
/////////////////////////////////////////////
// INIT FUNCTIONS
/////////////////////////////////////////////
private static void initIdGenerator() {
String workerID = Config.WORKER_ID;
workerId = NumberUtils.toLong(workerID, 1);
if (workerId > MAX_WORKER_ID || workerId < 0) {
workerId = new Random().nextInt((int) MAX_WORKER_ID + 1);
}
if (dataCenterId > MAX_DATACENTER_ID || dataCenterId < 0) {
dataCenterId = new Random().nextInt((int) MAX_DATACENTER_ID + 1);
}
}
/////////////////////////////////////////////
// HASH UTILS
/////////////////////////////////////////////
/**
* md5 hash function.
* @param s the string to be hashed
* @return an md5 hash
*/
public static String md5(String s) {
if (s == null) {
return "";
}
try {
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(s.getBytes());
byte[] byteData = md.digest();
//convert the byte to hex format method 1
StringBuilder sb = new StringBuilder();
for (int i = 0; i < byteData.length; i++) {
sb.append(Integer.toString((byteData[i] & 0xff) + 0x100, 16).substring(1));
}
return sb.toString();
} catch (NoSuchAlgorithmException ex) {
return "";
}
}
/**
* bcrypt hash function implemented by Spring Security.
*
* @param s the string to be hashed
* @return the hash
*/
public static String bcrypt(String s) {
return (s == null) ? s : BCrypt.hashpw(s, BCrypt.gensalt(12));
}
/**
* Checks if a hash matches a string.
*
* @param plain plain text string
* @param storedHash hashed string
* @return true if the hash matches
*/
public static boolean bcryptMatches(String plain, String storedHash) {
if (StringUtils.isBlank(plain) || StringUtils.isBlank(storedHash)) {
return false;
}
try {
return BCrypt.checkpw(plain, storedHash);
} catch (Exception e) {
return false;
}
}
/**
* Generates an authentication token - a random string encoded in Base64.
* @param length the length of the generated token
* @param urlSafe switches to a URL safe encoding
* @return a random string
*/
public static String generateSecurityToken(int length, boolean urlSafe) {
final byte[] bytes = new byte[length];
SecureRandom rand;
try {
rand = SecureRandom.getInstance("SHA1PRNG");
} catch (NoSuchAlgorithmException ex) {
logger.error(null, ex);
rand = new SecureRandom();
}
rand.nextBytes(bytes);
return urlSafe ? base64encURL(bytes) : base64enc(bytes);
}
/**
* Generates an authentication token - a random string encoded in Base64.
* @param length the length of the generated token
* @return a random string
*/
public static String generateSecurityToken(int length) {
return generateSecurityToken(length, false);
}
/**
* Generates an authentication token - a random 32 byte string encoded in Base64.
* @return a random string
*/
public static String generateSecurityToken() {
return generateSecurityToken(32);
}
/////////////////////////////////////////////
// STRING UTILS
/////////////////////////////////////////////
/**
* Escapes JavaScript.
* @param str a javascript string
* @return the escaped javascript string
*/
public static String escapeJavascript(String str) {
return (str == null) ? "" : StringEscapeUtils.escapeEcmaScript(str);
}
/**
* Strips all HTML tags from a string.
* @param html HTML string
* @return just the text
*/
public static String stripHtml(String html) {
return (html == null) ? "" : Jsoup.parse(html).text();
}
/**
* Converts Markdown to HTML.
* @param markdownString Markdown
* @return HTML
*/
public static String markdownToHtml(String markdownString) {
return StringUtils.isBlank(markdownString) ? "" :
Processor.process(markdownString, Configuration.DEFAULT_SAFE);
}
/**
* Compiles a mustache template with a given scope (map of fields and values).
* @param context a map of fields and values
* @param template a Mustache template
* @return the compiled template string
*/
public static String compileMustache(Map context, String template) {
if (context == null || StringUtils.isBlank(template)) {
return "";
}
Writer writer = new StringWriter();
try {
Mustache.compiler().escapeHTML(false).emptyStringIsFalse(true).compile(template).execute(context, writer);
} finally {
try {
writer.close();
} catch (IOException e) {
logger.error(null, e);
}
}
return writer.toString();
}
/**
* Abbreviates a string.
* @param str a string
* @param max max length
* @return a substring of that string
*/
public static String abbreviate(String str, int max) {
return StringUtils.isBlank(str) ? "" : StringUtils.abbreviate(str, max);
}
/**
* Joins a list of strings to String using a separator.
* @param arr a list of strings
* @param separator a separator string
* @return a string
*/
public static String arrayJoin(List arr, String separator) {
return (arr == null || separator == null) ? "" : StringUtils.join(arr, separator);
}
/**
* Strips all symbols, punctuation, whitespace and control chars from a string.
* @param str a dirty string
* @return a clean string
*/
public static String stripAndTrim(String str) {
return stripAndTrim(str, "");
}
/**
* Strips all symbols, punctuation, whitespace and control chars from a string.
* @param str a dirty string
* @param replaceWith a string to replace spaces with
* @return a clean string
*/
public static String stripAndTrim(String str, String replaceWith) {
return StringUtils.isBlank(str) ? "" :
str.replaceAll("[\\p{S}\\p{P}\\p{C}]", replaceWith).replaceAll("\\p{Z}+", " ").trim();
}
/**
* Converts spaces to dashes.
* @param str a string with spaces
* @param replaceWith a string to replace spaces with
* @return a string with dashes
*/
public static String noSpaces(String str, String replaceWith) {
return StringUtils.isBlank(str) ? "" : str.trim().replaceAll("[\\p{C}\\p{Z}]+",
StringUtils.trimToEmpty(replaceWith)).toLowerCase();
}
/**
* Formats a messages containing {0}, {1}... etc. Used for translation.
* @param msg a message with placeholders
* @param params objects used to populate the placeholders
* @return a formatted message
*/
public static String formatMessage(String msg, Object... params) {
try {
// required by MessageFormat, single quotes break string interpolation!
msg = StringUtils.replace(msg, "'", "''");
return StringUtils.isBlank(msg) ? "" : MessageFormat.format(msg, params);
} catch (IllegalArgumentException e) {
return msg;
}
}
/**
* Encodes a byte array to Base64.
* @param str the byte array
* @return an encoded string
*/
public static String base64enc(byte[] str) {
if (str == null) {
return "";
}
return new String(Base64.encodeBase64(str));
}
/**
* Encodes a byte array to Base64. URL safe.
* @param str the byte array
* @return an encoded string
*/
public static String base64encURL(byte[] str) {
if (str == null) {
return "";
}
return new String(Base64.encodeBase64URLSafe(str));
}
/**
* Decodes a string from Base64.
* @param str the encoded string
* @return a decoded string
*/
public static String base64dec(String str) {
if (str == null) {
return "";
}
try {
return new String(Base64.decodeBase64(str), Config.DEFAULT_ENCODING);
} catch (UnsupportedEncodingException ex) {
return "";
}
}
/////////////////////////////////////////////
// DATE UTILS
/////////////////////////////////////////////
/**
* Formats a date in a specific format.
* @param timestamp the Java timestamp
* @param format the date format
* @param loc the locale instance
* @return a formatted date
*/
public static String formatDate(Long timestamp, String format, Locale loc) {
if (StringUtils.isBlank(format)) {
format = DateFormatUtils.ISO_8601_EXTENDED_DATE_FORMAT.getPattern();
}
if (timestamp == null) {
timestamp = timestamp();
}
if (loc == null) {
loc = Locale.US;
}
return DateFormatUtils.format(timestamp, format, loc);
}
/**
* Formats the date for today, in a specific format.
* @param format the date format
* @param loc the locale instance
* @return today's date formatted
*/
public static String formatDate(String format, Locale loc) {
return formatDate(timestamp(), format, loc);
}
/**
* Returns the current year.
* @return this year
*/
public static int getCurrentYear() {
return Calendar.getInstance().get(Calendar.YEAR);
}
/**
* Java timestamp.
* @return {@link java.lang.System#currentTimeMillis()}
*/
public static long timestamp() {
return System.currentTimeMillis();
}
/**
* Returns an array of the months in the Gregorian calendar.
* @param locale the locale used for the months' names
* @return an array of the 12 months
*/
public static String[] getMonths(Locale locale) {
if (locale == null) {
locale = Locale.US;
}
DateFormatSymbols dfs = DateFormatSymbols.getInstance(locale);
return dfs.getMonths();
}
/**
* @param localeStr locale string
* @return a {@link Locale} instance from a locale string.
*/
public static Locale getLocale(String localeStr) {
try {
return LocaleUtils.toLocale(localeStr);
} catch (Exception e) {
return Locale.US;
}
}
/////////////////////////////////////////////
// NUMBER UTILS
/////////////////////////////////////////////
/**
* Rounds a float to an int.
* @param d a float
* @return a rounded int
*/
public static int round(float d) {
return Math.round(d);
}
/**
* Returns the price with two fractional digits at the end.
* @param price a price
* @return $###.##
*/
public static String formatPrice(double price) {
return NUMBER_FORMAT.format(price);
}
/**
* Round up a double using the "half up" method.
* @param d a double
* @return a double
*/
public static double roundHalfUp(double d) {
return roundHalfUp(d, 2);
}
/**
* Round up a double using the "half up" method.
* @param d a double
* @param scale the scale
* @return a double
*/
public static double roundHalfUp(double d, int scale) {
return BigDecimal.valueOf(d).setScale(scale, RoundingMode.HALF_UP).doubleValue();
}
/**
* Abbreviates an integer by adding a letter suffix at the end.
* E.g. "M" for millions, "K" for thousands, etc.
* @param number a big integer
* @param decPlaces decimal places
* @return the rounded integer as a string
*/
public static String abbreviateInt(Number number, int decPlaces) {
if (number == null) {
return "";
}
String abbrevn = number.toString();
// 2 decimal places => 100, 3 => 1000, etc
decPlaces = (int) Math.pow(10, decPlaces);
// Enumerate number abbreviations
String[] abbrev = {"K", "M", "B", "T"};
boolean done = false;
// Go through the array backwards, so we do the largest first
for (int i = abbrev.length - 1; i >= 0 && !done; i--) {
// Convert array index to "1000", "1000000", etc
int size = (int) Math.pow(10, (i + 1) * 3);
// If the number is bigger or equal do the abbreviation
if (size <= number.intValue()) {
// Here, we multiply by decPlaces, round, and then divide by decPlaces.
// This gives us nice rounding to a particular decimal place.
number = Math.round(number.intValue() * decPlaces / size) / decPlaces;
// Add the letter for the abbreviation
abbrevn = number + abbrev[i];
// We are done... stop
done = true;
}
}
return abbrevn;
}
/////////////////////////////////////////////
// URL UTILS
/////////////////////////////////////////////
/**
* Decodes a URL-encoded string.
* @param s a string
* @return the decoded string
*/
public static String urlDecode(String s) {
if (s == null) {
return "";
}
String decoded = s;
try {
decoded = URLDecoder.decode(s, Config.DEFAULT_ENCODING);
} catch (UnsupportedEncodingException ex) {
logger.error(null, ex);
}
return decoded;
}
/**
* URL-encodes a string.
* @param s a string
* @return the encoded string
*/
public static String urlEncode(String s) {
if (s == null) {
return "";
}
String encoded = s;
try {
encoded = URLEncoder.encode(s, Config.DEFAULT_ENCODING);
} catch (UnsupportedEncodingException ex) {
logger.error(null, ex);
}
return encoded;
}
/**
* URL validation.
* @param url a URL
* @return true if the URL is valid
*/
public static boolean isValidURL(String url) {
return toURL(url) != null;
}
/**
* Email validation.
* @param url a URL
* @return true if the URL is valid
*/
public static boolean isValidEmail(String url) {
return EMAIL_PATTERN.matcher(url).matches();
}
/**
* Returns the host part of the URL.
* @param url a URL
* @return just the host
*/
public static String getHostFromURL(String url) {
URL u = toURL(url);
String host = (u == null) ? "" : u.getHost();
return host;
}
/**
* The basic URL without any parameters: >scheme<:>authority<.
* @param url a full URL
* @return the basic URL
*/
public static String getBaseURL(String url) {
URL u = toURL(url);
String base = null;
if (u != null) {
try {
base = u.toURI().getScheme().concat("://").concat(u.getAuthority());
} catch (URISyntaxException ex) {
base = null;
}
}
return base;
}
private static URL toURL(String url) {
if (StringUtils.isBlank(url)) {
return null;
}
URL u;
try {
u = new URL(url);
} catch (MalformedURLException e) {
// the URL is not in a valid form
u = null;
}
return u;
}
/**
* Returns the default URL for a given domain object.
* @param obj the domain object
* @param includeName true if we want to include the name of the object in the URL
* @param includeId true if we want to include the ID of the object in the URL
* @return the object's URL - e.g. /users/123-name, /users/, /users/123
*/
public static String getObjectURI(ParaObject obj, boolean includeName, boolean includeId) {
if (obj == null) {
return "/";
}
if (includeId && obj.getId() != null) {
return (includeName && !StringUtils.isBlank(obj.getName())) ? obj.getObjectURI().concat("-").
concat(urlEncode(noSpaces(obj.getName(), "-"))) : obj.getObjectURI();
} else {
return obj.getObjectURI();
}
}
/////////////////////////////////////////////
// COOKIE & STATE UTILS
/////////////////////////////////////////////
/**
* Sets a cookie.
* @param name the name
* @param value the value
* @param req HTTP request
* @param res HTTP response
*/
public static void setStateParam(String name, String value, HttpServletRequest req,
HttpServletResponse res) {
setStateParam(name, value, req, res, false);
}
/**
* Sets a cookie.
* @param name the name
* @param value the value
* @param req HTTP request
* @param res HTTP response
* @param httpOnly HTTP only flag
*/
public static void setStateParam(String name, String value, HttpServletRequest req,
HttpServletResponse res, boolean httpOnly) {
setRawCookie(name, value, req, res, httpOnly, -1);
}
/**
* Reads a cookie.
* @param name the name
* @param req HTTP request
* @return the cookie value
*/
public static String getStateParam(String name, HttpServletRequest req) {
return getCookieValue(req, name);
}
/**
* Deletes a cookie.
* @param name the name
* @param req HTTP request
* @param res HTTP response
*/
public static void removeStateParam(String name, HttpServletRequest req,
HttpServletResponse res) {
setRawCookie(name, "", req, res, false, 0);
}
/**
* Sets a cookie.
* @param name the name
* @param value the value
* @param req HTTP request
* @param res HTTP response
* @param httpOnly HTTP only flag
* @param maxAge max age
*/
public static void setRawCookie(String name, String value, HttpServletRequest req,
HttpServletResponse res, boolean httpOnly, int maxAge) {
if (StringUtils.isBlank(name) || value == null || req == null || res == null) {
return;
}
Cookie cookie = new Cookie(name, value);
cookie.setHttpOnly(httpOnly);
cookie.setMaxAge(maxAge < 0 ? Config.SESSION_TIMEOUT_SEC.intValue() : maxAge);
cookie.setPath("/");
cookie.setSecure(req.isSecure());
res.addCookie(cookie);
}
/**
* Reads a cookie.
* @param name the name
* @param req HTTP request
* @return the cookie value
*/
public static String getCookieValue(HttpServletRequest req, String name) {
if (StringUtils.isBlank(name) || req == null) {
return null;
}
Cookie[] cookies = req.getCookies();
if (cookies == null || name == null || name.length() == 0) {
return null;
}
//Otherwise, we have to do a linear scan for the cookie.
for (Cookie cookie : cookies) {
if (cookie.getName().equals(name)) {
return cookie.getValue();
}
}
return null;
}
/**
* Same as {@link java.lang.System#getProperty(java.lang.String)}.
* @param name the name of the property
* @return the property value
*/
public static String getSystemProperty(String name) {
return StringUtils.isBlank(name) ? "" : System.getProperty(name);
}
/////////////////////////////////////////////
// MISC UTILS
/////////////////////////////////////////////
/**
* Returns the adjusted size of an image (doesn't do any resizing).
* @param h an image height
* @param w an image width
* @return the adjusted width and height if they are larger than {@link Config#MAX_IMG_SIZE_PX}.
* @see Config#MAX_IMG_SIZE_PX
*/
public static int[] getMaxImgSize(int h, int w) {
int[] size = {h, w};
int max = Config.MAX_IMG_SIZE_PX;
if (w == h) {
size[0] = Math.min(h, max);
size[1] = Math.min(w, max);
} else if (Math.max(h, w) > max) {
int ratio = (100 * max) / Math.max(h, w);
if (h > w) {
size[0] = max;
size[1] = (w * ratio) / 100;
} else {
size[0] = (h * ratio) / 100;
size[1] = max;
}
}
return size;
}
/**
* Checks if a request comes from JavaScript.
* @param request HTTP request
* @return true if AJAX
*/
public static boolean isAjaxRequest(HttpServletRequest request) {
return "XMLHttpRequest".equalsIgnoreCase(request.getHeader("X-Requested-With")) ||
"XMLHttpRequest".equalsIgnoreCase(request.getParameter("X-Requested-With"));
}
/**
* Checks if a response is of type JSON.
* @param contentType the value of "Content-Type" header
* @return true if JSON
*/
public static boolean isJsonType(String contentType) {
return StringUtils.startsWith(contentType, "application/json") ||
StringUtils.startsWith(contentType, "application/javascript") ||
StringUtils.startsWith(contentType, "text/javascript"); // F U facebook!
}
/**
* Quick and dirty singular to plural conversion.
* @param singul a word
* @return a guess of its plural form
*/
public static String singularToPlural(String singul) {
return (StringUtils.isBlank(singul) || singul.endsWith("es") || singul.endsWith("ies")) ? singul :
(singul.endsWith("s") ? singul + "es" :
(singul.endsWith("y") ? StringUtils.removeEndIgnoreCase(singul, "y") + "ies" :
singul + "s"));
}
/**
* Checks if a class is primitive, String or a primitive wrapper.
*
* @param clazz a class
* @return true if primitive or wrapper
*/
public static boolean isBasicType(Class> clazz) {
return (clazz == null) ? false : (clazz.isPrimitive()
|| clazz.equals(String.class)
|| clazz.equals(Long.class)
|| clazz.equals(Integer.class)
|| clazz.equals(Boolean.class)
|| clazz.equals(Byte.class)
|| clazz.equals(Short.class)
|| clazz.equals(Float.class)
|| clazz.equals(Double.class)
|| clazz.equals(Character.class));
}
/**
* Returns the simple name of a class in lowercase (AKA the type).
*
* @param clazz a core class
* @return just the name in lowercase or an empty string if clazz is null
*/
public static String type(Class extends ParaObject> clazz) {
return (clazz == null) ? "" : clazz.getSimpleName().toLowerCase();
}
/////////////////////////////////////////////
// ANNOTATIONS
/////////////////////////////////////////////
/**
* Returns a list of all declared fields in a class. Transient and serialVersionUID fields are skipped.
* This method scans parent classes as well.
* @param clazz a class to scan
* @return a list of fields including those of the parent classes excluding the Object class.
*/
public static List getAllDeclaredFields(Class extends ParaObject> clazz) {
LinkedList fields = new LinkedList();
if (clazz == null) {
return fields;
}
Class> parent = clazz;
do {
for (Field field : parent.getDeclaredFields()) {
if (!Modifier.isTransient(field.getModifiers()) &&
!field.getName().equals("serialVersionUID")) {
fields.add(field);
}
}
parent = parent.getSuperclass();
} while (!parent.equals(Object.class));
return fields;
}
/////////////////////////////////////////////
// MODIFIED SNOWFLAKE
/////////////////////////////////////////////
/**
* Distributed id generator. Relies on node/worker ids and datacenter ids to prevent collisions.
* @return a long unique ID string of digits
*/
public static String getNewId() {
// unique across JVMs as long as each has a different workerID
// based on Twitter's Snowflake algorithm
long timestamp = timestamp();
if (lastTimestamp == timestamp) {
sequence = (sequence + 1) & SEQUENCE_MASK;
if (sequence == 0) {
timestamp = tilNextMillis(lastTimestamp);
}
} else {
sequence = 0;
}
if (timestamp < lastTimestamp) {
throw new IllegalStateException(String.format("Clock moved backwards. "
+ "Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
}
lastTimestamp = timestamp;
return Long.toString(((timestamp - TIMER_OFFSET) << TIMESTAMP_LEFT_SHIFT) |
(dataCenterId << DATACENTER_ID_SHIFT) |
(workerId << WORKER_ID_SHIFT) |
(sequence));
}
private static long tilNextMillis(long lastTimestamp) {
long timestamp = timestamp();
while (timestamp <= lastTimestamp) {
timestamp = timestamp();
}
return timestamp;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy