All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.rx.core.App Maven / Gradle / Ivy

package org.rx.core;

import com.google.common.net.HttpHeaders;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.SystemUtils;
import org.rx.annotation.ErrorCode;
import org.rx.beans.AppConfig;
import org.rx.beans.Tuple;
import org.rx.security.MD5Util;
import org.rx.beans.DateTime;
import org.rx.socks.http.HttpClient;
import org.rx.io.MemoryStream;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.constructor.SafeConstructor;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ThreadLocalRandom;
import java.util.function.BiFunction;
import java.util.function.Function;

import static org.rx.core.Contract.*;

@Slf4j
public class App extends SystemUtils {
    //region Nested
    @RequiredArgsConstructor
    private static class ConvertItem {
        public final Class baseFromType;
        public final Class toType;
        public final BiFunction converter;
    }
    //endregion

    //region Fields
    public static final AppConfig Config;
    private static final NQuery> supportTypes;
    private static final List typeConverter;

    static {
        System.setProperty("bootstrapPath", getBootstrapPath());
        Config = isNull(readSetting("app", AppConfig.class), new AppConfig());
        if (Config.getBufferSize() <= 0) {
            Config.setBufferSize(512);
        }
        Contract.init();
        supportTypes = NQuery.of(String.class, Boolean.class, Byte.class, Short.class, Integer.class, Long.class,
                Float.class, Double.class, Enum.class, Date.class, UUID.class, BigDecimal.class);
        typeConverter = new CopyOnWriteArrayList<>();
    }
    //endregion

    //region Basic
    public static String getBootstrapPath() {
        String p = App.class.getClassLoader().getResource("").getFile();
        if (IS_OS_WINDOWS) {
            if (p.startsWith("file:/")) {
                p = p.substring(6);
            } else {
                p = p.substring(1);
            }
        }
        System.out.println("bootstrapPath:" + p);
        return p;
    }

    public static List execShell(String workspace, String... shellStrings) {
        List resultList = new ArrayList<>();
        StringBuilder msg = new StringBuilder();
        File dir = null;
        if (workspace != null) {
            msg.append(String.format("execShell workspace=%s\n", workspace));
            dir = new File(workspace);
        }
        for (String shellString : shellStrings) {
            msg.append(String.format("pre-execShell %s", shellString));
            StringBuilder result = new StringBuilder();
            try {
                Process process;
                if (IS_OS_WINDOWS) {
                    process = Runtime.getRuntime().exec(shellString, null, dir);
                } else {
                    process = Runtime.getRuntime().exec(new String[]{"/bin/sh", "-c", shellString}, null, dir);
                }
                try (LineNumberReader input = new LineNumberReader(
                        new InputStreamReader(process.getInputStream(), StandardCharsets.UTF_8))) {
                    String line;
                    while ((line = input.readLine()) != null) {
                        result.append(line).append("\n");
                    }
                }
            } catch (Exception e) {
                log.error("execShell", e);
                result.append("ERROR: " + e.getMessage()).append("\n");
            }
            msg.append(String.format("\npost-execShell %s\n\n", result));
            if (result.getLength() == 0) {
                result.append("NULL");
            }
            resultList.add(result.toString());
        }
        log.info(msg.toString());
        return resultList;
    }

    public static UUID hash(String key) {
        require(key);

        byte[] guidBytes = MD5Util.md5(key);
        return newUUID(guidBytes);
    }

    public static UUID newComb(boolean sequentialAtEnd) {
        return newComb(null, null);
    }

    public static UUID newComb(String key, Date now) {
        return newComb(key, now, false);
    }

    //http://www.codeproject.com/Articles/388157/GUIDs-as-fast-primary-keys-under-multiple-database
    public static UUID newComb(String key, Date date, boolean sequentialAtEnd) {
        byte[] guidBytes, msecsBytes;
        if (key != null) {
            guidBytes = MD5Util.md5(key);
        } else {
            guidBytes = new byte[16];
            ThreadLocalRandom.current().nextBytes(guidBytes);
        }
        if (date != null) {
            msecsBytes = ByteBuffer.allocate(8).putLong(date.getTime() - DateTime.BaseDate.getTime()).array();
        } else {
            msecsBytes = ByteBuffer.allocate(8).putLong(System.nanoTime() - DateTime.BaseDate.getTime()).array();
        }
        int copyCount = 6, copyOffset = msecsBytes.length - copyCount;
        if (sequentialAtEnd) {
            System.arraycopy(msecsBytes, copyOffset, guidBytes, guidBytes.length - copyCount, copyCount);
        } else {
            System.arraycopy(msecsBytes, copyOffset, guidBytes, 0, copyCount);
        }
        return newUUID(guidBytes);
    }

    private static UUID newUUID(byte[] guidBytes) {
        long msb = 0, lsb = 0;
        for (int i = 0; i < 8; i++) {
            msb = (msb << 8) | (guidBytes[i] & 0xff);
        }
        for (int i = 8; i < 16; i++) {
            lsb = (lsb << 8) | (guidBytes[i] & 0xff);
        }
        return new UUID(msb, lsb);
    }

    public static  T readSetting(String key) {
        return readSetting(key, null);
    }

    public static  T readSetting(String key, Class type) {
        return readSetting(key, type, loadYaml("application.yml"));
    }

    public static  T readSetting(String key, Class type, Map settings) {
        return readSetting(key, type, settings, false);
    }

    @ErrorCode(value = "keyError", messageKeys = {"$key", "$type"})
    @ErrorCode(value = "partialKeyError", messageKeys = {"$key", "$type"})
    public static  T readSetting(String key, Class type, Map settings, boolean throwOnEmpty) {
        require(key, settings);

        Function func = p -> {
            if (type == null) {
                return (T) p;
            }
            Map map = as(p, Map.class);
            if (map != null) {
                return fromJsonAsObject(map, type);
            }
            return changeType(p, type);
        };
        Object val;
        if ((val = settings.get(key)) != null) {
            return func.apply(val);
        }

        StringBuilder kBuf = new StringBuilder();
        String d = ".";
        String[] splits = Strings.split(key, d);
        int c = splits.length - 1;
        for (int i = 0; i <= c; i++) {
            if (kBuf.getLength() > 0) {
                kBuf.append(d);
            }
            String k = kBuf.append(splits[i]).toString();
            if ((val = settings.get(k)) == null) {
                continue;
            }
            if (i == c) {
                return func.apply(val);
            }
            if ((settings = as(val, Map.class)) == null) {
                throw new SystemException(values(k, type), "partialKeyError");
            }
            kBuf.setLength(0);
        }

        if (throwOnEmpty) {
            throw new SystemException(values(key, type), "keyError");
        }
        return null;
    }

    public static Map loadYaml(String... yamlFile) {
        require((Object) yamlFile);

        return MemoryCache.getOrStore(String.format("loadYaml-%s", toJsonString(yamlFile)), k -> {
            Map result = new HashMap<>();
            Yaml yaml = new Yaml(new SafeConstructor());
            for (String yf : yamlFile) {
                File file = new File(yf);
                for (Object data : yaml.loadAll(file.exists() ? new FileInputStream(file) : getClassLoader().getResourceAsStream(yf))) {
                    Map one = (Map) data;
                    fillDeep(one, result);
                }
            }
            return result;
        });
    }

    private static void fillDeep(Map one, Map all) {
        if (one == null) {
            return;
        }
        for (Map.Entry entry : one.entrySet()) {
            Map nextOne;
            if ((nextOne = as(entry.getValue(), Map.class)) == null) {
                all.put(entry.getKey(), entry.getValue());
                continue;
            }
            Map nextAll = (Map) all.get(entry.getKey());
            if (nextAll == null) {
                all.put(entry.getKey(), nextOne);
                continue;
            }
            fillDeep(nextOne, nextAll);
        }
    }

    public static  T loadYaml(String yamlContent, Class beanType) {
        require(yamlContent, beanType);

        Yaml yaml = new Yaml();
        return yaml.loadAs(yamlContent, beanType);
    }

    public static  String dumpYaml(T bean) {
        require(bean);

        Yaml yaml = new Yaml();
        return yaml.dump(bean);
    }
    //endregion

    //region Class
    @ErrorCode(messageKeys = {"$name", "$type"})
    public static InputStream getResource(Class owner, String name) {
        InputStream resource = owner.getResourceAsStream(name);
        if (resource == null) {
            throw new SystemException(values(owner, name));
        }
        return resource;
    }

    /**
     * ClassLoader.getSystemClassLoader()
     *
     * @return
     */
    public static ClassLoader getClassLoader() {
        return isNull(Thread.currentThread().getContextClassLoader(), App.class.getClassLoader());
    }

    public static  Class loadClass(String className, boolean initialize) {
        return loadClass(className, initialize, true);
    }

    //ClassPath.from(classloader).getTopLevelClasses(packageDirName)
    public static Class loadClass(String className, boolean initialize, boolean throwOnEmpty) {
        try {
            return Class.forName(className, initialize, getClassLoader());
        } catch (ClassNotFoundException e) {
            if (!throwOnEmpty) {
                return null;
            }
            throw SystemException.wrap(e);
        }
    }

    public static  T convert(Object val, Class toType) {
        return tryConvert(val, toType).right;
    }

    public static  Tuple tryConvert(Object val, Class toType) {
        return tryConvert(val, toType, null);
    }

    public static  Tuple tryConvert(Object val, Class toType, T defaultVal) {
        require(toType);

        try {
            return Tuple.of(true, changeType(val, toType));
        } catch (Exception ex) {
            return Tuple.of(false, defaultVal);
        }
    }

    public synchronized static  void registerConverter(Class baseFromType, Class toType, BiFunction converter) {
        require(baseFromType, toType, converter);

        typeConverter.add(0, new ConvertItem(baseFromType, toType, (BiFunction) converter));
        if (!supportTypes.contains(baseFromType)) {
            supportTypes.asCollection().add(baseFromType);
        }
    }

    private static BiFunction getConverter(Object fromValue, Class toType) {
        for (ConvertItem convertItem : NQuery.of(typeConverter).toList()) {
            if (Reflects.isInstance(fromValue, convertItem.baseFromType) && convertItem.toType.isAssignableFrom(toType)) {
                return convertItem.converter;
            }
        }
        return null;
    }

    @ErrorCode(value = "notSupported", messageKeys = {"$fType", "$tType"})
    @ErrorCode(value = "enumError", messageKeys = {"$name", "$names", "$eType"})
    @ErrorCode(cause = NoSuchMethodException.class, messageKeys = {"$type"})
    @ErrorCode(cause = ReflectiveOperationException.class, messageKeys = {"$fType", "$tType", "$val"})
    public static  T changeType(Object value, Class toType) {
        require(toType);

        if (value == null) {
            if (toType.isPrimitive()) {
                if (boolean.class.equals(toType)) {
                    value = false;
                } else {
                    value = 0;
                }
            } else {
                return null;
            }
        }
        if (Reflects.isInstance(value, toType)) {
            return (T) value;
        }
        Class strType = supportTypes.first();
        if (toType.equals(strType)) {
            return (T) value.toString();
        }
        final Class fromType = value.getClass();
        if (!(supportTypes.any(p -> p.equals(fromType)))) {
            throw new SystemException(values(fromType, toType), "notSupported");
        }
        BiFunction converter = getConverter(value, toType);
        if (converter != null) {
            return (T) converter.apply(value, toType);
        }

        String val = value.toString();
        if (toType.equals(UUID.class)) {
            value = UUID.fromString(val);
        } else if (toType.equals(BigDecimal.class)) {
            value = new BigDecimal(val);
        } else if (toType.isEnum()) {
            NQuery q = NQuery.of(toType.getEnumConstants()).select(p -> ((Enum) p).name());
            value = q.where(p -> p.equals(val)).singleOrDefault();
            if (value == null) {
                throw new SystemException(values(val, String.join(",", q), toType.getSimpleName()), "enumError");
            }
        } else {
            final String of = "valueOf";
            try {
                Method m;
                if (Date.class.isAssignableFrom(toType)) {
                    m = DateTime.class.getDeclaredMethod(of, strType);
                } else {
                    toType = checkType(toType);
                    m = toType.getDeclaredMethod(of, strType);
                }
                value = m.invoke(null, val);
            } catch (NoSuchMethodException ex) {
                throw new SystemException(values(toType), ex);
            } catch (ReflectiveOperationException ex) {
                throw new SystemException(values(fromType, toType, val), ex);
            }
        }
        return (T) value;
    }

    @ErrorCode(messageKeys = {"$type"})
    private static Class checkType(Class type) {
        if (!type.isPrimitive()) {
            return type;
        }

        String pName = type.equals(int.class) ? "Integer" : type.getName();
        String newName = "java.lang." + pName.substring(0, 1).toUpperCase() + pName.substring(1, pName.length());
        try {
            return Class.forName(newName);
        } catch (ClassNotFoundException ex) {
            throw new SystemException(values(newName), ex);
        }
    }

    public static boolean isBase64String(String base64String) {
        if (Strings.isNullOrEmpty(base64String)) {
            return false;
        }

        return org.apache.commons.codec.binary.Base64.isBase64(base64String);
    }

    @SneakyThrows
    public static String convertToBase64String(byte[] data) {
        require(data);

        byte[] ret = Base64.getEncoder().encode(data);
        return new String(ret, Contract.Utf8);
    }

    @SneakyThrows
    public static byte[] convertFromBase64String(String base64) {
        require(base64);

        byte[] data = base64.getBytes(Contract.Utf8);
        return Base64.getDecoder().decode(data);
    }

    public static String serializeToBase64(Object obj) {
        byte[] data = serialize(obj);
        return convertToBase64String(data);
    }

    @SneakyThrows
    public static byte[] serialize(Object obj) {
        require(obj);

        try (MemoryStream stream = new MemoryStream();
             ObjectOutputStream out = new ObjectOutputStream(stream.getWriter())) {
            out.writeObject(obj);
            return stream.toArray();
        }
    }

    public static Object deserializeFromBase64(String base64) {
        byte[] data = convertFromBase64String(base64);
        return deserialize(data);
    }

    @SneakyThrows
    public static Object deserialize(byte[] data) {
        require(data);

        try (MemoryStream stream = new MemoryStream(data, 0, data.length);
             ObjectInputStream in = new ObjectInputStream(stream.getReader())) {
            return in.readObject();
        }
    }

    public static  T deepClone(T obj) {
        byte[] data = serialize(obj);
        return (T) deserialize(data);
    }
    //endregion

    //region Servlet
    public static HttpServletRequest getCurrentRequest() {
        ServletRequestAttributes ra = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        return ra == null ? null : ra.getRequest();
    }

    public static String getRequestIp(HttpServletRequest request) {
        if (request == null) {
            return "0.0.0.0";
        }

        String ip = request.getHeader(HttpHeaders.X_FORWARDED_FOR);
        if (Strings.isNullOrEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (Strings.isNullOrEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (Strings.isNullOrEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_CLIENT_IP");
        }
        if (Strings.isNullOrEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_X_FORWARDED_FOR");
        }
        if (Strings.isNullOrEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("x-real-ip");
        }
        if (Strings.isNullOrEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        String[] ips = ip.split(",");
        if (ips.length > 1) {
            ip = ips[0];
        }
        return ip;
    }

    @SneakyThrows
    public static void downloadFile(HttpServletResponse response, String filePath) {
        require(response, filePath);
        switch (filePath) {
            case "info":
            case "error":
                filePath = String.format("%s/logs/%s", System.getProperty("catalina.home"), filePath);
        }

        File file = new File(filePath);
        response.setCharacterEncoding(Contract.Utf8);
        response.setContentType("application/octet-stream");
        response.setContentLength((int) file.length());
        response.setHeader(HttpHeaders.CONTENT_DISPOSITION, String.format("attachment; filename=\"%s\"", file.getName()));
        try (FileInputStream in = new FileInputStream(file)) {
            OutputStream out = response.getOutputStream();
            byte[] buffer = new byte[4096];
            int length;
            while ((length = in.read(buffer)) > 0) {
                out.write(buffer, 0, length);
            }
            out.flush();
        }
    }

    public static String getCookie(String name) {
        require(name);

        HttpServletRequest servletRequest = getCurrentRequest();
        if (servletRequest == null) {
            throw new InvalidOperationException("上下环境无ServletRequest");
        }

        return catchCall(() -> {
            if (ArrayUtils.isEmpty(servletRequest.getCookies())) {
                return null;
            }
            return NQuery.of(servletRequest.getCookies()).where(p -> p.getName().equals(name)).select(p -> HttpClient.decodeUrl(p.getValue())).firstOrDefault();
        });
    }

    public static void setCookie(HttpServletResponse response, String name, String value) {
        setCookie(response, name, value);
    }

    public static void setCookie(HttpServletResponse response, String name, String value, Date expire) {
        require(response, name);

        Cookie cookie = new Cookie(name, HttpClient.encodeUrl(value));
        cookie.setPath("/");
//        cookie.setSecure(true);
        cookie.setHttpOnly(true);
        if (expire != null) {
            cookie.setMaxAge((int) new DateTime(expire).subtract(DateTime.now()).getTotalSeconds());
        }
        response.addCookie(cookie);
    }

    public static void deleteCookie(HttpServletResponse response, String name) {
        require(response, name);

        Cookie cookie = new Cookie(name, null);
        cookie.setPath("/");
//        cookie.setSecure(true);
        cookie.setHttpOnly(true);
        cookie.setMaxAge(-1);
        response.addCookie(cookie);
    }
    //endregion
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy