blob: f932c380162711470d01249dc700435f697830a0 [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.felix.webconsole.internal.configuration;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Locale;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.felix.utils.json.JSONWriter;
import org.apache.felix.webconsole.DefaultVariableResolver;
import org.apache.felix.webconsole.SimpleWebConsolePlugin;
import org.apache.felix.webconsole.WebConsoleUtil;
import org.apache.felix.webconsole.internal.OsgiManagerPlugin;
import org.osgi.framework.Constants;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.service.cm.Configuration;
/**
* The <code>ConfigManager</code> class is the Web Console plugin to
* manage configurations.
*/
public class ConfigManager extends SimpleWebConsolePlugin implements OsgiManagerPlugin
{
private static final long serialVersionUID = 5021174538498622428L;
private static final String LABEL = "configMgr"; // was name //$NON-NLS-1$
private static final String TITLE = "%configMgr.pluginTitle"; //$NON-NLS-1$
private static final String CSS[] = { "/res/ui/config.css" }; //$NON-NLS-1$
static final String PID_FILTER = "pidFilter"; //$NON-NLS-1$
static final String PID = "pid"; //$NON-NLS-1$
static final String FACTORY_PID = "factoryPid"; //$NON-NLS-1$
static final String PLACEHOLDER_PID = "[Temporary PID replaced by real PID upon save]"; //$NON-NLS-1$
static final String REFERER = "referer"; //$NON-NLS-1$
static final String FACTORY_CREATE = "factoryCreate"; //$NON-NLS-1$
static final String ACTION_CREATE = "create"; //$NON-NLS-1$
static final String ACTION_DELETE = "delete"; //$NON-NLS-1$
static final String ACTION_APPLY = "apply"; //$NON-NLS-1$
static final String ACTION_UNBIND = "unbind"; //$NON-NLS-1$
static final String PROPERTY_LIST = "propertylist"; //$NON-NLS-1$
static final String LOCATION = "$location"; //$NON-NLS-1$
static final String CONFIGURATION_ADMIN_NAME = "org.osgi.service.cm.ConfigurationAdmin"; //$NON-NLS-1$
static final String META_TYPE_NAME = "org.osgi.service.metatype.MetaTypeService"; //$NON-NLS-1$
public static final String UNBOUND_LOCATION = "??unbound:bundle/location"; //$NON-NLS-1$
// templates
private final String TEMPLATE;
/** Default constructor */
public ConfigManager()
{
super(LABEL, TITLE, CATEGORY_OSGI, CSS);
// load templates
TEMPLATE = readTemplateFile( "/templates/config.html" ); //$NON-NLS-1$
}
static final boolean isAllowedPid(final String pid)
{
for(int i = 0; i < pid.length(); i++)
{
final char c = pid.charAt(i);
if ( c == '&' || c == '<' || c == '>' || c == '"' || c == '\'' )
{
return false;
}
}
return true;
}
private static final Locale getLocale( HttpServletRequest request )
{
try
{
return request.getLocale();
}
catch ( Throwable t )
{
// expected in standard OSGi Servlet 2.1 environments
// fallback to using the default locale
return Locale.getDefault();
}
}
/**
* @see javax.servlet.http.HttpServlet#doPost(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
*/
protected void doPost( HttpServletRequest request, HttpServletResponse response ) throws IOException
{
// needed multiple times below
String pid = request.getParameter( ConfigManager.PID );
if ( pid == null )
{
String info = request.getPathInfo();
pid = WebConsoleUtil.urlDecode( info.substring( info.lastIndexOf( '/' ) + 1 ) );
}
// the filter to select part of the configurations
String pidFilter = request.getParameter( PID_FILTER );
final ConfigAdminSupport cas = getConfigurationAdminSupport();
// ignore this request if the PID and/or configuration admin is missing
if ( pid == null || pid.length() == 0 || cas == null )
{
// should log this here !!
return;
}
// ignore this request, if the PID is invalid
if ( ! isAllowedPid(pid) )
{
response.sendError(500);
return;
}
if ( pidFilter != null && ! isAllowedPid(pidFilter) )
{
response.sendError(500);
return;
}
// the configuration to operate on (to be created or "missing")
Configuration config = null;
// should actually apply the configuration before redirecting
if ( request.getParameter( ACTION_CREATE ) != null )
{
config = cas.getPlaceholderConfiguration( pid ); // ca.createFactoryConfiguration( pid, null );
pid = config.getPid();
}
else if ( request.getParameter( ACTION_APPLY ) != null )
{
String redirect = cas.applyConfiguration( request, pid );
if ( redirect != null )
{
if (pidFilter != null) {
redirect += '?' + PID_FILTER + '=' + pidFilter;
}
WebConsoleUtil.sendRedirect(request, response, redirect);
}
else
{
response.setContentType( "application/json" ); //$NON-NLS-1$
response.setCharacterEncoding( "UTF-8" ); //$NON-NLS-1$
response.getWriter().print( "{ \"status\": true }" ); //$NON-NLS-1$
}
return;
}
if ( config == null )
{
config = cas.getConfiguration( pid );
}
// check for configuration unbinding
if ( request.getParameter( ACTION_UNBIND ) != null )
{
if ( config != null && config.getBundleLocation() != null )
{
config.setBundleLocation( UNBOUND_LOCATION );
}
response.setContentType( "application/json" ); //$NON-NLS-1$
response.setCharacterEncoding( "UTF-8" ); //$NON-NLS-1$
response.getWriter().print( "{ \"status\": true }" ); //$NON-NLS-1$
return;
}
// send the result
response.setContentType( "application/json" ); //$NON-NLS-1$
response.setCharacterEncoding( "UTF-8" ); //$NON-NLS-1$
final Locale loc = getLocale( request );
final String locale = ( loc != null ) ? loc.toString() : null;
cas.printConfigurationJson( response.getWriter(), pid, config, pidFilter, locale );
}
/**
* @see org.apache.felix.webconsole.AbstractWebConsolePlugin#doGet(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
*/
protected void doGet( HttpServletRequest request, HttpServletResponse response )
throws ServletException, IOException
{
// check for "post" requests from previous versions
if ( "true".equals(request.getParameter("post")) ) { //$NON-NLS-1$ //$NON-NLS-2$
this.doPost(request, response);
return;
}
final String info = request.getPathInfo();
// let's check for a JSON request
if ( info.endsWith( ".json" ) ) //$NON-NLS-1$
{
response.setContentType( "application/json" ); //$NON-NLS-1$
response.setCharacterEncoding( "UTF-8" ); //$NON-NLS-1$
// after last slash and without extension
String pid = info.substring( info.lastIndexOf( '/' ) + 1, info.length() - 5 );
// check whether the PID is actually a filter for the selection
// of configurations to display, if the filter correctly converts
// into an OSGi filter, we use it to select configurations
// to display
String pidFilter = request.getParameter( PID_FILTER );
if ( pidFilter == null )
{
pidFilter = pid;
}
try
{
getBundleContext().createFilter( pidFilter );
// if the pidFilter was set from the PID, clear the PID
if ( pid == pidFilter )
{
pid = null;
}
}
catch ( InvalidSyntaxException ise )
{
// its OK, if the PID is just a single PID
pidFilter = null;
}
// check both PID and PID filter
if ( pid != null && !isAllowedPid(pid) )
{
response.sendError(500);
}
if ( pidFilter != null && !isAllowedPid(pidFilter) )
{
response.sendError(500);
}
final Locale loc = getLocale( request );
final String locale = ( loc != null ) ? loc.toString() : null;
final PrintWriter pw = response.getWriter();
pw.write( "[" ); //$NON-NLS-1$
final ConfigAdminSupport ca = this.getConfigurationAdminSupport();
if ( ca != null )
{
// create filter
final StringBuffer sb = new StringBuffer();
if ( pid != null && pidFilter != null)
{
sb.append("(&"); //$NON-NLS-1$
}
if ( pid != null )
{
sb.append('(');
sb.append(Constants.SERVICE_PID);
sb.append('=');
sb.append(pid);
sb.append(')');
}
if ( pidFilter != null )
{
sb.append(pidFilter);
}
if ( pid != null && pidFilter != null)
{
sb.append(')');
}
final String filter = sb.toString();
try
{
// we use listConfigurations to not create configuration
// objects persistently without the user providing actual
// configuration
final Configuration[] configs = ca.listConfigurations( filter );
boolean printComma = false;
for(int i=0; configs != null && i<configs.length; i++)
{
final Configuration config = configs[i];
if ( config != null )
{
if ( printComma )
{
pw.print( ',' );
}
ca.printConfigurationJson( pw, config.getPid(), config, null, locale );
printComma = true;
}
}
}
catch ( final InvalidSyntaxException ise )
{
// should print message
// however this should not happen as we checked the filter before
}
catch ( final IOException ioe )
{
// should print message
}
}
pw.write( "]" ); //$NON-NLS-1$
// nothing more to do
return;
}
super.doGet( request, response );
}
/**
* @see org.apache.felix.webconsole.AbstractWebConsolePlugin#renderContent(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
*/
protected void renderContent( HttpServletRequest request, HttpServletResponse response ) throws IOException
{
// extract the configuration PID from the request path
String pid = request.getPathInfo().substring(this.getLabel().length() + 1);
if ( pid.length() == 0 ) {
pid = null;
} else {
pid = pid.substring( pid.lastIndexOf( '/' ) + 1 );
}
// check whether the PID is actually a filter for the selection
// of configurations to display, if the filter correctly converts
// into an OSGi filter, we use it to select configurations
// to display
String pidFilter = request.getParameter( PID_FILTER );
if ( pidFilter == null )
{
pidFilter = pid;
}
if ( pidFilter != null )
{
try
{
getBundleContext().createFilter( pidFilter );
// if the pidFilter was set from the PID, clear the PID
if ( pid == pidFilter )
{
pid = null;
}
}
catch ( InvalidSyntaxException ise )
{
// its OK, if the PID is just a single PID
pidFilter = null;
}
}
// check both PID and PID filter
if ( pid != null && !isAllowedPid(pid) )
{
response.sendError(500);
}
if ( pidFilter != null && !isAllowedPid(pidFilter) )
{
response.sendError(500);
}
final Locale loc = getLocale( request );
final String locale = ( loc != null ) ? loc.toString() : null;
StringWriter json = new StringWriter();
JSONWriter jw = new JSONWriter(json);
jw.object();
final ConfigAdminSupport ca = getConfigurationAdminSupport();
jw.key("status").value( ca != null ? Boolean.TRUE : Boolean.FALSE); //$NON-NLS-1$
if ( ca != null )
{
ca.listConfigurations( jw, pidFilter, locale, loc );
ca.listFactoryConfigurations( jw, pidFilter, locale );
}
jw.endObject();
// if a configuration is addressed, display it immediately
if ( request.getParameter( ACTION_CREATE ) != null && pid != null )
{
pid = PLACEHOLDER_PID; // new PlaceholderConfiguration( pid ).getPid();
}
// prepare variables
final String referer = request.getParameter( REFERER );
final boolean factoryCreate = "true".equals( request.getParameter(FACTORY_CREATE) ); //$NON-NLS-1$
DefaultVariableResolver vars = ( ( DefaultVariableResolver ) WebConsoleUtil.getVariableResolver( request ) );
vars.put( "__data__", json.toString() ); //$NON-NLS-1$
vars.put( "selectedPid", pid != null ? pid : "" ); //$NON-NLS-1$ //$NON-NLS-2$
vars.put( "configurationReferer", referer != null ? referer : "" ); //$NON-NLS-1$ //$NON-NLS-2$
vars.put( "factoryCreate", Boolean.valueOf(factoryCreate) ); //$NON-NLS-1$
vars.put( "param.apply", ACTION_APPLY ); //$NON-NLS-1$
vars.put( "param.create", ACTION_CREATE ); //$NON-NLS-1$
vars.put( "param.unbind", ACTION_UNBIND ); //$NON-NLS-1$
vars.put( "param.delete", ACTION_DELETE ); //$NON-NLS-1$
vars.put( "param.propertylist", PROPERTY_LIST ); //$NON-NLS-1$
vars.put( "param.pidFilter", PID_FILTER ); //$NON-NLS-1$
response.getWriter().print(TEMPLATE);
}
private ConfigAdminSupport getConfigurationAdminSupport()
{
Object configurationAdmin = getService( CONFIGURATION_ADMIN_NAME );
if ( configurationAdmin != null )
{
return new ConfigAdminSupport( this, this.getBundleContext(), configurationAdmin );
}
return null;
}
}