/* * 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; } } public 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(); } } }