blob: e2fe918519e2a22ff7ac71a149edb203c55407e6 [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.brooklyn.core;
import java.util.Map;
import org.apache.brooklyn.api.mgmt.ha.HighAvailabilityMode;
import org.apache.brooklyn.core.internal.BrooklynProperties;
import org.apache.brooklyn.core.internal.storage.BrooklynStorage;
import org.apache.brooklyn.util.core.internal.ssh.ShellTool;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.annotations.Beta;
import com.google.common.collect.Maps;
/**
* For enabling/disabling experimental features.
* They can be enabled via java system properties, or by explicitly calling {@link #setEnablement(String, boolean)}.
* <p>
* For example, start brooklyn with {@code -Dbrooklyn.experimental.feature.policyPersistence=true}
*
* @author aled
*/
@Beta
public class BrooklynFeatureEnablement {
private static final Logger LOG = LoggerFactory.getLogger(BrooklynFeatureEnablement.class);
public static final String FEATURE_PROPERTY_PREFIX = "brooklyn.experimental.feature";
public static final String FEATURE_POLICY_PERSISTENCE_PROPERTY = FEATURE_PROPERTY_PREFIX+".policyPersistence";
public static final String FEATURE_ENRICHER_PERSISTENCE_PROPERTY = FEATURE_PROPERTY_PREFIX+".enricherPersistence";
public static final String FEATURE_FEED_PERSISTENCE_PROPERTY = FEATURE_PROPERTY_PREFIX+".feedPersistence";
/** whether feeds are automatically registered when set on entities, so that they are persisted */
public static final String FEATURE_FEED_REGISTRATION_PROPERTY = FEATURE_PROPERTY_PREFIX+".feedRegistration";
public static final String FEATURE_CATALOG_PERSISTENCE_PROPERTY = FEATURE_PROPERTY_PREFIX+".catalogPersistence";
/** whether the default standby mode is {@link HighAvailabilityMode#HOT_STANDBY} or falling back to the traditional
* {@link HighAvailabilityMode#STANDBY} */
public static final String FEATURE_DEFAULT_STANDBY_IS_HOT_PROPERTY = FEATURE_PROPERTY_PREFIX+".defaultStandbyIsHot";
/** whether to attempt to use {@link BrooklynStorage} (datagrid) as a backing store for data;
* note this is <b>not</b> compatible with {@link #FEATURE_DEFAULT_STANDBY_IS_HOT_PROPERTY}
* which uses a blob/file store and a larger-granularity rebind process than was intended with the datagrid */
/* not sure if we still even need this? now the rebind/read-only feature reloads on demand from the persistence store;
* the data-grid backing */
public static final String FEATURE_USE_BROOKLYN_LIVE_OBJECTS_DATAGRID_STORAGE = FEATURE_PROPERTY_PREFIX+".useBrooklynLiveObjectsDatagridStorage";
/**
* Renaming threads can really helps with debugging etc; however it's a massive performance hit (2x)
* <p>
* We get 55000 tasks per sec with this off, 28k/s with this on.
* <p>
* Defaults to false if system property is not set.
*/
public static final String FEATURE_RENAME_THREADS = "brooklyn.executionManager.renameThreads";
/**
* When rebinding to state created from very old versions, the catalogItemId properties will be missing which
* results in errors when OSGi bundles are used. When enabled the code tries to infer the catalogItemId from
* <ul>
* <li> parent entities
* <li> catalog items matching the type that needs to be deserialized
* <li> iterating through all catalog items and checking if they can provide the needed type
* </ul>
*/
public static final String FEATURE_BACKWARDS_COMPATIBILITY_INFER_CATALOG_ITEM_ON_REBIND = "brooklyn.backwardCompatibility.feature.inferCatalogItemOnRebind";
/**
* When rebinding, an entity could reference a catalog item that no longer exists. This option
* will automatically update the catalog item reference to what is inferred as the most
* suitable catalog symbolicName:version.
*/
public static final String FEATURE_AUTO_FIX_CATALOG_REF_ON_REBIND = "brooklyn.quickfix.fixDanglingCatalogItemOnRebind";
/**
* When executing over ssh, whether to support the "async exec" approach, or only the classic approach.
*
* If this feature is disabled, then even if the {@link ShellTool#PROP_EXEC_ASYNC} is configured it
* will still use the classic ssh approach.
*/
public static final String FEATURE_SSH_ASYNC_EXEC = FEATURE_PROPERTY_PREFIX+".ssh.asyncExec";
public static final String FEATURE_VALIDATE_LOCATION_SSH_KEYS = "brooklyn.validate.locationSshKeys";
private static final Map<String, Boolean> FEATURE_ENABLEMENTS = Maps.newLinkedHashMap();
private static final Object MUTEX = new Object();
static void setDefaults() {
// Idea is here one can put experimental features that are *enabled* by default, but
// that can be turned off via system properties, or vice versa.
// Typically this is useful where a feature is deemed risky!
setDefault(FEATURE_POLICY_PERSISTENCE_PROPERTY, true);
setDefault(FEATURE_ENRICHER_PERSISTENCE_PROPERTY, true);
setDefault(FEATURE_FEED_PERSISTENCE_PROPERTY, true);
setDefault(FEATURE_FEED_REGISTRATION_PROPERTY, false);
setDefault(FEATURE_CATALOG_PERSISTENCE_PROPERTY, true);
setDefault(FEATURE_DEFAULT_STANDBY_IS_HOT_PROPERTY, false);
setDefault(FEATURE_USE_BROOKLYN_LIVE_OBJECTS_DATAGRID_STORAGE, false);
setDefault(FEATURE_RENAME_THREADS, false);
setDefault(FEATURE_BACKWARDS_COMPATIBILITY_INFER_CATALOG_ITEM_ON_REBIND, true);
setDefault(FEATURE_AUTO_FIX_CATALOG_REF_ON_REBIND, false);
setDefault(FEATURE_SSH_ASYNC_EXEC, false);
setDefault(FEATURE_VALIDATE_LOCATION_SSH_KEYS, true);
}
static {
setDefaults();
}
/**
* Initialises the feature-enablement from brooklyn properties. For each
* property, prefer a system-property if present; otherwise use the value
* from brooklyn properties.
*/
public static void init(BrooklynProperties props) {
boolean changed = false;
for (Map.Entry<String, Object> entry : props.asMapWithStringKeys().entrySet()) {
String property = entry.getKey();
if (property.startsWith(FEATURE_PROPERTY_PREFIX)) {
if (!FEATURE_ENABLEMENTS.containsKey(property)) {
Object rawVal = System.getProperty(property);
if (rawVal == null) {
rawVal = entry.getValue();
}
boolean val = Boolean.parseBoolean(""+rawVal);
FEATURE_ENABLEMENTS.put(property, val);
changed = true;
LOG.debug("Init feature enablement of "+property+" set to "+val);
}
}
}
if (!changed) {
LOG.debug("Init feature enablement did nothing, as no settings in brooklyn properties");
}
}
public static boolean isEnabled(String property) {
synchronized (MUTEX) {
if (!FEATURE_ENABLEMENTS.containsKey(property)) {
String rawVal = System.getProperty(property);
boolean val = Boolean.parseBoolean(rawVal);
FEATURE_ENABLEMENTS.put(property, val);
}
return FEATURE_ENABLEMENTS.get(property);
}
}
public static boolean enable(String property) {
return setEnablement(property, true);
}
public static boolean disable(String property) {
return setEnablement(property, false);
}
public static boolean setEnablement(String property, boolean val) {
synchronized (MUTEX) {
boolean oldVal = isEnabled(property);
FEATURE_ENABLEMENTS.put(property, val);
return oldVal;
}
}
static void setDefault(String property, boolean val) {
synchronized (MUTEX) {
if (!FEATURE_ENABLEMENTS.containsKey(property)) {
String rawVal = System.getProperty(property);
if (rawVal == null) {
FEATURE_ENABLEMENTS.put(property, val);
LOG.debug("Default enablement of "+property+" set to "+val);
} else {
LOG.debug("Not setting default enablement of "+property+" to "+val+", because system property is "+rawVal);
}
}
}
}
static void clearCache() {
synchronized (MUTEX) {
FEATURE_ENABLEMENTS.clear();
setDefaults();
}
}
}