| /* |
| * 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.discovery.oak; |
| |
| import java.net.MalformedURLException; |
| import java.net.URL; |
| import java.util.LinkedList; |
| import java.util.List; |
| import java.util.Map; |
| |
| import org.apache.felix.scr.annotations.Activate; |
| import org.apache.felix.scr.annotations.Component; |
| import org.apache.felix.scr.annotations.Property; |
| import org.apache.felix.scr.annotations.Service; |
| import org.apache.sling.commons.osgi.PropertiesUtil; |
| import org.apache.sling.discovery.base.connectors.BaseConfig; |
| import org.apache.sling.discovery.commons.providers.spi.base.DiscoveryLiteConfig; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| /** |
| * Configuration object used as a central config point for the discovery service |
| * implementation |
| * <p> |
| * The properties are described below under. |
| */ |
| @Component(metatype = true, label="%config.name", description="%config.description") |
| @Service(value = { Config.class, BaseConfig.class, DiscoveryLiteConfig.class }) |
| public class Config implements BaseConfig, DiscoveryLiteConfig { |
| |
| private final Logger logger = LoggerFactory.getLogger(this.getClass()); |
| |
| /** resource used to keep instance information such as last heartbeat, properties, incoming announcements **/ |
| private static final String CLUSTERINSTANCES_RESOURCE = "clusterInstances"; |
| |
| /** resource used to store the sync tokens as part of a topology change **/ |
| private static final String SYNC_TOKEN_RESOURCE = "syncTokens"; |
| |
| /** resource used to store the clusterNodeIds to slingIds map **/ |
| private static final String ID_MAP_RESOURCE = "idMap"; |
| |
| /** Configure the timeout (in seconds) after which an instance is considered dead/crashed. */ |
| public static final long DEFAULT_TOPOLOGY_CONNECTOR_TIMEOUT = 120; |
| @Property(longValue=DEFAULT_TOPOLOGY_CONNECTOR_TIMEOUT) |
| public static final String TOPOLOGY_CONNECTOR_TIMEOUT_KEY = "connectorPingTimeout"; |
| protected long connectorPingTimeout = DEFAULT_TOPOLOGY_CONNECTOR_TIMEOUT; |
| |
| /** Configure the interval (in seconds) according to which the heartbeats are exchanged in the topology. */ |
| public static final long DEFAULT_TOPOLOGY_CONNECTOR_INTERVAL = 30; |
| @Property(longValue=DEFAULT_TOPOLOGY_CONNECTOR_INTERVAL) |
| public static final String TOPOLOGY_CONNECTOR_INTERVAL_KEY = "connectorPingInterval"; |
| protected long connectorPingInterval = DEFAULT_TOPOLOGY_CONNECTOR_INTERVAL; |
| |
| /** Configure the interval (in seconds) according to which the heartbeats are exchanged in the topology. */ |
| public static final long DEFAULT_DISCOVERY_LITE_CHECK_INTERVAL = 2; |
| @Property(longValue=DEFAULT_DISCOVERY_LITE_CHECK_INTERVAL) |
| public static final String DISCOVERY_LITE_CHECK_INTERVAL_KEY = "discoveryLiteCheckInterval"; |
| protected long discoveryLiteCheckInterval = DEFAULT_DISCOVERY_LITE_CHECK_INTERVAL; |
| |
| public static final long DEFAULT_CLUSTER_SYNC_SERVICE_TIMEOUT = 120; |
| @Property(longValue=DEFAULT_CLUSTER_SYNC_SERVICE_TIMEOUT) |
| public static final String CLUSTER_SYNC_SERVICE_TIMEOUT_KEY = "clusterSyncServiceTimeout"; |
| protected long clusterSyncServiceTimeout = DEFAULT_CLUSTER_SYNC_SERVICE_TIMEOUT; |
| |
| public static final long DEFAULT_CLUSTER_SYNC_SERVICE_INTERVAL = 2; |
| @Property(longValue=DEFAULT_CLUSTER_SYNC_SERVICE_INTERVAL) |
| public static final String CLUSTER_SYNC_SERVICE_INTERVAL_KEY = "clusterSyncServiceInterval"; |
| protected long clusterSyncServiceInterval = DEFAULT_CLUSTER_SYNC_SERVICE_INTERVAL; |
| |
| /** |
| * If set to true a syncToken will be used on top of waiting for |
| * deactivating instances to be fully processed. |
| * If set to false, only deactivating instances will be waited for |
| * to be fully processed. |
| */ |
| @Property(boolValue=true) |
| private static final String SYNC_TOKEN_ENABLED = "enableSyncToken"; |
| |
| /** Configure the time (in seconds) which must be passed at minimum between sending TOPOLOGY_CHANGING/_CHANGED (avoid flooding). */ |
| public static final int DEFAULT_MIN_EVENT_DELAY = 3; |
| @Property(intValue=DEFAULT_MIN_EVENT_DELAY) |
| public static final String MIN_EVENT_DELAY_KEY = "minEventDelay"; |
| protected int minEventDelay = DEFAULT_MIN_EVENT_DELAY; |
| |
| /** Configure the socket connect timeout for topology connectors. */ |
| public static final int DEFAULT_SOCKET_CONNECT_TIMEOUT = 10; |
| @Property(intValue=DEFAULT_SOCKET_CONNECT_TIMEOUT) |
| public static final String SOCKET_CONNECT_TIMEOUT_KEY = "socketConnectTimeout"; |
| private int socketConnectTimeout = DEFAULT_SOCKET_CONNECT_TIMEOUT; |
| |
| /** Configure the socket read timeout (SO_TIMEOUT) for topology connectors. */ |
| public static final int DEFAULT_SO_TIMEOUT = 10; |
| @Property(intValue=DEFAULT_SO_TIMEOUT) |
| public static final String SO_TIMEOUT_KEY = "soTimeout"; |
| private int soTimeout = DEFAULT_SO_TIMEOUT; |
| |
| /** URLs where to join a topology, eg http://localhost:4502/libs/sling/topology/connector */ |
| @Property(cardinality=1024) |
| public static final String TOPOLOGY_CONNECTOR_URLS_KEY = "topologyConnectorUrls"; |
| private URL[] topologyConnectorUrls = {null}; |
| |
| /** list of ips and/or hostnames which are allowed to connect to /libs/sling/topology/connector */ |
| private static final String[] DEFAULT_TOPOLOGY_CONNECTOR_WHITELIST = {"localhost","127.0.0.1"}; |
| @Property(value={"localhost","127.0.0.1"}) |
| public static final String TOPOLOGY_CONNECTOR_WHITELIST_KEY = "topologyConnectorWhitelist"; |
| protected String[] topologyConnectorWhitelist = DEFAULT_TOPOLOGY_CONNECTOR_WHITELIST; |
| |
| /** Path of resource where to keep discovery information, e.g /var/discovery/oak/ */ |
| private static final String DEFAULT_DISCOVERY_RESOURCE_PATH = "/var/discovery/oak/"; |
| @Property(value=DEFAULT_DISCOVERY_RESOURCE_PATH, propertyPrivate=true) |
| public static final String DISCOVERY_RESOURCE_PATH_KEY = "discoveryResourcePath"; |
| protected String discoveryResourcePath = DEFAULT_DISCOVERY_RESOURCE_PATH; |
| |
| /** |
| * If set to true, local-loops of topology connectors are automatically stopped when detected so. |
| */ |
| @Property(boolValue=false) |
| private static final String AUTO_STOP_LOCAL_LOOP_ENABLED = "autoStopLocalLoopEnabled"; |
| |
| /** |
| * If set to true, request body will be gzipped - only works if counter-part accepts gzip-requests! |
| */ |
| @Property(boolValue=false) |
| private static final String GZIP_CONNECTOR_REQUESTS_ENABLED = "gzipConnectorRequestsEnabled"; |
| |
| /** |
| * If set to true, hmac is enabled and the white list is disabled. |
| */ |
| @Property(boolValue=false) |
| private static final String HMAC_ENABLED = "hmacEnabled"; |
| |
| /** |
| * If set to true, and the whitelist is disabled, messages will be encrypted. |
| */ |
| @Property(boolValue=false) |
| private static final String ENCRYPTION_ENABLED = "enableEncryption"; |
| |
| /** |
| * The value fo the shared key, shared amongst all instances in the same cluster. |
| */ |
| @Property |
| private static final String SHARED_KEY = "sharedKey"; |
| |
| /** |
| * The default lifetime of a HMAC shared key in ms. (4h) |
| */ |
| private static final long DEFAULT_SHARED_KEY_INTERVAL = 3600*1000*4; |
| |
| @Property(longValue=DEFAULT_SHARED_KEY_INTERVAL) |
| private static final String SHARED_KEY_INTERVAL = "hmacSharedKeyTTL"; |
| |
| /** |
| * The property for defining the backoff factor for standby (loop) connectors |
| */ |
| @Property |
| private static final String BACKOFF_STANDBY_FACTOR = "backoffStandbyFactor"; |
| private static final int DEFAULT_BACKOFF_STANDBY_FACTOR = 5; |
| |
| /** |
| * The property for defining the maximum backoff factor for stable connectors |
| */ |
| @Property |
| private static final String BACKOFF_STABLE_FACTOR = "backoffStableFactor"; |
| private static final int DEFAULT_BACKOFF_STABLE_FACTOR = 5; |
| |
| /** True when auto-stop of a local-loop is enabled. Default is false. **/ |
| private boolean autoStopLocalLoopEnabled; |
| |
| /** |
| * True when the hmac is enabled and signing is disabled. |
| */ |
| private boolean hmacEnabled; |
| |
| /** |
| * the shared key. |
| */ |
| private String sharedKey; |
| |
| /** |
| * The key interval. |
| */ |
| private long keyInterval; |
| |
| /** |
| * true when encryption is enabled. |
| */ |
| private boolean encryptionEnabled; |
| |
| /** |
| * true when topology connector requests should be gzipped |
| */ |
| private boolean gzipConnectorRequestsEnabled; |
| |
| /** the backoff factor to be used for standby (loop) connectors **/ |
| private int backoffStandbyFactor = DEFAULT_BACKOFF_STANDBY_FACTOR; |
| |
| /** the maximum backoff factor to be used for stable connectors **/ |
| private int backoffStableFactor = DEFAULT_BACKOFF_STABLE_FACTOR; |
| |
| /** |
| * Whether, on top of waiting for deactivating instances, |
| * a syncToken should also be used |
| */ |
| private boolean syncTokenEnabled; |
| |
| @Activate |
| protected void activate(final Map<String, Object> properties) { |
| logger.debug("activate: config activated."); |
| configure(properties); |
| } |
| |
| protected void configure(final Map<String, Object> properties) { |
| this.connectorPingTimeout = PropertiesUtil.toLong( |
| properties.get(TOPOLOGY_CONNECTOR_TIMEOUT_KEY), |
| DEFAULT_TOPOLOGY_CONNECTOR_TIMEOUT); |
| logger.debug("configure: connectorPingTimeout='{}'", |
| this.connectorPingTimeout); |
| |
| this.connectorPingInterval = PropertiesUtil.toLong( |
| properties.get(TOPOLOGY_CONNECTOR_INTERVAL_KEY), |
| DEFAULT_TOPOLOGY_CONNECTOR_INTERVAL); |
| logger.debug("configure: connectorPingInterval='{}'", |
| this.connectorPingInterval); |
| |
| this.discoveryLiteCheckInterval = PropertiesUtil.toLong( |
| properties.get(DISCOVERY_LITE_CHECK_INTERVAL_KEY), |
| DEFAULT_DISCOVERY_LITE_CHECK_INTERVAL); |
| logger.debug("configure: discoveryLiteCheckInterval='{}'", |
| this.discoveryLiteCheckInterval); |
| |
| this.clusterSyncServiceTimeout = PropertiesUtil.toLong( |
| properties.get(CLUSTER_SYNC_SERVICE_TIMEOUT_KEY), |
| DEFAULT_CLUSTER_SYNC_SERVICE_TIMEOUT); |
| logger.debug("configure: clusterSyncServiceTimeout='{}'", |
| this.clusterSyncServiceTimeout); |
| |
| this.clusterSyncServiceInterval = PropertiesUtil.toLong( |
| properties.get(CLUSTER_SYNC_SERVICE_INTERVAL_KEY), |
| DEFAULT_CLUSTER_SYNC_SERVICE_TIMEOUT); |
| logger.debug("configure: clusterSyncServiceInterval='{}'", |
| this.clusterSyncServiceInterval); |
| |
| this.minEventDelay = PropertiesUtil.toInteger( |
| properties.get(MIN_EVENT_DELAY_KEY), |
| DEFAULT_MIN_EVENT_DELAY); |
| logger.debug("configure: minEventDelay='{}'", |
| this.minEventDelay); |
| |
| this.socketConnectTimeout = PropertiesUtil.toInteger( |
| properties.get(SOCKET_CONNECT_TIMEOUT_KEY), |
| DEFAULT_SOCKET_CONNECT_TIMEOUT); |
| logger.debug("configure: socketConnectTimeout='{}'", |
| this.socketConnectTimeout); |
| |
| this.soTimeout = PropertiesUtil.toInteger( |
| properties.get(SO_TIMEOUT_KEY), |
| DEFAULT_SO_TIMEOUT); |
| logger.debug("configure: soTimeout='{}'", |
| this.soTimeout); |
| |
| |
| String[] topologyConnectorUrlsStr = PropertiesUtil.toStringArray( |
| properties.get(TOPOLOGY_CONNECTOR_URLS_KEY), null); |
| if (topologyConnectorUrlsStr!=null && topologyConnectorUrlsStr.length > 0) { |
| List<URL> urls = new LinkedList<URL>(); |
| for (int i = 0; i < topologyConnectorUrlsStr.length; i++) { |
| String anUrlStr = topologyConnectorUrlsStr[i]; |
| try { |
| if (anUrlStr!=null && anUrlStr.length()>0) { |
| URL url = new URL(anUrlStr); |
| logger.debug("configure: a topologyConnectorbUrl='{}'", |
| url); |
| urls.add(url); |
| } |
| } catch (MalformedURLException e) { |
| logger.error("configure: could not set a topologyConnectorUrl: " + e, |
| e); |
| } |
| } |
| if (urls.size()>0) { |
| this.topologyConnectorUrls = urls.toArray(new URL[urls.size()]); |
| logger.debug("configure: number of topologyConnectorUrls='{}''", |
| urls.size()); |
| } else { |
| this.topologyConnectorUrls = null; |
| logger.debug("configure: no (valid) topologyConnectorUrls configured"); |
| } |
| } else { |
| this.topologyConnectorUrls = null; |
| logger.debug("configure: no (valid) topologyConnectorUrls configured"); |
| } |
| this.topologyConnectorWhitelist = PropertiesUtil.toStringArray( |
| properties.get(TOPOLOGY_CONNECTOR_WHITELIST_KEY), |
| DEFAULT_TOPOLOGY_CONNECTOR_WHITELIST); |
| logger.debug("configure: topologyConnectorWhitelist='{}'", |
| this.topologyConnectorWhitelist); |
| |
| this.discoveryResourcePath = PropertiesUtil.toString( |
| properties.get(DISCOVERY_RESOURCE_PATH_KEY), |
| ""); |
| while(this.discoveryResourcePath.endsWith("/")) { |
| this.discoveryResourcePath = this.discoveryResourcePath.substring(0, |
| this.discoveryResourcePath.length()-1); |
| } |
| this.discoveryResourcePath = this.discoveryResourcePath + "/"; |
| if (this.discoveryResourcePath==null || this.discoveryResourcePath.length()<=1) { |
| // if the path is empty, or /, then use the default |
| this.discoveryResourcePath = DEFAULT_DISCOVERY_RESOURCE_PATH; |
| } |
| logger.debug("configure: discoveryResourcePath='{}'", |
| this.discoveryResourcePath); |
| |
| autoStopLocalLoopEnabled = PropertiesUtil.toBoolean(properties.get(AUTO_STOP_LOCAL_LOOP_ENABLED), false); |
| gzipConnectorRequestsEnabled = PropertiesUtil.toBoolean(properties.get(GZIP_CONNECTOR_REQUESTS_ENABLED), false); |
| |
| hmacEnabled = PropertiesUtil.toBoolean(properties.get(HMAC_ENABLED), true); |
| encryptionEnabled = PropertiesUtil.toBoolean(properties.get(ENCRYPTION_ENABLED), false); |
| syncTokenEnabled = PropertiesUtil.toBoolean(properties.get(SYNC_TOKEN_ENABLED), true); |
| sharedKey = PropertiesUtil.toString(properties.get(SHARED_KEY), null); |
| keyInterval = PropertiesUtil.toLong(SHARED_KEY_INTERVAL, DEFAULT_SHARED_KEY_INTERVAL); |
| |
| backoffStandbyFactor = PropertiesUtil.toInteger(properties.get(BACKOFF_STANDBY_FACTOR), |
| DEFAULT_BACKOFF_STANDBY_FACTOR); |
| backoffStableFactor = PropertiesUtil.toInteger(properties.get(BACKOFF_STABLE_FACTOR), |
| DEFAULT_BACKOFF_STABLE_FACTOR); |
| } |
| |
| /** |
| * Returns the socket connect() timeout used by the topology connector, 0 disables the timeout |
| * @return the socket connect() timeout used by the topology connector, 0 disables the timeout |
| */ |
| public int getSocketConnectTimeout() { |
| return socketConnectTimeout; |
| } |
| |
| /** |
| * Returns the socket read timeout (SO_TIMEOUT) used by the topology connector, 0 disables the timeout |
| * @return the socket read timeout (SO_TIMEOUT) used by the topology connector, 0 disables the timeout |
| */ |
| public int getSoTimeout() { |
| return soTimeout; |
| } |
| |
| /** |
| * Returns the minimum time (in seconds) between sending TOPOLOGY_CHANGING/_CHANGED events - to avoid flooding |
| * @return the minimum time (in seconds) between sending TOPOLOGY_CHANGING/_CHANGED events - to avoid flooding |
| */ |
| public int getMinEventDelay() { |
| return minEventDelay; |
| } |
| |
| /** |
| * Returns the URLs to which to open a topology connector - or null/empty if no topology connector |
| * is configured (default is null) |
| * @return the URLs to which to open a topology connector - or null/empty if no topology connector |
| * is configured |
| */ |
| public URL[] getTopologyConnectorURLs() { |
| return topologyConnectorUrls; |
| } |
| |
| /** |
| * Returns a comma separated list of hostnames and/or ip addresses which are allowed as |
| * remote hosts to open connections to the topology connector servlet |
| * @return a comma separated list of hostnames and/or ip addresses which are allowed as |
| * remote hosts to open connections to the topology connector servlet |
| */ |
| public String[] getTopologyConnectorWhitelist() { |
| return topologyConnectorWhitelist; |
| } |
| |
| protected String getDiscoveryResourcePath() { |
| return discoveryResourcePath; |
| } |
| |
| /** |
| * Returns the resource path where cluster instance informations are stored. |
| * @return the resource path where cluster instance informations are stored |
| */ |
| public String getClusterInstancesPath() { |
| return getDiscoveryResourcePath() + CLUSTERINSTANCES_RESOURCE; |
| } |
| |
| @Override |
| public String getSyncTokenPath() { |
| return getDiscoveryResourcePath() + SYNC_TOKEN_RESOURCE; |
| } |
| |
| @Override |
| public String getIdMapPath() { |
| return getDiscoveryResourcePath() + ID_MAP_RESOURCE; |
| } |
| |
| /** |
| * @return true if hmac is enabled. |
| */ |
| public boolean isHmacEnabled() { |
| return hmacEnabled; |
| } |
| |
| /** |
| * @return the shared key |
| */ |
| public String getSharedKey() { |
| return sharedKey; |
| } |
| |
| /** |
| * @return the interval of the shared key for hmac. |
| */ |
| public long getKeyInterval() { |
| return keyInterval; |
| } |
| |
| /** |
| * @return true if encryption is enabled. |
| */ |
| public boolean isEncryptionEnabled() { |
| return encryptionEnabled; |
| } |
| |
| /** |
| * @return true if requests on the topology connector should be gzipped |
| * (which only works if the server accepts that.. ie discovery.impl 1.0.4+) |
| */ |
| public boolean isGzipConnectorRequestsEnabled() { |
| return gzipConnectorRequestsEnabled; |
| } |
| |
| /** |
| * @return true if the auto-stopping of local-loop topology connectors is enabled. |
| */ |
| public boolean isAutoStopLocalLoopEnabled() { |
| return autoStopLocalLoopEnabled; |
| } |
| |
| /** |
| * Returns the backoff factor to be used for standby (loop) connectors |
| * @return the backoff factor to be used for standby (loop) connectors |
| */ |
| public int getBackoffStandbyFactor() { |
| return backoffStandbyFactor; |
| } |
| |
| /** |
| * Returns the (maximum) backoff factor to be used for stable connectors |
| * @return the (maximum) backoff factor to be used for stable connectors |
| */ |
| public int getBackoffStableFactor() { |
| return backoffStableFactor; |
| } |
| |
| /** |
| * Returns the backoff interval for standby (loop) connectors in seconds |
| * @return the backoff interval for standby (loop) connectors in seconds |
| */ |
| public long getBackoffStandbyInterval() { |
| final int factor = getBackoffStandbyFactor(); |
| if (factor<=1) { |
| return -1; |
| } else { |
| return factor * getConnectorPingInterval(); |
| } |
| } |
| |
| @Override |
| public long getConnectorPingInterval() { |
| return connectorPingInterval; |
| } |
| |
| @Override |
| public long getConnectorPingTimeout() { |
| return connectorPingTimeout; |
| } |
| |
| public long getDiscoveryLiteCheckInterval() { |
| return discoveryLiteCheckInterval; |
| } |
| |
| @Override |
| public long getClusterSyncServiceTimeoutMillis() { |
| return clusterSyncServiceTimeout * 1000; |
| } |
| |
| @Override |
| public long getClusterSyncServiceIntervalMillis() { |
| return clusterSyncServiceInterval * 1000; |
| } |
| |
| public boolean getSyncTokenEnabled() { |
| return syncTokenEnabled; |
| } |
| } |