blob: d03e3cfdd1a96cccef1b875db455fd6f766b9740 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
package org.apache.sling.caconfig.management.impl.console;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Array;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import javax.servlet.Servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.felix.webconsole.AbstractWebConsolePlugin;
import org.apache.felix.webconsole.WebConsoleConstants;
import org.apache.sling.api.resource.LoginException;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.api.resource.ResourceResolverFactory;
import org.apache.sling.caconfig.management.ConfigurationData;
import org.apache.sling.caconfig.management.ConfigurationManager;
import org.apache.sling.caconfig.management.ValueInfo;
import org.apache.sling.caconfig.management.multiplexer.ContextPathStrategyMultiplexer;
import org.apache.sling.caconfig.resource.spi.ContextResource;
import org.osgi.framework.Constants;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
import org.osgi.service.component.annotations.ReferencePolicyOption;
import org.owasp.encoder.Encode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Web console plugin to test configuration resolution.
*/
@Component(service=Servlet.class,
property={Constants.SERVICE_DESCRIPTION + "=Apache Sling Context-Aware Configuration Web Console Plugin",
WebConsoleConstants.PLUGIN_LABEL + "=" + ConfigurationWebConsolePlugin.LABEL,
WebConsoleConstants.PLUGIN_TITLE + "=" + ConfigurationWebConsolePlugin.TITLE,
WebConsoleConstants.PLUGIN_CATEGORY + "=Sling"})
@SuppressWarnings("serial")
public class ConfigurationWebConsolePlugin extends AbstractWebConsolePlugin {
public static final String LABEL = "slingcaconfig";
public static final String TITLE = "Context-Aware Configuration";
private static final Logger log = LoggerFactory.getLogger(ConfigurationWebConsolePlugin.class);
@Reference(policyOption = ReferencePolicyOption.GREEDY)
private ResourceResolverFactory resolverFactory;
@Reference(policyOption = ReferencePolicyOption.GREEDY)
private ConfigurationManager configurationManager;
@Reference(policyOption = ReferencePolicyOption.GREEDY)
private ContextPathStrategyMultiplexer contextPathStrategyMultiplexer;
@Override
public String getLabel() {
return LABEL;
}
@Override
public String getTitle() {
return TITLE;
}
@Override
protected void renderContent(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
final PrintWriter pw = response.getWriter();
info(pw, "Configurations are managed in the resource tree. Use this tool to test configuration resolutions.");
pw.println("<br/>");
printResolutionTestTool(request, pw);
}
private String getParameter(final HttpServletRequest request, final String name, final String defaultValue) {
String value = request.getParameter(name);
if ( value != null && !value.trim().isEmpty() ) {
return value.trim();
}
return defaultValue;
}
private void printResolutionTestTool(HttpServletRequest request, PrintWriter pw) {
final String path = this.getParameter(request, "path", "/content");
String configNameOther = this.getParameter(request, "configNameOther", null);
String configName = this.getParameter(request, "configName", null);
if (configName == null) {
configName = configNameOther;
}
else {
configNameOther = null;
}
final boolean resourceCollection = BooleanUtils.toBoolean(this.getParameter(request, "resourceCollection", "false"));
ResourceResolver resolver = null;
try {
Resource contentResource = null;
if (path != null) {
resolver = getResolver(request);
if (resolver != null) {
contentResource = resolver.getResource(path);
}
}
pw.println("<form method='get'>");
tableStart(pw, "Test Configuration Resolution", 2);
String alertMessage = null;
if (path != null) {
if (resolver == null) {
alertMessage = "Unable to access repository - please check system configuration.";
}
else if (contentResource == null) {
alertMessage = "Path does not exist.";
}
}
textField(pw, "Content Path", "path", path, alertMessage);
tableRows(pw);
selectField(pw, "Config Name", "configName", configName, configurationManager.getConfigurationNames());
tableRows(pw);
textField(pw, "Other Config Name", "configNameOther", configNameOther);
tableRows(pw);
checkboxField(pw, "Resource collection", "resourceCollection", resourceCollection);
tableRows(pw);
pw.println("<td></td>");
pw.println("<td><input type='submit' value='Resolve'/></td>");
tableEnd(pw);
pw.println("</form>");
pw.println("<br/>");
if (contentResource != null && configName != null) {
// context paths
Iterator<ContextResource> contextResources = contextPathStrategyMultiplexer.findContextResources(contentResource);
tableStart(pw, "Context paths", 3);
pw.println("<th>Context path</th>");
pw.println("<th>Config reference</th>");
pw.println("<th>Ranking</th>");
while (contextResources.hasNext()) {
ContextResource contextResource = contextResources.next();
tableRows(pw);
pw.println("<td>" + Encode.forHtmlContent(contextResource.getResource().getPath()) + "</td>");
pw.println("<td>" + Encode.forHtmlContent(contextResource.getConfigRef()) + "</td>");
pw.println("<td>" + contextResource.getServiceRanking() + "</td>");
}
tableEnd(pw);
pw.println("<br/>");
// resolve configuration
Collection<ConfigurationData> configDatas;
if (resourceCollection) {
configDatas = configurationManager.getConfigurationCollection(contentResource, configName).getItems();
}
else {
ConfigurationData configData = configurationManager.getConfiguration(contentResource, configName);
if (configData != null) {
configDatas = Collections.singletonList(configData);
}
else {
configDatas = Collections.emptyList();
}
}
tableStart(pw, "Result", 6);
if (configDatas.size() == 0) {
pw.println("<td colspan='6'>");
alertDiv(pw, "No matching item found.");
pw.println("<br/>&nbsp;</td>");
}
else {
pw.println("<th>Property</th>");
pw.println("<th>Effective Value</th>");
pw.println("<th>Value</th>");
pw.println("<th>Default</th>");
pw.println("<th>Inherited</th>");
pw.println("<th>Overwritten</th>");
for (ConfigurationData data : configDatas) {
tableRows(pw);
pw.println("<td colspan='6' style='background-color:#f3f3f3'>");
pw.print("Path: " + Encode.forHtmlContent(data.getResourcePath()));
pw.println("</td>");
for (String propertyName : data.getPropertyNames()) {
ValueInfo<?> valueInfo = data.getValueInfo(propertyName);
if (valueInfo == null) {
continue;
}
tableRows(pw);
td(pw, propertyName);
td(pw, valueInfo.getEffectiveValue());
td(pw, valueInfo.getValue());
td(pw, valueInfo.isDefault());
String title = null;
if (valueInfo.isInherited()) {
title = "Source path: " + valueInfo.getConfigSourcePath();
}
td(pw, valueInfo.isInherited(), title);
td(pw, valueInfo.isOverridden());
}
}
}
tableEnd(pw);
}
}
finally {
if (resolver != null) {
resolver.close();
}
}
}
private void info(PrintWriter pw, String text) {
pw.print("<p class='statline ui-state-highlight'>");
pw.print(Encode.forHtmlContent(text));
pw.println("</p>");
}
private void tableStart(PrintWriter pw, String title, int colspan) {
pw.println("<table class='nicetable ui-widget'>");
pw.println("<thead class='ui-widget-header'>");
pw.println("<tr>");
pw.print("<th colspan=");
pw.print(String.valueOf(colspan));
pw.print(">");
pw.print(Encode.forHtmlContent(title));
pw.println("</th>");
pw.println("</tr>");
pw.println("</thead>");
pw.println("<tbody class='ui-widget-content'>");
pw.println("<tr>");
}
private void tableEnd(PrintWriter pw) {
pw.println("</tr>");
pw.println("</tbody>");
pw.println("</table>");
}
private void tableRows(PrintWriter pw) {
pw.println("</tr>");
pw.println("<tr>");
}
private void textField(PrintWriter pw, String label, String fieldName, String value, String... alertMessages) {
pw.print("<td style='width:20%'>");
pw.print(Encode.forHtmlContent(label));
pw.println("</td>");
pw.print("<td><input name='");
pw.print(Encode.forHtmlAttribute(fieldName));
pw.print("' value='");
pw.print(Encode.forHtmlAttribute(StringUtils.defaultString(value)));
pw.print("' style='width:100%'/>");
for (String alertMessage : alertMessages) {
alertDiv(pw, alertMessage);
}
pw.println("</td>");
}
private void selectField(PrintWriter pw, String label, String fieldName, String value, Collection<String> options) {
pw.print("<td style='width:20%'>");
pw.print(Encode.forHtmlContent(label));
pw.println("</td>");
pw.print("<td><select name='");
pw.print(Encode.forHtmlAttribute(fieldName));
pw.print("' style='width:100%'>");
pw.print("<option value=''>(please select)</option>");
for (String option : options) {
pw.print("<option");
if (StringUtils.equals(option, value)) {
pw.print(" selected");
}
pw.print(">");
pw.print(Encode.forHtmlAttribute(option));
pw.print("</option>");
}
pw.print("</select>");
pw.println("</td>");
}
private void checkboxField(PrintWriter pw, String label, String fieldName, boolean checked) {
pw.print("<td style='width:20%'>");
pw.print(Encode.forHtmlContent(label));
pw.println("</td>");
pw.print("<td><input type='checkbox' name='");
pw.print(Encode.forHtmlAttribute(fieldName));
pw.print("' value='true'");
if (checked) {
pw.print(" checked");
}
pw.print("/></td>");
}
private void alertDiv(PrintWriter pw, String text) {
if (StringUtils.isBlank(text)) {
return;
}
pw.println("<div>");
pw.println("<span class='ui-icon ui-icon-alert' style='float:left'></span>");
pw.print("<span style='float:left'>");
pw.print(Encode.forHtmlContent(text));
pw.println("</span>");
pw.println("</div>");
}
private void td(PrintWriter pw, Object value, String... title) {
pw.print("<td");
if (title.length > 0 && !StringUtils.isBlank(title[0])) {
pw.print(" title='");
pw.print(Encode.forHtmlAttribute(title[0]));
pw.print("'");
}
pw.print(">");
if (value != null) {
if (value.getClass().isArray()) {
for (int i = 0; i < Array.getLength(value); i++) {
Object itemValue = Array.get(value, i);
pw.print(Encode.forHtmlContent(ObjectUtils.defaultIfNull(itemValue, "").toString()));
pw.println("<br>");
}
}
else {
pw.print(Encode.forHtmlContent(value.toString()));
}
}
if (title.length > 0 && !StringUtils.isBlank(title[0])) {
pw.print("<span class='ui-icon ui-icon-info' style='float:left'></span>");
}
pw.print("</td>");
}
private ResourceResolver getResolver(HttpServletRequest request) {
ResourceResolver resolver = null;
try {
resolver = resolverFactory.getServiceResourceResolver(null);
}
catch (final LoginException ex) {
// fallback if no service user is registered - try to get current web console resource resolver
resolver = (ResourceResolver)request.getAttribute("org.apache.sling.auth.core.ResourceResolver");
if (resolver == null) {
log.warn("Unable to get resource resolver - please ensure a system user is configured: {}", ex.getMessage());
}
else {
log.debug("No system user configured, use resource resolver from web console.");
}
}
return resolver;
}
}