Coverage Summary for Class: Helper (org.kitodo.production.helper)
Class |
Class, %
|
Method, %
|
Line, %
|
Helper |
100%
(1/1)
|
61,8%
(21/34)
|
64,1%
(93/145)
|
/*
* (c) Kitodo. Key to digital objects e. V. <contact@kitodo.org>
*
* This file is part of the Kitodo project.
*
* It is licensed under GNU General Public License version 3 or later.
*
* For the full copyright and license information, please read the
* GPL3-License.txt file that was distributed with this source code.
*/
package org.kitodo.production.helper;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.security.SecureRandom;
import java.text.DateFormat;
import java.text.MessageFormat;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.ResourceBundle;
import javax.faces.application.FacesMessage;
import javax.faces.context.FacesContext;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.kitodo.production.enums.MessageLevel;
import org.kitodo.production.enums.ReportLevel;
import org.kitodo.production.helper.messages.Error;
import org.kitodo.production.helper.messages.Message;
import org.kitodo.production.interfaces.activemq.WebServiceResult;
public class Helper {
private static Map<String, String> activeMQReporting = null;
private static final Logger logger = LogManager.getLogger(Helper.class);
private static Map<Locale, ResourceBundle> commonMessages = null;
private static Map<Locale, ResourceBundle> errorMessages = null;
/**
* Determine a specific parameter of the request.
*
* @return parameter als String
*/
@SuppressWarnings("rawtypes")
public static String getRequestParameter(String parameter) {
FacesContext context = FacesContext.getCurrentInstance();
if (Objects.nonNull(context)) {
Map requestParams = context.getExternalContext().getRequestParameterMap();
return (String) requestParams.get(parameter);
}
return null;
}
/**
* Set error message for user.
*
* @param message
* for user
*/
public static void setErrorMessage(String message) {
setMessage(null, message, "", MessageLevel.ERROR);
}
/**
* Set error message and description for user.
*
* @param message
* for user
* @param description
* additional information to message
*/
public static void setErrorMessage(String message, String description) {
setMessage(null, message, description, MessageLevel.ERROR);
}
/**
* Set error message and description for user.
*
* @param control
* what is it - no documentation for clientId in FacesContext
* @param message
* for user
* @param description
* additional information to message
*/
public static void setErrorMessage(String control, String message, String description) {
setMessage(control, message, description, MessageLevel.ERROR);
}
/**
* Set error message for user with usage of exception.
*
* @param e
* thrown exception
*/
public static void setErrorMessage(Exception e) {
setErrorMessage("Error (" + e.getClass().getName() + "): ", getExceptionMessage(e));
}
/**
* Set error message for user with usage of exception.
*
* @param control
* what is it - no documentation for clientId in FacesContext
* @param message
* for user
* @param e
* thrown exception
*/
public static void setErrorMessage(String control, String message, Exception e) {
setErrorMessage(control, message + " (" + e.getClass().getSimpleName() + "): ", getExceptionMessage(e));
}
/**
* Set error message to message tag with given name 'title'. Substitute all
* placeholders in message tag with elements of given array 'parameters'.
*
* @param title
* name of the message tag set as error message
* @param parameters
* list of parameters used for string substitution in message tag
*/
public static void setErrorMessage(String title, Object... parameters) {
if (Objects.nonNull(parameters) && parameters.length > 0) {
setErrorMessage(getTranslation(title,
Arrays.stream(parameters).map(Objects::toString).toArray(String[]::new)));
} else {
setErrorMessage(getTranslation(title));
}
}
/**
* Set error message to message tag with given name 'title'.
*
* <p>
* This method also accepts logger and exception instances to automatically log
* the exceptions message or stackTrace values to the given logger.
*
* @param title
* name of the message tag set as error message
* @param logger
* Logger instance for error logging
* @param exception
* Exception instance for error logging
*/
public static void setErrorMessage(String title, Logger logger, Exception exception) {
logger.error(title, exception);
if (Objects.isNull(exception.getMessage()) || exception.getMessage().equals(title)) {
setErrorMessage(getRootCause(exception));
} else {
setErrorMessage(title, exception.getMessage());
}
}
/**
* Set error message to message tag with given name 'title'. Substitute all
* placeholders in message tag with elements of given array 'parameters'.
*
* <p>
* This method also accepts logger and exception instances to automatically log
* the exceptions message or stackTrace values to the given logger.
*
* @param title
* name of the message tag set as error message
* @param parameters
* list of parameters used for string substitution in message tag
* @param logger
* Logger instance for error logging
* @param exception
* Exception instance for error logging
*/
public static void setErrorMessage(String title, final Object[] parameters, Logger logger, Exception exception) {
logger.error(title, exception);
setErrorMessage(title, parameters);
}
/**
* Set error message to message tag with given name 'title'.
*
* <p>
* This method also accepts a description text and logger and exception
* instances to automatically log the exceptions message or stackTrace values to
* the given logger.
*
* @param title
* name of the message tag set as error message
* @param description
* description text that will be displayed in the faces message
* @param logger
* Logger instance for error logging
* @param exception
* Exception instance for error logging
*/
public static void setErrorMessage(String title, String description, Logger logger, Exception exception) {
logger.error(title, exception);
setErrorMessage(title, description);
}
/**
* Set error message with empty description, e.g. only with title. That means no compound message is created.
* This is a convenience function for calling "setMessage" with parameters "level" = "MessageLevel.ERROR" and
* "createCompoundMessage" = "false".
*
* @param message message String to be displayed.
*/
public static void setErrorMessagesWithoutDescription(String message) {
setMessage(null, message, "", MessageLevel.ERROR, false);
}
private static String getRootCause(Throwable problem) {
Throwable cause = problem.getCause();
String className = problem.getClass().getSimpleName();
if (Objects.nonNull(cause)) {
return className + " / " + getRootCause(cause);
} else {
String message = problem.getLocalizedMessage();
return StringUtils.isEmpty(message) ? className : className + ": " + message;
}
}
private static String getExceptionMessage(Throwable e) {
String message = e.getMessage();
if (Objects.isNull(message)) {
StringWriter sw = new StringWriter();
e.printStackTrace(new PrintWriter(sw));
message = sw.toString();
}
return message;
}
/**
* Set a message with warning level.
*
* @param message
* Message displayed to the user
*/
public static void setWarnMessage(String message) {
setMessage(null, message, "", MessageLevel.WARN);
}
/**
* Set message for user.
*
* @param message
* for user
*/
public static void setMessage(String message) {
setMessage(null, message, "", MessageLevel.INFO);
}
/**
* Set message and description for user.
*
* @param message
* for user
* @param description
* additional information to message
*/
public static void setMessage(String message, String description) {
setMessage(null, message, description, MessageLevel.INFO);
}
/**
* Set message and description for user.
*
* @param control
* what is it - no documentation for clientId in FacesContext
* @param message
* for user
* @param description
* additional information to message
*/
public static void setMessage(String control, String message, String description) {
setMessage(control, message, description, MessageLevel.INFO);
}
private static void setMessage(String control, String message, String description, MessageLevel level) {
setMessage(control, message, description, level, true);
}
/**
* Transfer an error message for a specific control to the current form.
*/
private static void setMessage(String control, String message, String description, MessageLevel level,
boolean createCompoundMessage) {
String msg = getTranslation(Objects.toString(message));
String descript = getTranslation(Objects.toString(description));
String detail = descript;
String compoundMessage = msg.replaceFirst(":\\s*$", "");
if (createCompoundMessage) {
if (StringUtils.isNotEmpty(descript)) {
compoundMessage += ": " + descript;
}
detail = null;
}
if (Objects.nonNull(activeMQReporting)) {
new WebServiceResult(activeMQReporting.get("queueName"), activeMQReporting.get("id"),
MessageLevel.ERROR.equals(level) ? ReportLevel.ERROR :
MessageLevel.WARN.equals(level) ? ReportLevel.WARN : ReportLevel.INFO, compoundMessage).send();
}
FacesContext context = FacesContext.getCurrentInstance();
if (Objects.nonNull(context)) {
context.addMessage(control,
new FacesMessage(MessageLevel.ERROR.equals(level) ? FacesMessage.SEVERITY_ERROR : MessageLevel.WARN.equals(level)
? FacesMessage.SEVERITY_WARN : FacesMessage.SEVERITY_INFO, compoundMessage, detail));
} else {
// wenn kein Kontext da ist, dann die Meldungen in Log
logger.log(MessageLevel.ERROR.equals(level) ? Level.ERROR : MessageLevel.WARN.equals(level) ? Level.WARN : Level.INFO,
compoundMessage);
}
}
/**
* Set message with empty description, e.g. only with title. That means no compound message is created.
* This is a convenience function for calling "setMessage" with parameters "level" = "MessageLevel.INFO" and
* "createCompoundMessage" = "false".
*
* @param message message String to be displayed.
*/
public static void setMessageWithoutDescription(String message) {
setMessage(null, message, "", MessageLevel.INFO, false);
}
/**
* Get String.
*
* @param locale
* Locale object
* @param key
* String
* @return String
*/
public static String getString(Locale locale, String key) {
if ((Objects.isNull(commonMessages) || commonMessages.size() <= 1)
&& (Objects.isNull(errorMessages) || errorMessages.size() <= 1)) {
loadMessages();
}
List<Map<Locale, ResourceBundle>> messages = new ArrayList<>();
messages.add(commonMessages);
messages.add(errorMessages);
for (Map<Locale, ResourceBundle> message : messages) {
// support locale with and without country to load message
Optional<Locale> optionalLocale = message.keySet().stream()
.filter(messageKeyLocale -> messageKeyLocale.getLanguage().equals(locale.getLanguage()))
.findFirst();
if (optionalLocale.isPresent()) {
String foundMessage = getTranslatedMessage(message, optionalLocale.get(), key);
if (StringUtils.isNotBlank(foundMessage)) {
return foundMessage;
}
}
}
return key;
}
private static String getTranslatedMessage(Map<Locale, ResourceBundle> messages, Locale locale, String key) {
ResourceBundle languageLocal = messages.get(locale);
if (languageLocal.containsKey(key)) {
return languageLocal.getString(key);
}
String lowKey = key.toLowerCase();
if (languageLocal.containsKey(lowKey)) {
return languageLocal.getString(lowKey);
}
return "";
}
/**
* Get date as formatted String.
*
* @param date
* Date object
* @return String
*/
public static String getDateAsFormattedString(Date date) {
if (Objects.isNull(date)) {
return "-";
} else {
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
return dateFormat.format(date);
}
}
/**
* Parse date string to date.
*
* <p>Reverse operation of `Helper.getDateAsFormattedString`.</p>
*
* @param date the date as string formatted in "yyyy-MM-dd HH:mm:ss"
* @return the date or null if it can not be parsed
*/
public static Date parseDateFromFormattedString(String date) {
if (Objects.isNull(date) || date.equals("")) {
return null;
}
try {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
LocalDateTime localDate = LocalDateTime.parse(date, formatter);
Instant instant = localDate.toInstant(ZoneOffset.UTC);
return Date.from(instant);
} catch (DateTimeParseException e) {
logger.info("invalid date format (yyyy-MM-dd HH:mm:ss) for date string: '" + date + "'");
return null;
}
}
/**
* Removes a managed bean from the faces
* context by name. If nothing such is available, nothing happens.
*
* @param name
* managed bean to remove
*/
public static void removeManagedBean(String name) {
try {
@SuppressWarnings("rawtypes")
Map sessionMap = FacesContext.getCurrentInstance().getExternalContext().getSessionMap();
sessionMap.remove(name);
} catch (RuntimeException nothingToDo) {
logger.error(nothingToDo);
}
}
private static void loadMessages() {
commonMessages = new HashMap<>();
errorMessages = new HashMap<>();
if (Objects.nonNull(FacesContext.getCurrentInstance())) {
Iterator<Locale> polyglot = FacesContext.getCurrentInstance().getApplication().getSupportedLocales();
while (polyglot.hasNext()) {
Locale language = polyglot.next();
commonMessages.put(language, Message.getResourceBundle("messages.messages", "messages", language));
errorMessages.put(language, Error.getResourceBundle("messages.errors", "errors", language));
}
} else {
Locale defaultLocale = new Locale("EN");
commonMessages.put(defaultLocale, Message.getResourceBundle("messages.messages", "messages", defaultLocale));
errorMessages.put(defaultLocale, Error.getResourceBundle("messages.errors", "errors", defaultLocale));
}
}
/**
* Get translation.
*
* @param title
* String
* @param insertions
* Strings
* @return translated String
*/
public static String getTranslation(String title, String... insertions) {
String pattern = getString(desiredLanguage(), title);
String message = pattern;
try {
message = MessageFormat.format(pattern, (Object[]) insertions);
} catch (IllegalArgumentException e) {
logger.catching(Level.WARN, e);
}
return appendUnusedInsertions(message, insertions);
}
/**
* Appends insertions that were not used. There are reasons why insertions
* are not used: if the key is not found in the messages, or if a curly
* bracket with the corresponding number is missing therein. Since this
* function is used in error messages, which could be difficult to
* reproduce, a loss of the additional information should be avoided.
*/
private static String appendUnusedInsertions(String message, String... insertions) {
StringBuilder messageBuilder = new StringBuilder(message);
for (String insertion : insertions) {
String separator = ": ";
if (!messageBuilder.toString().contains(insertion)) {
messageBuilder.append(separator).append(insertion);
}
}
message = messageBuilder.toString();
return message;
}
private static Locale desiredLanguage() {
if (Objects.nonNull(FacesContext.getCurrentInstance())) {
Locale desiredLanguage = FacesContext.getCurrentInstance().getViewRoot().getLocale();
if (Objects.nonNull(desiredLanguage)) {
return desiredLanguage;
}
}
return Locale.ENGLISH;
}
/**
* Set activeMQReporting.
*
* @param activeMQReporting
* as Map of Strings
*/
public static void setActiveMQReporting(Map<String, String> activeMQReporting) {
Helper.activeMQReporting = activeMQReporting;
}
/**
* Get title without white spaces.
*
* @param title
* of object
* @return title with '__' instead of ' '
*/
public static String getNormalizedTitle(String title) {
return title.replace(" ", "__");
}
/**
* Generate random string.
*
* @param length
* of random string to be created
* @return random string
*/
public static String generateRandomString(int length) {
final String AB = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
SecureRandom random = new SecureRandom();
StringBuilder sb = new StringBuilder(length);
for (int i = 0; i < length; i++) {
sb.append(AB.charAt(random.nextInt(AB.length())));
}
return sb.toString();
}
}