org.ff4j.web.embedded.ConsoleRenderer Maven / Gradle / Ivy
package org.ff4j.web.embedded;
import static org.ff4j.web.embedded.ConsoleConstants.CONTENT_TYPE_CSS;
import static org.ff4j.web.embedded.ConsoleConstants.CONTENT_TYPE_HTML;
import static org.ff4j.web.embedded.ConsoleConstants.CONTENT_TYPE_JS;
import static org.ff4j.web.embedded.ConsoleConstants.FEATID;
import static org.ff4j.web.embedded.ConsoleConstants.KEY_ALERT_MESSAGE;
import static org.ff4j.web.embedded.ConsoleConstants.KEY_AUDIT_ROWS;
import static org.ff4j.web.embedded.ConsoleConstants.KEY_FEATURE_ROWS;
import static org.ff4j.web.embedded.ConsoleConstants.KEY_GROUP_LIST_CREATE;
import static org.ff4j.web.embedded.ConsoleConstants.KEY_GROUP_LIST_EDIT;
import static org.ff4j.web.embedded.ConsoleConstants.KEY_GROUP_LIST_TOGGLE;
import static org.ff4j.web.embedded.ConsoleConstants.KEY_PERMISSIONLIST;
import static org.ff4j.web.embedded.ConsoleConstants.KEY_PROPERTIES_ROWS;
import static org.ff4j.web.embedded.ConsoleConstants.KEY_SERVLET_CONTEXT;
import static org.ff4j.web.embedded.ConsoleConstants.KEY_VERSION;
import static org.ff4j.web.embedded.ConsoleConstants.MODAL_CREATE;
import static org.ff4j.web.embedded.ConsoleConstants.MODAL_EDIT;
import static org.ff4j.web.embedded.ConsoleConstants.MODAL_TOGGLE;
import static org.ff4j.web.embedded.ConsoleConstants.NEW_LINE;
import static org.ff4j.web.embedded.ConsoleConstants.OP_RMV_FEATURE;
import static org.ff4j.web.embedded.ConsoleConstants.OP_RMV_PROPERTY;
import static org.ff4j.web.embedded.ConsoleConstants.PREFIX_CHECKBOX;
import static org.ff4j.web.embedded.ConsoleConstants.RESOURCE;
import static org.ff4j.web.embedded.ConsoleConstants.RESOURCE_CSS_FILE;
import static org.ff4j.web.embedded.ConsoleConstants.RESOURCE_CSS_PARAM;
import static org.ff4j.web.embedded.ConsoleConstants.RESOURCE_JS_FILE;
import static org.ff4j.web.embedded.ConsoleConstants.RESOURCE_JS_PARAM;
import static org.ff4j.web.embedded.ConsoleConstants.TEMPLATE_FILE;
import static org.ff4j.web.embedded.ConsoleConstants.TEMPLATE_FILE_MONITORING;
import static org.ff4j.web.embedded.ConsoleConstants.UTF8_ENCODING;
import java.io.IOException;
/*
* #%L AdministrationConsoleRenderer.java (ff4j-web) by Cedrick LUNVEN %% Copyright (C) 2013 Ff4J %% 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. #L%
*/
import java.io.InputStream;
import java.io.PrintWriter;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.ff4j.FF4j;
import org.ff4j.audit.Event;
import org.ff4j.audit.EventQueryDefinition;
import org.ff4j.audit.repository.EventRepository;
import org.ff4j.core.Feature;
import org.ff4j.core.FlippingStrategy;
import org.ff4j.property.Property;
import org.ff4j.property.PropertyBigDecimal;
import org.ff4j.property.PropertyBigInteger;
import org.ff4j.property.PropertyBoolean;
import org.ff4j.property.PropertyByte;
import org.ff4j.property.PropertyDouble;
import org.ff4j.property.PropertyFloat;
import org.ff4j.property.PropertyInt;
import org.ff4j.property.PropertyLogLevel;
import org.ff4j.property.PropertyLong;
import org.ff4j.property.PropertyShort;
import org.ff4j.property.PropertyString;
import org.ff4j.utils.Util;
/**
* Used to build GUI Interface for feature flip servlet. It contains gui component render and parmeters
*
* @author Cedrick LUNVEN
*/
public final class ConsoleRenderer {
/** Cache for page blocks. */
private static String htmlTemplate = null;
/** Cache for page blocks. */
private static String htmlTemplateMonitoring = null;
/** Load CSS. */
private static String cssContent = null;
/** Load JS. */
private static String jsContent = null;
/** Cache for page blocks. */
static final String TABLE_FEATURES_FOOTER = "" + "";
/** fin de ligne. **/
static final String END_OF_LINE = "\r\n";
/** Get version of the component. */
static final String FF4J_VERSION = ConsoleRenderer.class.getPackage().getImplementationVersion();
/** Display audit log date. */
static final SimpleDateFormat SDF = new SimpleDateFormat("yyyy-dd-MM HH:mm:ss");
/** Mapping from simple 'String' <=> 'org.ff4j.property.PropertyString'. */
private static Map < String , String > uxTypes = new HashMap< String , String>();
/**
* Initialized Primitive to work with Properties.
*/
static {
uxTypes.put(Byte.class.getSimpleName(), PropertyByte.class.getName());
uxTypes.put(Short.class.getSimpleName(), PropertyShort.class.getName());
uxTypes.put(Integer.class.getSimpleName(), PropertyInt.class.getName());
uxTypes.put(Long.class.getSimpleName(), PropertyLong.class.getName());
uxTypes.put(Double.class.getSimpleName(), PropertyDouble.class.getName());
uxTypes.put(Boolean.class.getSimpleName(), PropertyBoolean.class.getName());
uxTypes.put(Float.class.getSimpleName(), PropertyFloat.class.getName());
uxTypes.put(BigInteger.class.getSimpleName(), PropertyBigInteger.class.getName());
uxTypes.put(BigDecimal.class.getSimpleName(), PropertyBigDecimal.class.getName());
uxTypes.put("LogLevel", PropertyLogLevel.class.getName());
uxTypes.put(String.class.getSimpleName(), PropertyString.class.getName());
}
private ConsoleRenderer() {}
/**
* Render the ff4f console webpage through different block.
*
* @param req
* http request (with parameters)
* @param res
* http response (with outouput test)
* @param message
* text in the information box (blue/green/orange/red)
* @param messagetype
* type of informatice message (info,success,warning,error)
* @throws IOException
* error during populating http response
*/
public static void renderPage(FF4j ff4j, HttpServletRequest req, HttpServletResponse res, String msg, String msgType) throws IOException {
res.setContentType(CONTENT_TYPE_HTML);
PrintWriter out = res.getWriter();
// Header of the page
String htmlContent = renderTemplate(req);
// Subsctitution MESSAGE BOX
htmlContent = htmlContent.replaceAll("\\{" + KEY_ALERT_MESSAGE + "\\}", renderMessageBox(msg, msgType));
// Subsctitution FEATURE_ROWS
try {
htmlContent = htmlContent.replaceAll("\\{" + KEY_FEATURE_ROWS + "\\}", renderFeatureRows(ff4j, req));
} catch(IllegalArgumentException ieo) {
htmlContent = htmlContent.replaceAll("\\{" + KEY_FEATURE_ROWS + "\\}",
"Cannot render Features please check names (no $) '" + ieo.getMessage() + "'");
}
// substitution PROPERTIES_ROWS
try {
htmlContent = htmlContent.replaceAll("\\{" + KEY_PROPERTIES_ROWS + "\\}", renderPropertiesRows(ff4j, req));
} catch(IllegalArgumentException ieo) {
htmlContent = htmlContent.replaceAll("\\{" + KEY_PROPERTIES_ROWS + "\\}",
"Cannot render propertie please check names (no $) '" + ieo.getMessage() + "'");
}
// Substitution GROUP_LIST
String groups = ConsoleRenderer.renderGroupList(ff4j, MODAL_EDIT);
htmlContent = htmlContent.replaceAll("\\{" + KEY_GROUP_LIST_EDIT + "\\}", groups);
groups = groups.replaceAll(MODAL_EDIT, MODAL_CREATE);
htmlContent = htmlContent.replaceAll("\\{" + KEY_GROUP_LIST_CREATE + "\\}", groups);
groups = groups.replaceAll(MODAL_CREATE, MODAL_TOGGLE);
htmlContent = htmlContent.replaceAll("\\{" + KEY_GROUP_LIST_TOGGLE + "\\}", groups);
// Substitution PERMISSIONS
final String permissions = renderPermissionList(ff4j);
htmlContent = htmlContent.replaceAll("\\{" + KEY_PERMISSIONLIST + "\\}", permissions);
out.println(htmlContent);
}
/**
* Render the ff4f console webpage through different block.
*
* @param req
* http request (with parameters)
* @param res
* http response (with outouput test)
* @param msg
* text in the information box (blue/green/orange/red)
* @param msgType
* type of informatice message (info,success,warning,error)
* @throws IOException
* error during populating http response
*/
public static void renderPageMonitoring(FF4j ff4j, HttpServletRequest req, HttpServletResponse res, String msg, String msgType) throws IOException {
res.setContentType(CONTENT_TYPE_HTML);
PrintWriter out = res.getWriter();
String htmlContent = renderTemplateMonitoring(req);
htmlContent = htmlContent.replaceAll("\\{" + KEY_ALERT_MESSAGE + "\\}", renderMessageBox(msg, msgType));
htmlContent = htmlContent.replaceAll("\\{" + KEY_AUDIT_ROWS + "\\}", renderAuditRows(ff4j , req));
out.println(htmlContent);
}
/**
* Build info messages.
*
* @param featureName
* target feature name
* @param operationId
* target operationId
* @return
*/
public static String msg(String featureName, String operationId) {
return String.format("Feature %s has been successfully %s", featureName, operationId);
}
/**
* Build info messages.
*
* @param featureName
* target feature name
* @param operationId
* target operationId
* @return
*/
public static String renderMsgProperty(String featureName, String operationId) {
return String.format("Property %s has been successfully %s", featureName, operationId);
}
/**
* Build info messages.
*
* @param groupName
* target group name
* @param operationId
* target operationId
* @return
*/
public static String renderMsgGroup(String groupName, String operationId) {
return String.format("Group %s has been successfully %s", groupName, operationId);
}
/**
* Deliver CSS and Javascript files/
*
* @param req
* request
* @param res
* response
* @return value for resources
* @throws IOException
* exceptions
*/
public static boolean renderResources(HttpServletRequest req, HttpServletResponse res) throws IOException {
// Serve static resource file as CSS and Javascript
String resources = req.getParameter(RESOURCE);
if (resources != null && !resources.isEmpty()) {
if (RESOURCE_CSS_PARAM.equalsIgnoreCase(resources)) {
res.setContentType(CONTENT_TYPE_CSS);
res.getWriter().println(ConsoleRenderer.getCSS());
return true;
} else if (RESOURCE_JS_PARAM.equalsIgnoreCase(resources)) {
res.setContentType(CONTENT_TYPE_JS);
res.getWriter().println(ConsoleRenderer.getJS());
return true;
}
}
return false;
}
/**
* Display message box if message.
*
* @param message
* target message to display
* @param type
* type of messages
* @return html content to be displayed as message
*/
public static String renderMessageBox(String message, String type) {
StringBuilder sb = new StringBuilder();
// Display Message box
if (message != null && !message.isEmpty()) {
sb.append("");
sb.append("");
sb.append("");
sb.append(message);
sb.append("");
sb.append("");
}
return sb.toString();
}
/**
* Load HTML template file and substitute by current URL context path
*
* @param req
* current http request
* @return current text part as string
*/
private static final String renderTemplate(HttpServletRequest req) {
if (htmlTemplate == null || htmlTemplate.isEmpty()) {
String ctx = req.getContextPath() + req.getServletPath() + "";
htmlTemplate = loadFileAsString(TEMPLATE_FILE);
htmlTemplate = htmlTemplate.replaceAll("\\{" + KEY_SERVLET_CONTEXT + "\\}", ctx);
htmlTemplate = htmlTemplate.replaceAll("\\{" + KEY_VERSION + "\\}", FF4J_VERSION);
}
return htmlTemplate;
}
/**
* Load HTML template file and substitute by current URL context path
*
* @param req
* current http request
* @return current text part as string
*/
private static final String renderTemplateMonitoring(HttpServletRequest req) {
if (htmlTemplateMonitoring == null || htmlTemplateMonitoring.isEmpty()) {
String ctx = req.getContextPath() + req.getServletPath() + "";
htmlTemplateMonitoring = loadFileAsString(TEMPLATE_FILE_MONITORING);
htmlTemplateMonitoring = htmlTemplateMonitoring.replaceAll("\\{" + KEY_SERVLET_CONTEXT + "\\}", ctx);
htmlTemplateMonitoring = htmlTemplateMonitoring.replaceAll("\\{" + KEY_VERSION + "\\}", FF4J_VERSION);
}
return htmlTemplateMonitoring;
}
public static String renderValue(String source, int column) {
StringBuilder sb = new StringBuilder();
source = source.replaceAll("\\\\", "/");
source = source.replaceAll("\\$", "$");
while (source.length() > column) {
sb.append(source.substring(0, column));
sb.append("\r\n
");
source = source.substring(column);
}
sb.append(source);
return sb.toString();
}
private static final String renderPropertiesRows(FF4j ff4j, HttpServletRequest req) {
StringBuilder sb = new StringBuilder();
final Map < String, Property>> mapOfProperties = ff4j.getProperties();
for(Map.Entry> uid : mapOfProperties.entrySet()) {
Property> currentProperty = uid.getValue();
sb.append("" + END_OF_LINE);
// Column with uid and description as tooltip
sb.append("");
sb.append(renderValue(currentProperty.getName(), 50));
sb.append("");
// Colonne Value
sb.append(" ");
if (null != currentProperty.asString()) {
sb.append(renderValue(currentProperty.asString(), 60));
} else {
sb.append("--");
}
// Colonne Type
sb.append(" ");
if (uxTypes.containsValue(currentProperty.getType())) {
sb.append(Util.getFirstKeyByValue(uxTypes, currentProperty.getType()));
} else {
sb.append(currentProperty.getType());
}
// Colonne Fixed Value
sb.append(" ");
if (null != currentProperty.getFixedValues()) {
for (Object o : currentProperty.getFixedValues()) {
sb.append("" + o.toString());
}
} else {
sb.append("--");
}
// Colonne Button Edit
sb.append(" ");
sb.append("");
sb.append("");
// Colonne Button Delete
sb.append(" ");
sb.append("");
sb.append("");
sb.append("");
sb.append(" ");
}
return sb.toString();
}
private static final String renderAuditRows(FF4j ff4j, HttpServletRequest req) {
StringBuilder sb = new StringBuilder();
EventRepository er = ff4j.getEventRepository();
EventQueryDefinition query = new EventQueryDefinition();
for (Event event : er.searchFeatureUsageEvents(query)) {
sb.append("" + END_OF_LINE);
sb.append("" + SDF.format(new Date(event.getTimestamp())) + " ");
sb.append("" + event.getType() + " ");
sb.append("" + event.getName() + " ");
sb.append("" + event.getAction() + " ");
sb.append(" ");
}
return sb.toString();
}
/**
* Produce the rows of the Feature Table.
*
* @param ff4j
* target ff4j.
* @param req
* current http request
* @return string representing the list of features
*/
private static final String renderFeatureRows(FF4j ff4j, HttpServletRequest req) {
StringBuilder sb = new StringBuilder();
final Map < String, Feature> mapOfFeatures = ff4j.getFeatures();
for(Map.Entry uid : mapOfFeatures.entrySet()) {
Feature currentFeature = uid.getValue();
sb.append("" + END_OF_LINE);
// Column with uid and description as tooltip
sb.append("");
sb.append(currentFeature.getUid());
sb.append("");
// Colonne Group
sb.append(" ");
if (null != currentFeature.getGroup()) {
sb.append(currentFeature.getGroup());
} else {
sb.append("--");
}
// Colonne Permissions
sb.append(" ");
Set < String > permissions = currentFeature.getPermissions();
if (null != permissions && !permissions.isEmpty()) {
boolean first = true;
for (String perm : permissions) {
if (!first) {
sb.append(",");
}
sb.append(perm);
first = false;
}
} else {
sb.append("--");
}
// Colonne Strategy
sb.append(" ");
FlippingStrategy fs = currentFeature.getFlippingStrategy();
if (null != fs) {
sb.append(renderValue(fs.getClass().getName(), 50));
if (fs.getInitParams() != null) {
for (Map.Entry entry : fs.getInitParams().entrySet()) {
sb.append("" + renderValue(entry.getKey() + " = " + entry.getValue(), 40));
}
}
} else {
sb.append("--");
}
// Colonne 'Holy' Toggle
sb.append(" ");
sb.append("");
// Colonne Button Edit
sb.append(" ");
sb.append("");
sb.append("");
// Colonne Button Delete
sb.append(" ");
sb.append("");
sb.append("");
sb.append("");
sb.append(" ");
}
return sb.toString();
}
/**
* Render group list block.
*
* @param ff4j
* target ff4j.
* @return list of group
*/
private static String renderGroupList(FF4j ff4j, String modalId) {
StringBuilder sb = new StringBuilder();
if (null != ff4j.getFeatureStore().readAllGroups()) {
for (String group : ff4j.getFeatureStore().readAllGroups()) {
sb.append("");
sb.append(group);
sb.append(" ");
}
}
return sb.toString();
}
/**
* Render a permission list.
*
* @param ff4j
* reference to curent ff4j instance
* @return string representing the list of permissions
*/
private static String renderPermissionList(FF4j ff4j) {
StringBuilder sb = new StringBuilder("
");
if (null != ff4j.getAuthorizationsManager()) {
for (String permission : ff4j.getAuthorizationsManager().listAllPermissions()) {
sb.append("\r\n
");
sb.append(permission);
}
}
return sb.toString();
}
/**
* Load the CSS File As String.
*
* @return CSS File
*/
private static final String getCSS() {
if (null == cssContent) {
cssContent = loadFileAsString(RESOURCE_CSS_FILE);
}
return cssContent;
}
/**
* Load the JS File As String.
*
* @return JS File
*/
private static final String getJS() {
if (null == jsContent) {
jsContent = loadFileAsString(RESOURCE_JS_FILE);
}
return jsContent;
}
/**
* Utils method to load a file as String.
*
* @param fileName
* target file Name.
* @return target file content as String
*/
private static String loadFileAsString(String fileName) {
InputStream in = ConsoleRenderer.class.getClassLoader().getResourceAsStream(fileName);
if (in == null) {
throw new IllegalArgumentException("Cannot load file " + fileName + " from classpath");
}
Scanner currentScan = null;
StringBuilder strBuilder = new StringBuilder();
try {
currentScan = new Scanner(in, UTF8_ENCODING);
while (currentScan.hasNextLine()) {
strBuilder.append(currentScan.nextLine());
strBuilder.append(NEW_LINE);
}
} finally {
if (currentScan != null) {
currentScan.close();
}
}
return strBuilder.toString();
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy