blob: 5379d2044b0c0a6c25bf1ba99ed8eb45ce69258e [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.servlet;
import java.io.IOException;
import java.net.URL;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Dictionary;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.ResourceBundle;
import java.util.Set;
import javax.servlet.GenericServlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.io.FilenameUtils;
import org.apache.felix.webconsole.AbstractWebConsolePlugin;
import org.apache.felix.webconsole.BrandingPlugin;
import org.apache.felix.webconsole.WebConsoleConstants;
import org.apache.felix.webconsole.WebConsoleSecurityProvider;
import org.apache.felix.webconsole.internal.OsgiManagerPlugin;
import org.apache.felix.webconsole.internal.Util;
import org.apache.felix.webconsole.internal.core.BundlesServlet;
import org.apache.felix.webconsole.internal.filter.FilteringResponseWrapper;
import org.apache.felix.webconsole.internal.i18n.ResourceBundleManager;
import org.apache.felix.webconsole.internal.misc.ConfigurationRender;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
import org.osgi.framework.Filter;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceFactory;
import org.osgi.framework.ServiceReference;
import org.osgi.framework.ServiceRegistration;
import org.osgi.service.http.HttpContext;
import org.osgi.service.http.HttpService;
import org.osgi.service.log.LogService;
import org.osgi.util.tracker.ServiceTracker;
/**
* The <code>OSGi Manager</code> is the actual Web Console Servlet which
* is registered with the OSGi Http Service and which maintains registered
* console plugins.
*/
public class OsgiManager extends GenericServlet
{
/** Pseudo class version ID to keep the IDE quite. */
private static final long serialVersionUID = 1L;
/**
* Old name of the request attribute providing the root to the web console.
* This attribute is no deprecated and replaced by
* {@link WebConsoleConstants#ATTR_APP_ROOT}.
*
* @deprecated use {@link WebConsoleConstants#ATTR_APP_ROOT} instead
*/
private static final String ATTR_APP_ROOT_OLD = OsgiManager.class.getName()
+ ".appRoot";
/**
* Old name of the request attribute providing the mappings from label to
* page title. This attribute is no deprecated and replaced by
* {@link WebConsoleConstants#ATTR_LABEL_MAP}.
*
* @deprecated use {@link WebConsoleConstants#ATTR_LABEL_MAP} instead
*/
private static final String ATTR_LABEL_MAP_OLD = OsgiManager.class.getName()
+ ".labelMap";
/**
* The name and value of a parameter which will prevent redirection to a
* render after the action has been executed (value is "_noredir_"). This
* may be used by programmatic action submissions.
*/
public static final String PARAM_NO_REDIRECT_AFTER_ACTION = "_noredir_";
/**
* The name of the cookie storing user-configured locale
* See https://issues.apache.org/jira/browse/FELIX-2267
*/
private static final String COOKIE_LOCALE = "felix.webconsole.locale"; //$NON-NLS-1$
private final String FRAMEWORK_PROP_MANAGER_ROOT = "felix.webconsole.manager.root"; //$NON-NLS-1$
private static final String FRAMEWORK_PROP_REALM = "felix.webconsole.realm"; //$NON-NLS-1$
private static final String FRAMEWORK_PROP_USER_NAME = "felix.webconsole.username"; //$NON-NLS-1$
private static final String FRAMEWORK_PROP_PASSWORD = "felix.webconsole.password"; //$NON-NLS-1$
private static final String FRAMEWORK_PROP_LOG_LEVEL = "felix.webconsole.loglevel"; //$NON-NLS-1$
private static final String FRAMEWORK_PROP_LOCALE = "felix.webconsole.locale"; //$NON-NLS-1$
static final String PROP_MANAGER_ROOT = "manager.root"; //$NON-NLS-1$
static final String PROP_DEFAULT_RENDER = "default.render"; //$NON-NLS-1$
static final String PROP_REALM = "realm"; //$NON-NLS-1$
static final String PROP_USER_NAME = "username"; //$NON-NLS-1$
static final String PROP_PASSWORD = "password"; //$NON-NLS-1$
static final String PROP_ENABLED_PLUGINS = "plugins"; //$NON-NLS-1$
static final String PROP_LOG_LEVEL = "loglevel"; //$NON-NLS-1$
static final String PROP_LOCALE = "locale"; //$NON-NLS-1$
static final String PROP_HTTP_SERVICE_SELECTOR = "http.service.filter"; //$NON-NLS-1$
public static final int DEFAULT_LOG_LEVEL = LogService.LOG_WARNING;
static final String DEFAULT_PAGE = BundlesServlet.NAME;
static final String DEFAULT_REALM = "OSGi Management Console"; //$NON-NLS-1$
static final String DEFAULT_USER_NAME = "admin"; //$NON-NLS-1$
static final String DEFAULT_PASSWORD = "admin"; //$NON-NLS-1$
static final String DEFAULT_HTTP_SERVICE_SELECTOR = ""; //$NON-NLS-1$
/**
* The default value for the {@link #PROP_MANAGER_ROOT} configuration
* property (value is "/system/console").
*/
static final String DEFAULT_MANAGER_ROOT = "/system/console"; //$NON-NLS-1$
static final String[] PLUGIN_CLASSES = {
"org.apache.felix.webconsole.internal.compendium.ConfigurationAdminConfigurationPrinter", //$NON-NLS-1$
"org.apache.felix.webconsole.internal.compendium.PreferencesConfigurationPrinter", //$NON-NLS-1$
"org.apache.felix.webconsole.internal.compendium.WireAdminConfigurationPrinter", //$NON-NLS-1$
"org.apache.felix.webconsole.internal.core.BundlesConfigurationPrinter", //$NON-NLS-1$
"org.apache.felix.webconsole.internal.core.PermissionsConfigurationPrinter", //$NON-NLS-1$
"org.apache.felix.webconsole.internal.core.ServicesConfigurationPrinter", //$NON-NLS-1$
"org.apache.felix.webconsole.internal.misc.SystemPropertiesPrinter", //$NON-NLS-1$
"org.apache.felix.webconsole.internal.misc.ThreadPrinter", }; //$NON-NLS-1$
static final String[] PLUGIN_MAP = {
"org.apache.felix.webconsole.internal.compendium.ConfigManager", "configMgr", //$NON-NLS-1$ //$NON-NLS-2$
"org.apache.felix.webconsole.internal.compendium.LogServlet", "logs", //$NON-NLS-1$ //$NON-NLS-2$
"org.apache.felix.webconsole.internal.core.BundlesServlet", "bundles", //$NON-NLS-1$ //$NON-NLS-2$
"org.apache.felix.webconsole.internal.core.ServicesServlet", "services", //$NON-NLS-1$ //$NON-NLS-2$
"org.apache.felix.webconsole.internal.misc.LicenseServlet", "licenses", //$NON-NLS-1$ //$NON-NLS-2$
"org.apache.felix.webconsole.internal.system.VMStatPlugin", "vmstat", //$NON-NLS-1$ //$NON-NLS-2$
};
private BundleContext bundleContext;
private HttpServiceTracker httpServiceTracker;
private HttpService httpService;
private PluginHolder holder;
private ServiceTracker brandingTracker;
private ServiceTracker securityProviderTracker;
private ServiceRegistration configurationListener;
// list of OsgiManagerPlugin instances activated during init. All these
// instances will have to be deactivated during destroy
private List<OsgiManagerPlugin> osgiManagerPlugins = new ArrayList<OsgiManagerPlugin>();
private String webManagerRoot;
// true if the OsgiManager is registered as a Servlet with the HttpService
private boolean httpServletRegistered;
// true if the resources have been registered with the HttpService
private boolean httpResourcesRegistered;
// default configuration from framework properties
private HashMap<String, Object> defaultConfiguration;
// configuration from Configuration Admin
private HashMap<String, ?> configuration;
// See https://issues.apache.org/jira/browse/FELIX-2267
private Locale configuredLocale;
private Set<String> enabledPlugins;
ResourceBundleManager resourceBundleManager;
private int logLevel = DEFAULT_LOG_LEVEL;
@SuppressWarnings("serial")
public OsgiManager(BundleContext bundleContext)
{
this.bundleContext = bundleContext;
this.holder = new PluginHolder(bundleContext);
// new plugins setup
for (int i = 0; i < PLUGIN_MAP.length; i++)
{
final String pluginClassName = PLUGIN_MAP[i++];
final String label = PLUGIN_MAP[i];
holder.addInternalPlugin(this, pluginClassName, label);
}
// setup the included plugins
ClassLoader classLoader = getClass().getClassLoader();
for (int i = 0; i < PLUGIN_CLASSES.length; i++)
{
String pluginClassName = PLUGIN_CLASSES[i];
try
{
Class<?> pluginClass = classLoader.loadClass(pluginClassName);
Object plugin = pluginClass.newInstance();
if (plugin instanceof OsgiManagerPlugin)
{
((OsgiManagerPlugin) plugin).activate(bundleContext);
osgiManagerPlugins.add((OsgiManagerPlugin) plugin);
}
if (plugin instanceof BrandingPlugin)
{
AbstractWebConsolePlugin.setBrandingPlugin((BrandingPlugin) plugin);
}
}
catch (NoClassDefFoundError ncdfe)
{
String message = ncdfe.getMessage();
if (message == null)
{
// no message, construct it
message = "Class definition not found (NoClassDefFoundError)";
}
else if (message.indexOf(' ') < 0)
{
// message is just a class name, try to be more descriptive
message = "Class " + message + " missing";
}
log(LogService.LOG_INFO, pluginClassName + " not enabled. Reason: "
+ message);
}
catch (Throwable t)
{
log(LogService.LOG_INFO, "Failed to instantiate plugin "
+ pluginClassName + ". Reason: " + t);
}
}
// the resource bundle manager
resourceBundleManager = new ResourceBundleManager(getBundleContext());
// start the configuration render, providing the resource bundle manager
ConfigurationRender cr = new ConfigurationRender(resourceBundleManager);
cr.activate(bundleContext);
osgiManagerPlugins.add(cr);
holder.addOsgiManagerPlugin(cr);
// start tracking external plugins after setting up our own plugins
holder.open();
// accept new console branding service
brandingTracker = new BrandingServiceTracker(this);
brandingTracker.open();
// add support for pluggable security
securityProviderTracker = new ServiceTracker(bundleContext,
WebConsoleSecurityProvider.class.getName(), null);
securityProviderTracker.open();
// load the default configuration from the framework
this.defaultConfiguration = new HashMap<String, Object>();
this.defaultConfiguration.put( PROP_MANAGER_ROOT,
ConfigurationUtil.getProperty( bundleContext, FRAMEWORK_PROP_MANAGER_ROOT, DEFAULT_MANAGER_ROOT ) );
this.defaultConfiguration.put( PROP_REALM,
ConfigurationUtil.getProperty( bundleContext, FRAMEWORK_PROP_REALM, DEFAULT_REALM ) );
this.defaultConfiguration.put( PROP_USER_NAME,
ConfigurationUtil.getProperty( bundleContext, FRAMEWORK_PROP_USER_NAME, DEFAULT_USER_NAME ) );
this.defaultConfiguration.put( PROP_PASSWORD,
ConfigurationUtil.getProperty( bundleContext, FRAMEWORK_PROP_PASSWORD, DEFAULT_PASSWORD ) );
this.defaultConfiguration.put( PROP_LOG_LEVEL,
ConfigurationUtil.getProperty( bundleContext, FRAMEWORK_PROP_LOG_LEVEL, DEFAULT_LOG_LEVEL) );
this.defaultConfiguration.put( PROP_LOCALE,
ConfigurationUtil.getProperty( bundleContext, FRAMEWORK_PROP_LOCALE, null ) );
// configure and start listening for configuration
updateConfiguration(null);
// register managed service as a service factory
this.configurationListener = bundleContext.registerService( "org.osgi.service.cm.ManagedService", //$NON-NLS-1$
new ServiceFactory()
{
public Object getService( Bundle bundle, ServiceRegistration registration )
{
return new ConfigurationSupport( OsgiManager.this );
}
public void ungetService( Bundle bundle, ServiceRegistration registration, Object service )
{
// do nothing
}
}, new Hashtable<String, String>()
{
{
put( Constants.SERVICE_VENDOR, "The Apache Software Foundation" ); //$NON-NLS-1$
put( Constants.SERVICE_DESCRIPTION, "OSGi Management Console Configuration Receiver" ); //$NON-NLS-1$
put( Constants.SERVICE_PID, getConfigurationPid() );
}
} );
}
public void dispose()
{
// dispose off held plugins
holder.close();
// dispose off the resource bundle manager
if (resourceBundleManager != null)
{
resourceBundleManager.dispose();
resourceBundleManager = null;
}
// stop listening for brandings
if (brandingTracker != null)
{
brandingTracker.close();
brandingTracker = null;
}
// deactivate any remaining plugins
for ( Iterator<OsgiManagerPlugin> pi = osgiManagerPlugins.iterator(); pi.hasNext(); )
{
pi.next().deactivate();
}
// simply remove all operations, we should not be used anymore
this.osgiManagerPlugins.clear();
// now drop the HttpService and continue with further destroyals
if (httpServiceTracker != null)
{
httpServiceTracker.close();
httpServiceTracker = null;
}
// stop listening for configuration
if (configurationListener != null)
{
configurationListener.unregister();
configurationListener = null;
}
// stop tracking security provider
if (securityProviderTracker != null)
{
securityProviderTracker.close();
securityProviderTracker = null;
}
this.bundleContext = null;
}
//---------- Servlet API
/**
* @see javax.servlet.GenericServlet#init()
*/
public void init()
{
// base class initialization not needed, since the GenericServlet.init
// is an empty method
holder.setServletContext(getServletContext());
}
/**
* @see javax.servlet.GenericServlet#service(javax.servlet.ServletRequest, javax.servlet.ServletResponse)
*/
public void service(final ServletRequest req, final ServletResponse res)
throws ServletException, IOException
{
// don't really expect to be called within a non-HTTP environment
service((HttpServletRequest) req, (HttpServletResponse) res);
// ensure response has been sent back and response is committed
// (we are authorative for our URL space and no other servlet should interfere)
res.flushBuffer();
}
private void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
// check whether we are not at .../{webManagerRoot}
final String pathInfo = request.getPathInfo();
if (pathInfo == null || pathInfo.equals("/")) //$NON-NLS-1$
{
String path = request.getRequestURI();
if (!path.endsWith("/")) //$NON-NLS-1$
{
path = path.concat("/"); //$NON-NLS-1$
}
path = path.concat(holder.getDefaultPluginLabel());
response.sendRedirect(path);
return;
}
int slash = pathInfo.indexOf("/", 1);
if (slash < 2)
{
slash = pathInfo.length();
}
final Locale locale = getConfiguredLocale(request);
final String label = pathInfo.substring(1, slash);
AbstractWebConsolePlugin plugin = getConsolePlugin(label);
if (plugin != null)
{
final Map<String, String> labelMap = holder.getLocalizedLabelMap( resourceBundleManager, locale );
// the official request attributes
request.setAttribute(WebConsoleConstants.ATTR_LANG_MAP, getLangMap());
request.setAttribute(WebConsoleConstants.ATTR_LABEL_MAP, labelMap);
request.setAttribute(WebConsoleConstants.ATTR_APP_ROOT,
request.getContextPath() + request.getServletPath());
request.setAttribute(WebConsoleConstants.ATTR_PLUGIN_ROOT,
request.getContextPath() + request.getServletPath() + '/' + label);
// deprecated request attributes
request.setAttribute(ATTR_LABEL_MAP_OLD, labelMap);
request.setAttribute(ATTR_APP_ROOT_OLD,
request.getContextPath() + request.getServletPath());
// wrap the response for localization and template variable replacement
request = wrapRequest(request, locale);
response = wrapResponse(request, response, plugin);
plugin.service(request, response);
}
else
{
final String body404 = MessageFormat.format(
resourceBundleManager.getResourceBundle(bundleContext.getBundle(), locale).getString(
"404"), //$NON-NLS-1$
new Object[] { request.getContextPath() + request.getServletPath() + '/'
+ BundlesServlet.NAME });
response.setCharacterEncoding("utf-8"); //$NON-NLS-1$
response.setContentType("text/html"); //$NON-NLS-1$
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
response.getWriter().println(body404);
}
}
private final AbstractWebConsolePlugin getConsolePlugin(final String label)
{
// backwards compatibility for the former "install" action which is
// used by the Maven Sling Plugin
if ("install".equals(label)) //$NON-NLS-1$
{
return holder.getPlugin(BundlesServlet.NAME);
}
AbstractWebConsolePlugin plugin = holder.getPlugin( label );
if ( plugin == null && label.indexOf( '.' ) > 0 )
{
int last = 0;
for ( int dot = label.indexOf( '.', last ); plugin == null && dot > last; last = dot + 1, dot = label
.indexOf( '.', last ) )
{
final String pluginLabel = label.substring( 0, dot );
plugin = holder.getPlugin( pluginLabel );
}
}
return plugin;
}
// See https://issues.apache.org/jira/browse/FELIX-2267
private final Locale getConfiguredLocale(HttpServletRequest request)
{
Locale locale = null;
Cookie[] cookies = request.getCookies();
for (int i = 0; cookies != null && i < cookies.length; i++)
{
if (COOKIE_LOCALE.equals(cookies[i].getName()))
{
locale = Util.parseLocaleString(cookies[i].getValue());
break;
}
}
// TODO: check UserAdmin ?
if (locale == null)
locale = configuredLocale;
if (locale == null)
locale = request.getLocale();
return locale;
}
/**
* @see javax.servlet.GenericServlet#destroy()
*/
public void destroy()
{
// base class destroy not needed, since the GenericServlet.destroy
// is an empty method
holder.setServletContext(null);
}
//---------- internal
BundleContext getBundleContext()
{
return bundleContext;
}
/**
* Returns the Service PID used to retrieve configuration and to describe
* the configuration properties.
*/
String getConfigurationPid()
{
return getClass().getName();
}
/**
* Calls the <code>ServletContext.log(String)</code> method if the
* configured log level is less than or equal to the given <code>level</code>.
* <p>
* Note, that the <code>level</code> parameter is only used to decide whether
* the <code>GenericServlet.log(String)</code> method is called or not. The
* actual implementation of the <code>GenericServlet.log</code> method is
* outside of the control of this method.
* <p>
* If the servlet has not been initialized yet or has already been destroyed
* the message is printed to stderr.
*
* @param level The log level at which to log the message
* @param message The message to log
*/
void log(int level, String message)
{
if (logLevel >= level)
{
ServletConfig config = getServletConfig();
if ( config != null )
{
ServletContext context = config.getServletContext();
if ( context != null )
{
context.log( message );
return;
}
}
System.err.println( message );
}
}
/**
* Calls the <code>ServletContext.log(String, Throwable)</code> method if
* the configured log level is less than or equal to the given
* <code>level</code>.
* <p>
* Note, that the <code>level</code> parameter is only used to decide whether
* the <code>GenericServlet.log(String, Throwable)</code> method is called
* or not. The actual implementation of the <code>GenericServlet.log</code>
* method is outside of the control of this method.
* <p>
* If the servlet has not been initialized yet or has already been destroyed
* the message is printed to stderr.
*
* @param level The log level at which to log the message
* @param message The message to log
* @param t The <code>Throwable</code> to log with the message
*/
void log(int level, String message, Throwable t)
{
if (logLevel >= level)
{
ServletConfig config = getServletConfig();
if ( config != null )
{
ServletContext context = config.getServletContext();
if ( context != null )
{
context.log( message, t );
return;
}
}
System.err.println( message );
if ( t != null )
{
t.printStackTrace( System.err );
}
}
}
private HttpServletRequest wrapRequest(final HttpServletRequest request,
final Locale locale)
{
return new HttpServletRequestWrapper(request)
{
/**
* @see javax.servlet.ServletRequestWrapper#getLocale()
*/
public Locale getLocale()
{
return locale;
}
};
}
private HttpServletResponse wrapResponse(final HttpServletRequest request,
final HttpServletResponse response, final AbstractWebConsolePlugin plugin)
{
final Locale locale = request.getLocale();
final ResourceBundle resourceBundle = resourceBundleManager.getResourceBundle(
plugin.getBundle(), locale);
return new FilteringResponseWrapper(response, resourceBundle, request);
}
private static class HttpServiceTracker extends ServiceTracker
{
private static final String HTTP_SERVICE = "org.osgi.service.http.HttpService"; //$NON-NLS-1$
private final OsgiManager osgiManager;
private final String httpServiceSelector;
static HttpServiceTracker create(OsgiManager osgiManager,
String httpServiceSelector)
{
// got a service selector filter
if (httpServiceSelector != null && httpServiceSelector.length() > 0)
{
try
{
final String filterString = "(&(" + Constants.OBJECTCLASS + "=" //$NON-NLS-1$ //$NON-NLS-2$
+ HTTP_SERVICE + ")(" + httpServiceSelector + "))"; //$NON-NLS-1$ //$NON-NLS-2$
Filter filter = osgiManager.getBundleContext().createFilter(
filterString);
return new HttpServiceTracker(osgiManager, httpServiceSelector,
filter);
}
catch (InvalidSyntaxException ise)
{
// TODO: log or throw or ignore ....
}
}
// no filter or illegal filter string
return new HttpServiceTracker(osgiManager);
}
private HttpServiceTracker(final OsgiManager osgiManager)
{
super(osgiManager.getBundleContext(), HTTP_SERVICE, null);
this.osgiManager = osgiManager;
this.httpServiceSelector = null;
}
private HttpServiceTracker(final OsgiManager osgiManager, final String httpServiceSelector, final Filter httpServiceFilter)
{
super(osgiManager.getBundleContext(), httpServiceFilter, null);
this.osgiManager = osgiManager;
this.httpServiceSelector = httpServiceSelector;
}
boolean isSameSelector(final String newHttpServiceSelector)
{
if (newHttpServiceSelector != null)
{
return newHttpServiceSelector.equals(httpServiceSelector);
}
return httpServiceSelector == null;
}
public Object addingService(ServiceReference reference)
{
Object service = super.addingService(reference);
if (service instanceof HttpService)
{
osgiManager.bindHttpService((HttpService) service);
}
return service;
}
public void removedService(ServiceReference reference, Object service)
{
if (service instanceof HttpService)
{
osgiManager.unbindHttpService((HttpService) service);
}
super.removedService(reference, service);
}
}
private static class BrandingServiceTracker extends ServiceTracker
{
BrandingServiceTracker(OsgiManager osgiManager)
{
super(osgiManager.getBundleContext(), BrandingPlugin.class.getName(), null);
}
public Object addingService(ServiceReference reference)
{
Object plugin = super.addingService(reference);
if (plugin instanceof BrandingPlugin)
{
AbstractWebConsolePlugin.setBrandingPlugin((BrandingPlugin) plugin);
}
return plugin;
}
public void removedService(ServiceReference reference, Object service)
{
if (service instanceof BrandingPlugin)
{
AbstractWebConsolePlugin.setBrandingPlugin(null);
}
super.removedService(reference, service);
}
}
protected synchronized void bindHttpService(HttpService httpService)
{
// do not bind service, when we are already bound
if (this.httpService != null)
{
log(LogService.LOG_DEBUG,
"bindHttpService: Already bound to an HTTP Service, ignoring further services");
return;
}
Map<String, ?> config = getConfiguration();
// get authentication details
String realm = ConfigurationUtil.getProperty(config, PROP_REALM, DEFAULT_REALM);
String userId = ConfigurationUtil.getProperty(config, PROP_USER_NAME, DEFAULT_USER_NAME);
String password = ConfigurationUtil.getProperty(config, PROP_PASSWORD, DEFAULT_PASSWORD);
// register the servlet and resources
try
{
HttpContext httpContext = new OsgiManagerHttpContext(httpService,
securityProviderTracker, userId, password, realm);
Dictionary<String, String> servletConfig = toStringConfig(config);
// register this servlet and take note of this
httpService.registerServlet(this.webManagerRoot, this, servletConfig,
httpContext);
httpServletRegistered = true;
// register resources and take of this
httpService.registerResources(this.webManagerRoot + "/res", "/res",
httpContext);
httpResourcesRegistered = true;
}
catch (Exception e)
{
log(LogService.LOG_ERROR, "bindHttpService: Problem setting up", e);
}
this.httpService = httpService;
}
protected synchronized void unbindHttpService(HttpService httpService)
{
if (this.httpService != httpService)
{
log(LogService.LOG_DEBUG,
"unbindHttpService: Ignoring unbind of an HttpService to which we are not registered");
return;
}
// drop the service reference
this.httpService = null;
if (httpResourcesRegistered)
{
try
{
httpService.unregister(this.webManagerRoot + "/res");
}
catch (Throwable t)
{
log(LogService.LOG_WARNING,
"unbindHttpService: Failed unregistering Resources", t);
}
httpResourcesRegistered = false;
}
if (httpServletRegistered)
{
try
{
httpService.unregister(this.webManagerRoot);
}
catch (Throwable t)
{
log(LogService.LOG_WARNING,
"unbindHttpService: Failed unregistering Servlet", t);
}
httpServletRegistered = false;
}
}
private Map<String, ?> getConfiguration()
{
return configuration;
}
Map<String, ?> getDefaultConfiguration()
{
return defaultConfiguration;
}
synchronized void updateConfiguration(Dictionary<String, ?> osgiConfig)
{
HashMap<String, Object> config = new HashMap<String, Object>(this.defaultConfiguration);
if ( osgiConfig != null )
{
for ( Enumeration<String> keys = osgiConfig.keys(); keys.hasMoreElements(); )
{
final String key = keys.nextElement();
config.put( key, osgiConfig.get( key ) );
}
}
configuration = config;
final Object locale = config.get(PROP_LOCALE);
configuredLocale = locale == null || locale.toString().trim().length() == 0 //
? null : Util.parseLocaleString(locale.toString().trim());
logLevel = ConfigurationUtil.getProperty(config, PROP_LOG_LEVEL, DEFAULT_LOG_LEVEL);
AbstractWebConsolePlugin.setLogLevel(logLevel);
// default plugin page configuration
holder.setDefaultPluginLabel(ConfigurationUtil.getProperty(config, PROP_DEFAULT_RENDER, DEFAULT_PAGE));
// get the web manager root path
String newWebManagerRoot = ConfigurationUtil.getProperty(config, PROP_MANAGER_ROOT, DEFAULT_MANAGER_ROOT);
if (!newWebManagerRoot.startsWith("/")) //$NON-NLS-1$
{
newWebManagerRoot = "/" + newWebManagerRoot; //$NON-NLS-1$
}
// get the HTTP Service selector (and dispose tracker for later
// recreation)
final String newHttpServiceSelector = ConfigurationUtil.getProperty(config,
PROP_HTTP_SERVICE_SELECTOR, DEFAULT_HTTP_SERVICE_SELECTOR);
if (httpServiceTracker != null
&& !httpServiceTracker.isSameSelector(newHttpServiceSelector))
{
httpServiceTracker.close();
httpServiceTracker = null;
}
// get enabled plugins
String[] plugins = ConfigurationUtil.getStringArrayProperty(config, PROP_ENABLED_PLUGINS);
enabledPlugins = null == plugins ? null : new HashSet<String>(Arrays.asList(plugins));
initInternalPlugins();
// might update HTTP service registration
HttpService httpService = this.httpService;
if (httpService != null)
{
// unbind old location first
unbindHttpService(httpService);
// switch location
this.webManagerRoot = newWebManagerRoot;
// bind new location now
bindHttpService(httpService);
}
else
{
// just set the configured location (FELIX-2034)
this.webManagerRoot = newWebManagerRoot;
}
// create or recreate the HTTP service tracker with the new selector
if (httpServiceTracker == null)
{
httpServiceTracker = HttpServiceTracker.create(this, newHttpServiceSelector);
httpServiceTracker.open();
}
}
private void initInternalPlugins()
{
for (int i = 0; i < PLUGIN_MAP.length; i++)
{
final String pluginClassName = PLUGIN_MAP[i++];
final String label = PLUGIN_MAP[i];
boolean active = holder.getPlugin(label) != null;
boolean disabled = isPluginDisabled(pluginClassName);
if (disabled)
{
if (active)
{
holder.removeOsgiManagerPlugin(label);
}
}
else
{
if (!active)
{
holder.addInternalPlugin(this, pluginClassName, label);
}
}
}
}
/**
* Returns <code>true</code> if the list of enabled plugins is
* configured but the plugin is not contained in that list.
* <p>
* This method is intended to be used only for {@link InternalPlugin#isEnabled()}
*/
boolean isPluginDisabled(String pluginClass)
{
return enabledPlugins != null && !enabledPlugins.contains(pluginClass);
}
private Dictionary<String, String> toStringConfig( Map<String, ?> config )
{
Dictionary<String, String> stringConfig = new Hashtable<String, String>();
for ( Entry<String, ?> entry : config.entrySet() )
{
stringConfig.put( entry.getKey(), String.valueOf( entry.getValue() ) );
}
return stringConfig;
}
private Map<String, String> langMap;
private final Map<String, String> getLangMap()
{
if ( null != langMap )
{
return langMap;
}
final Map<String, String> map = new HashMap<String, String>();
final Bundle bundle = bundleContext.getBundle();
@SuppressWarnings("unchecked")
final Enumeration<URL> e = bundle.findEntries( "res/flags", null, false ); //$NON-NLS-1$
while ( e != null && e.hasMoreElements() )
{
final URL img = e.nextElement();
final String name = FilenameUtils.getBaseName( img.getFile() );
try
{
final String locale = new Locale( name, "" ).getDisplayLanguage(); //$NON-NLS-1$
map.put( name, null != locale ? locale : name );
}
catch ( Throwable t )
{
t.printStackTrace();
/* ignore invalid locale? */
}
}
return langMap = map;
}
}