password.pwm.util.java.JavaHelper.java Source code

Java tutorial

Introduction

Here is the source code for password.pwm.util.java.JavaHelper.java

Source

/*
 * Password Management Servlets (PWM)
 * http://www.pwm-project.org
 *
 * Copyright (c) 2006-2009 Novell, Inc.
 * Copyright (c) 2009-2017 The PWM Project
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

package password.pwm.util.java;

import org.apache.commons.csv.CSVPrinter;
import org.apache.commons.io.IOUtils;
import password.pwm.PwmApplication;
import password.pwm.PwmConstants;
import password.pwm.http.ContextManager;
import password.pwm.util.logging.PwmLogger;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.management.LockInfo;
import java.lang.management.MonitorInfo;
import java.lang.management.ThreadInfo;
import java.lang.reflect.Method;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.Enumeration;
import java.util.GregorianCalendar;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Properties;
import java.util.TimeZone;
import java.util.TreeSet;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.function.Predicate;

public class JavaHelper {

    private static final PwmLogger LOGGER = PwmLogger.forClass(JavaHelper.class);

    private JavaHelper() {
    }

    /**
     * Convert a byte[] array to readable string format. This makes the "hex" readable
     *
     * @param in byte[] buffer to convert to string format
     * @return result String buffer in String format
     */
    public static String byteArrayToHexString(final byte[] in) {
        final String[] pseudo = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F" };

        if (in == null || in.length <= 0) {
            return "";
        }

        final StringBuilder out = new StringBuilder(in.length * 2);

        for (final byte b : in) {
            byte ch = (byte) (b & 0xF0); // strip off high nibble
            ch = (byte) (ch >>> 4); // shift the bits down
            ch = (byte) (ch & 0x0F); // must do this is high order bit is on!
            out.append(pseudo[(int) ch]); // convert the nibble to a String Character
            ch = (byte) (b & 0x0F); // strip off low nibble
            out.append(pseudo[(int) ch]); // convert the nibble to a String Character
        }

        return out.toString();
    }

    /**
     * Pause the calling thread the specified amount of time.
     *
     * @param sleepTimeMS - a time duration in milliseconds
     * @return time actually spent sleeping
     */
    public static long pause(final long sleepTimeMS) {
        final long startTime = System.currentTimeMillis();
        do {
            try {
                final long sleepTime = sleepTimeMS - (System.currentTimeMillis() - startTime);
                Thread.sleep(sleepTime > 0 ? sleepTime : 5);
            } catch (InterruptedException e) {
                //who cares
            }
        } while ((System.currentTimeMillis() - startTime) < sleepTimeMS);

        return System.currentTimeMillis() - startTime;
    }

    public static long pause(final long sleepTimeMS, final long predicateCheckIntervalMS,
            final Predicate predicate) {
        final long startTime = System.currentTimeMillis();
        final long pauseTime = Math.max(sleepTimeMS, predicateCheckIntervalMS);
        while ((System.currentTimeMillis() - startTime) < sleepTimeMS) {
            JavaHelper.pause(pauseTime);
            if (predicate.test(null)) {
                break;
            }
        }

        return System.currentTimeMillis() - startTime;
    }

    public static String binaryArrayToHex(final byte[] buf) {
        final char[] HEX_CHARS = "0123456789ABCDEF".toCharArray();
        final char[] chars = new char[2 * buf.length];
        for (int i = 0; i < buf.length; ++i) {
            chars[2 * i] = HEX_CHARS[(buf[i] & 0xF0) >>> 4];
            chars[2 * i + 1] = HEX_CHARS[buf[i] & 0x0F];
        }
        return new String(chars);
    }

    public static Instant nextZuluZeroTime() {
        final Calendar nextZuluMidnight = GregorianCalendar.getInstance(TimeZone.getTimeZone("Zulu"));
        nextZuluMidnight.set(Calendar.HOUR_OF_DAY, 0);
        nextZuluMidnight.set(Calendar.MINUTE, 0);
        nextZuluMidnight.set(Calendar.SECOND, 0);
        nextZuluMidnight.add(Calendar.HOUR, 24);
        return nextZuluMidnight.getTime().toInstant();
    }

    public static <E extends Enum<E>> List<E> readEnumListFromStringCollection(final Class<E> enumClass,
            final Collection<String> inputs) {
        final List<E> returnList = new ArrayList<E>();
        for (final String input : inputs) {
            final E item = readEnumFromString(enumClass, null, input);
            if (item != null) {
                returnList.add(item);
            }
        }
        return Collections.unmodifiableList(returnList);
    }

    public static <E extends Enum<E>> E readEnumFromString(final Class<E> enumClass, final E defaultValue,
            final String input) {
        if (StringUtil.isEmpty(input)) {
            return defaultValue;
        }

        if (enumClass == null || !enumClass.isEnum()) {
            return defaultValue;
        }

        try {
            return Enum.valueOf(enumClass, input);
        } catch (IllegalArgumentException e) {
            /* noop */
            //LOGGER.trace("input=" + input + " does not exist in enumClass=" + enumClass.getSimpleName());
        } catch (Throwable e) {
            LOGGER.warn("unexpected error translating input=" + input + " to enumClass=" + enumClass.getSimpleName()
                    + ", error: " + e.getMessage());
        }

        return defaultValue;
    }

    public static String throwableToString(final Throwable throwable) {
        final StringWriter sw = new StringWriter();
        final PrintWriter pw = new PrintWriter(sw);
        throwable.printStackTrace(pw);
        pw.flush();
        return sw.toString();
    }

    /**
     * Converts an exception to a string message.  Handles cases where the message in the exception is null
     * and/or there are multiple nested cause exceptions.
     * @param e The exception to convert to a string
     * @return A string containing any meaningful extractable cause information, suitable for debugging.
     */
    public static String readHostileExceptionMessage(final Throwable e) {
        String errorMsg = e.getClass().getName();
        if (e.getMessage() != null) {
            errorMsg += ": " + e.getMessage();
        }

        Throwable cause = e.getCause();
        int safetyCounter = 0;
        while (cause != null && safetyCounter < 10) {
            safetyCounter++;
            errorMsg += ", cause:" + cause.getClass().getName();
            if (cause.getMessage() != null) {
                errorMsg += ": " + cause.getMessage();
            }
            cause = cause.getCause();
        }

        return errorMsg;
    }

    public static <E extends Enum<E>> boolean enumArrayContainsValue(final E[] enumArray, final E enumValue) {
        return !(enumArray == null || enumArray.length == 0) && Arrays.asList(enumArray).contains(enumValue);
    }

    public static void unhandledSwitchStatement(final Object switchParameter) {
        final String className = switchParameter == null ? "unknown - see stack trace"
                : switchParameter.getClass().getName();

        final String paramValue = switchParameter == null ? "unknown" : switchParameter.toString();

        final String errorMsg = "unhandled switch statement on parameter class=" + className + ", value="
                + paramValue;
        final UnsupportedOperationException exception = new UnsupportedOperationException(errorMsg);
        LOGGER.warn(errorMsg, exception);
        throw exception;
    }

    public static long copyWhilePredicate(final InputStream input, final OutputStream output,
            final Predicate predicate) throws IOException {
        final int bufferSize = 4 * 1024;
        final byte[] buffer = new byte[bufferSize];
        long bytesCopied;
        long totalCopied = 0;
        do {
            bytesCopied = IOUtils.copyLarge(input, output, 0, bufferSize, buffer);
            if (bytesCopied > 0) {
                totalCopied += bytesCopied;
            }
            if (!predicate.test(null)) {
                return totalCopied;
            }
        } while (bytesCopied > 0);
        return totalCopied;
    }

    public static String toIsoDate(final Instant instant) {
        return instant == null ? "" : instant.truncatedTo(ChronoUnit.SECONDS).toString();
    }

    public static String toIsoDate(final Date date) {
        if (date == null) {
            return "";
        }

        final DateFormat dateFormat = new SimpleDateFormat(PwmConstants.DEFAULT_DATETIME_FORMAT_STR,
                PwmConstants.DEFAULT_LOCALE);

        dateFormat.setTimeZone(PwmConstants.DEFAULT_TIMEZONE);

        return dateFormat.format(date);
    }

    public static Instant parseIsoToInstant(final String input) {
        return Instant.parse(input);
    }

    public static boolean closeAndWaitExecutor(final ExecutorService executor, final TimeDuration timeDuration) {
        if (executor == null) {
            return true;
        }

        executor.shutdown();
        try {
            return executor.awaitTermination(timeDuration.getTotalMilliseconds(), TimeUnit.MILLISECONDS);
        } catch (InterruptedException e) {
            LOGGER.warn("unexpected error shutting down executor service " + executor.getClass().toString()
                    + " error: " + e.getMessage());
        }
        return false;
    }

    public static String makeThreadName(final PwmApplication pwmApplication, final Class theClass) {
        String instanceName = "-";
        if (pwmApplication != null && pwmApplication.getInstanceID() != null) {
            instanceName = pwmApplication.getInstanceID();
        }

        return PwmConstants.PWM_APP_NAME + "-" + instanceName + "-" + theClass.getSimpleName();
    }

    public static Properties newSortedProperties() {
        return new Properties() {
            public synchronized Enumeration<Object> keys() {
                return Collections.enumeration(new TreeSet<>(super.keySet()));
            }
        };
    }

    public static ThreadFactory makePwmThreadFactory(final String namePrefix, final boolean daemon) {
        return new ThreadFactory() {
            private final ThreadFactory realThreadFactory = Executors.defaultThreadFactory();

            @Override
            public Thread newThread(final Runnable r) {
                final Thread t = realThreadFactory.newThread(r);
                t.setDaemon(daemon);
                if (namePrefix != null) {
                    final String newName = namePrefix + t.getName();
                    t.setName(newName);
                }
                return t;
            }
        };
    }

    public static Collection<Method> getAllMethodsForClass(final Class clazz) {
        final LinkedHashSet<Method> methods = new LinkedHashSet<>();

        // add local methods;
        methods.addAll(Arrays.asList(clazz.getDeclaredMethods()));

        final Class superClass = clazz.getSuperclass();
        if (superClass != null) {
            methods.addAll(getAllMethodsForClass(superClass));
        }

        return Collections.unmodifiableSet(methods);
    }

    public static CSVPrinter makeCsvPrinter(final OutputStream outputStream) throws IOException {
        return new CSVPrinter(new OutputStreamWriter(outputStream, PwmConstants.DEFAULT_CHARSET),
                PwmConstants.DEFAULT_CSV_FORMAT);
    }

    public static ScheduledExecutorService makeSingleThreadExecutorService(final PwmApplication pwmApplication,
            final Class clazz) {
        return Executors.newSingleThreadScheduledExecutor(
                makePwmThreadFactory(JavaHelper.makeThreadName(pwmApplication, clazz) + "-", true));
    }

    /**
     * Copy of {@link ThreadInfo#toString()} but with the MAX_FRAMES changed from 8 to 256.
     */
    public static String threadInfoToString(final ThreadInfo threadInfo) {
        final int MAX_FRAMES = 256;
        final StringBuilder sb = new StringBuilder("\"" + threadInfo.getThreadName() + "\"" + " Id="
                + threadInfo.getThreadId() + " " + threadInfo.getThreadState());
        if (threadInfo.getLockName() != null) {
            sb.append(" on " + threadInfo.getLockName());
        }
        if (threadInfo.getLockOwnerName() != null) {
            sb.append(" owned by \"" + threadInfo.getLockOwnerName() + "\" Id=" + threadInfo.getLockOwnerId());
        }
        if (threadInfo.isSuspended()) {
            sb.append(" (suspended)");
        }
        if (threadInfo.isInNative()) {
            sb.append(" (in native)");
        }
        sb.append('\n');

        int counter = 0;
        for (; counter < threadInfo.getStackTrace().length && counter < MAX_FRAMES; counter++) {
            final StackTraceElement ste = threadInfo.getStackTrace()[counter];
            sb.append("\tat ").append(ste.toString());
            sb.append('\n');
            if (counter == 0 && threadInfo.getLockInfo() != null) {
                final Thread.State ts = threadInfo.getThreadState();
                switch (ts) {
                case BLOCKED:
                    sb.append("\t-  blocked on " + threadInfo.getLockInfo());
                    sb.append('\n');
                    break;
                case WAITING:
                    sb.append("\t-  waiting on " + threadInfo.getLockInfo());
                    sb.append('\n');
                    break;
                case TIMED_WAITING:
                    sb.append("\t-  waiting on " + threadInfo.getLockInfo());
                    sb.append('\n');
                    break;
                default:
                }
            }

            for (MonitorInfo mi : threadInfo.getLockedMonitors()) {
                if (mi.getLockedStackDepth() == counter) {
                    sb.append("\t-  locked " + mi);
                    sb.append('\n');
                }
            }
        }
        if (counter < threadInfo.getStackTrace().length) {
            sb.append("\t...");
            sb.append('\n');
        }

        final LockInfo[] locks = threadInfo.getLockedSynchronizers();
        if (locks.length > 0) {
            sb.append("\n\tNumber of locked synchronizers = " + locks.length);
            sb.append('\n');
            for (LockInfo li : locks) {
                sb.append("\t- " + li);
                sb.append('\n');
            }
        }
        sb.append('\n');
        return sb.toString();
    }

    public static String readEulaText(final ContextManager contextManager, final String filename)
            throws IOException {
        final String path = PwmConstants.URL_PREFIX_PUBLIC + "/resources/text/" + filename;
        final InputStream inputStream = contextManager.getResourceAsStream(path);
        final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        copyWhilePredicate(inputStream, byteArrayOutputStream, o -> true);
        return byteArrayOutputStream.toString(PwmConstants.DEFAULT_CHARSET.name());
    }
}