You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by cz...@apache.org on 2013/04/22 11:27:37 UTC
svn commit: r1470424 [1/5] - in
/sling/trunk/contrib/extensions/discovery/impl: ./ src/ src/main/
src/main/java/ src/main/java/org/ src/main/java/org/apache/
src/main/java/org/apache/sling/ src/main/java/org/apache/sling/discovery/
src/main/java/org/ap...
Author: cziegeler
Date: Mon Apr 22 09:27:35 2013
New Revision: 1470424
URL: http://svn.apache.org/r1470424
Log:
SLING-2827 : discovery.impl: a resource based implementation of the discovery.api . Committed initial contribution from Stefan Egli (md5: d8891e5401114b2a629d3ff01044a1d6)
Added:
sling/trunk/contrib/extensions/discovery/impl/ (with props)
sling/trunk/contrib/extensions/discovery/impl/pom.xml (with props)
sling/trunk/contrib/extensions/discovery/impl/src/
sling/trunk/contrib/extensions/discovery/impl/src/main/
sling/trunk/contrib/extensions/discovery/impl/src/main/java/
sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/
sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/
sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/
sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/
sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/
sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/Config.java (with props)
sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/DiscoveryServiceImpl.java (with props)
sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/InfrastructurePropertyProvider.java (with props)
sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/TopologyWebConsolePlugin.java (with props)
sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/cluster/
sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/cluster/ClusterViewService.java (with props)
sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/cluster/ClusterViewServiceImpl.java (with props)
sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/cluster/voting/
sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/cluster/voting/VotingHandler.java (with props)
sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/cluster/voting/VotingHelper.java (with props)
sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/cluster/voting/VotingView.java (with props)
sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/common/
sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/common/DefaultClusterViewImpl.java (with props)
sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/common/DefaultInstanceDescriptionImpl.java (with props)
sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/common/View.java (with props)
sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/common/ViewHelper.java (with props)
sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/common/heartbeat/
sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/common/heartbeat/HeartbeatHandler.java (with props)
sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/common/resource/
sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/common/resource/EstablishedClusterView.java (with props)
sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/common/resource/EstablishedInstanceDescription.java (with props)
sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/common/resource/IsolatedInstanceDescription.java (with props)
sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/common/resource/ResourceHelper.java (with props)
sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/topology/
sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/topology/TopologyChangeHandler.java (with props)
sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/topology/TopologyViewImpl.java (with props)
sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/topology/announcement/
sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/topology/announcement/Announcement.java (with props)
sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/topology/announcement/AnnouncementFilter.java (with props)
sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/topology/announcement/AnnouncementRegistry.java (with props)
sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/topology/announcement/AnnouncementRegistryImpl.java (with props)
sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/topology/announcement/IncomingInstanceDescription.java (with props)
sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/topology/connector/
sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/topology/connector/ConnectorRegistry.java (with props)
sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/topology/connector/ConnectorRegistryImpl.java (with props)
sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/topology/connector/TopologyConnectorClient.java (with props)
sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/topology/connector/TopologyConnectorClientInformation.java (with props)
sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/topology/connector/TopologyConnectorServlet.java (with props)
sling/trunk/contrib/extensions/discovery/impl/src/main/resources/
sling/trunk/contrib/extensions/discovery/impl/src/main/resources/OSGI-INF/
sling/trunk/contrib/extensions/discovery/impl/src/main/resources/OSGI-INF/metatype/
sling/trunk/contrib/extensions/discovery/impl/src/main/resources/OSGI-INF/metatype/metatype.properties (with props)
sling/trunk/contrib/extensions/discovery/impl/src/test/
sling/trunk/contrib/extensions/discovery/impl/src/test/java/
sling/trunk/contrib/extensions/discovery/impl/src/test/java/org/
sling/trunk/contrib/extensions/discovery/impl/src/test/java/org/apache/
sling/trunk/contrib/extensions/discovery/impl/src/test/java/org/apache/sling/
sling/trunk/contrib/extensions/discovery/impl/src/test/java/org/apache/sling/discovery/
sling/trunk/contrib/extensions/discovery/impl/src/test/java/org/apache/sling/discovery/impl/
sling/trunk/contrib/extensions/discovery/impl/src/test/java/org/apache/sling/discovery/impl/cluster/
sling/trunk/contrib/extensions/discovery/impl/src/test/java/org/apache/sling/discovery/impl/cluster/ClusterTest.java (with props)
sling/trunk/contrib/extensions/discovery/impl/src/test/java/org/apache/sling/discovery/impl/cluster/SingleInstanceTest.java (with props)
sling/trunk/contrib/extensions/discovery/impl/src/test/java/org/apache/sling/discovery/impl/cluster/helpers/
sling/trunk/contrib/extensions/discovery/impl/src/test/java/org/apache/sling/discovery/impl/cluster/helpers/AcceptsMultiple.java (with props)
sling/trunk/contrib/extensions/discovery/impl/src/test/java/org/apache/sling/discovery/impl/cluster/helpers/AcceptsParticularTopologyEvent.java (with props)
sling/trunk/contrib/extensions/discovery/impl/src/test/java/org/apache/sling/discovery/impl/cluster/helpers/AssertingDiscoveryAware.java (with props)
sling/trunk/contrib/extensions/discovery/impl/src/test/java/org/apache/sling/discovery/impl/cluster/helpers/TopologyEventAsserter.java (with props)
sling/trunk/contrib/extensions/discovery/impl/src/test/java/org/apache/sling/discovery/impl/common/
sling/trunk/contrib/extensions/discovery/impl/src/test/java/org/apache/sling/discovery/impl/common/ClusterViewTest.java (with props)
sling/trunk/contrib/extensions/discovery/impl/src/test/java/org/apache/sling/discovery/impl/common/InstanceDescriptionTest.java (with props)
sling/trunk/contrib/extensions/discovery/impl/src/test/java/org/apache/sling/discovery/impl/common/resource/
sling/trunk/contrib/extensions/discovery/impl/src/test/java/org/apache/sling/discovery/impl/common/resource/EstablishedInstanceDescriptionTest.java (with props)
sling/trunk/contrib/extensions/discovery/impl/src/test/java/org/apache/sling/discovery/impl/setup/
sling/trunk/contrib/extensions/discovery/impl/src/test/java/org/apache/sling/discovery/impl/setup/Instance.java (with props)
sling/trunk/contrib/extensions/discovery/impl/src/test/java/org/apache/sling/discovery/impl/setup/MockFactory.java (with props)
sling/trunk/contrib/extensions/discovery/impl/src/test/java/org/apache/sling/discovery/impl/setup/MockedResource.java (with props)
sling/trunk/contrib/extensions/discovery/impl/src/test/java/org/apache/sling/discovery/impl/setup/MockedResourceResolver.java (with props)
sling/trunk/contrib/extensions/discovery/impl/src/test/java/org/apache/sling/discovery/impl/setup/OSGiFactory.java (with props)
sling/trunk/contrib/extensions/discovery/impl/src/test/java/org/apache/sling/discovery/impl/setup/OSGiMock.java (with props)
sling/trunk/contrib/extensions/discovery/impl/src/test/java/org/apache/sling/discovery/impl/setup/PropertyProviderImpl.java (with props)
sling/trunk/contrib/extensions/discovery/impl/src/test/java/org/apache/sling/discovery/impl/topology/
sling/trunk/contrib/extensions/discovery/impl/src/test/java/org/apache/sling/discovery/impl/topology/TopologyTestHelper.java (with props)
sling/trunk/contrib/extensions/discovery/impl/src/test/java/org/apache/sling/discovery/impl/topology/TopologyViewImplTest.java (with props)
sling/trunk/contrib/extensions/discovery/impl/src/test/java/org/apache/sling/discovery/impl/topology/announcement/
sling/trunk/contrib/extensions/discovery/impl/src/test/java/org/apache/sling/discovery/impl/topology/announcement/IncomingInstanceDescriptionTest.java (with props)
sling/trunk/contrib/extensions/discovery/impl/src/test/java/org/apache/sling/discovery/impl/topology/announcement/TopologyAnnouncementRegistryTest.java (with props)
sling/trunk/contrib/extensions/discovery/impl/src/test/java/org/apache/sling/discovery/impl/topology/connector/
sling/trunk/contrib/extensions/discovery/impl/src/test/java/org/apache/sling/discovery/impl/topology/connector/ConnectorRegistryTest.java (with props)
Propchange: sling/trunk/contrib/extensions/discovery/impl/
------------------------------------------------------------------------------
--- svn:ignore (added)
+++ svn:ignore Mon Apr 22 09:27:35 2013
@@ -0,0 +1,13 @@
+target
+bin
+*.iml
+*.ipr
+*.iws
+.settings
+.project
+.classpath
+.externalToolBuilders
+maven-eclipse.xml
+
+
+
Added: sling/trunk/contrib/extensions/discovery/impl/pom.xml
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/extensions/discovery/impl/pom.xml?rev=1470424&view=auto
==============================================================================
--- sling/trunk/contrib/extensions/discovery/impl/pom.xml (added)
+++ sling/trunk/contrib/extensions/discovery/impl/pom.xml Mon Apr 22 09:27:35 2013
@@ -0,0 +1,172 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+ 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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.apache.sling</groupId>
+ <artifactId>sling</artifactId>
+ <version>15</version>
+ <relativePath>../../../parent/pom.xml</relativePath>
+ </parent>
+
+ <artifactId>org.apache.sling.discovery.impl</artifactId>
+ <packaging>bundle</packaging>
+ <version>0.0.2-SNAPSHOT</version>
+
+ <name>Apache Sling Resource-Based Discovery Service</name>
+ <description>Implementation of Apache Sling Discovery based on Sling Resource providing a ClusterView through resource-clustering (eg jackrabbit clustering) and a TopologyView through HTTP POST heartbeats announcing sub-topologies to each other.</description>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <configuration>
+ <redirectTestOutputToFile>true</redirectTestOutputToFile>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-scr-plugin</artifactId>
+ <version>1.11.0</version>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <extensions>true</extensions>
+ </plugin>
+ </plugins>
+ </build>
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.apache.felix.scr.annotations</artifactId>
+ <version>1.9.0</version>
+ </dependency>
+ <dependency>
+ <groupId>biz.aQute</groupId>
+ <artifactId>bndlib</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.compendium</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.sling</groupId>
+ <artifactId>org.apache.sling.jcr.api</artifactId>
+ <version>2.1.0</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>javax.jcr</groupId>
+ <artifactId>jcr</artifactId>
+ <version>2.0</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.jackrabbit</groupId>
+ <artifactId>jackrabbit-api</artifactId>
+ <version>2.2.4</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.sling</groupId>
+ <artifactId>org.apache.sling.commons.osgi</artifactId>
+ <version>2.1.0</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.sling</groupId>
+ <artifactId>org.apache.sling.settings</artifactId>
+ <version>1.2.2</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.sling</groupId>
+ <artifactId>org.apache.sling.discovery.api</artifactId>
+ <version>0.1.0-SNAPSHOT</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.sling</groupId>
+ <artifactId>org.apache.sling.api</artifactId>
+ <version>2.3.0</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.sling</groupId>
+ <artifactId>org.apache.sling.commons.scheduler</artifactId>
+ <version>2.3.4</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.apache.felix.webconsole</artifactId>
+ <version>3.0.0</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>javax.servlet</groupId>
+ <artifactId>servlet-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>commons-httpclient</groupId>
+ <artifactId>commons-httpclient</artifactId>
+ <version>3.1</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.sling</groupId>
+ <artifactId>org.apache.sling.commons.json</artifactId>
+ <version>2.0.6</version>
+ <scope>provided</scope>
+ </dependency>
+ <!-- Testing -->
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>junit-addons</groupId>
+ <artifactId>junit-addons</artifactId>
+ <version>1.4</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.jmock</groupId>
+ <artifactId>jmock-junit4</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.sling</groupId>
+ <artifactId>org.apache.sling.commons.testing</artifactId>
+ <version>2.0.14</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+</project>
Propchange: sling/trunk/contrib/extensions/discovery/impl/pom.xml
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: sling/trunk/contrib/extensions/discovery/impl/pom.xml
------------------------------------------------------------------------------
svn:keywords = Id
Propchange: sling/trunk/contrib/extensions/discovery/impl/pom.xml
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/Config.java
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/Config.java?rev=1470424&view=auto
==============================================================================
--- sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/Config.java (added)
+++ sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/Config.java Mon Apr 22 09:27:35 2013
@@ -0,0 +1,219 @@
+/*
+ * 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.impl;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Dictionary;
+
+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.osgi.service.component.ComponentContext;
+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 'description'
+ */
+@Component(metatype = true, label = "Sling Resource-Based Discovery Service Configuration")
+@Service(value = { Config.class })
+public class Config {
+
+ private final Logger logger = LoggerFactory.getLogger(this.getClass());
+
+ /** node used to keep instance information such as last heartbeat, properties, incoming announcements **/
+ private static final String CLUSTERINSTANCES_NODE = "/clusterInstances";
+
+ /** node used to keep the currently established view **/
+ private static final String ESTABLISHED_VIEW_NODE = "/establishedView";
+
+ /** node used to keep the previously established view **/
+ private static final String PREVIOUS_VIEW_NODE = "/previousView";
+
+ /** node used to keep ongoing votings **/
+ private static final String ONGOING_VOTING_NODE = "/ongoingVotings";
+
+ public static final long DEFAULT_HEARTBEAT_TIMEOUT = 20;
+ @Property(label = "Heartbeat timeout (seconds)", description = "Configure the timeout (in seconds) after which an instance is considered dead/crashed, eg 20.")
+ public static final String HEARTBEAT_TIMEOUT_KEY = "heartbeatTimeout";
+ private long heartbeatTimeout = DEFAULT_HEARTBEAT_TIMEOUT;
+
+ public static final long DEFAULT_HEARTBEAT_INTERVAL = 15;
+ @Property(label = "Heartbeat interval (seconds)", description = "Configure the interval (in seconds) according to which the heartbeats are exchanged in the topology, eg 15.")
+ public static final String HEARTBEAT_INTERVAL_KEY = "heartbeatInterval";
+ private long heartbeatInterval = DEFAULT_HEARTBEAT_INTERVAL;
+
+ @Property(label = "Topology Connector URL", description = "URL where to join a topology, eg http://localhost:4502/libs/sling/topology/connector")
+ public static final String TOPOLOGY_CONNECTOR_URL_KEY = "topologyConnectorUrl";
+ private URL topologyConnectorUrl = null;
+
+ private static final String DEFAULT_TOPOLOGY_CONNECTOR_WHITELIST = "localhost,127.0.0.1";
+ @Property(label = "Topology Connector Whitelist", description = "comma separated list of ips and/or hostnames which are allowed to connect to /libs/sling/topology/connector")
+ public static final String TOPOLOGY_CONNECTOR_WHITELIST_KEY = "topologyConnectorWhitelist";
+ private String topologyConnectorWhitelist = DEFAULT_TOPOLOGY_CONNECTOR_WHITELIST;
+
+ private static final String DEFAULT_DISCOVERY_RESOURCE_PATH = "/var/discovery/impl";
+ @Property(label = "Discovery Resource Path", description = "Path of resource where to keep discovery information, e.g /var/discovery/impl")
+ public static final String DISCOVERY_RESOURCE_PATH_KEY = "discoveryResourcePath";
+ private String discoveryResourcePath = DEFAULT_DISCOVERY_RESOURCE_PATH;
+
+ @Property(label = "Repository Descriptor Name", description = "Name of the repository descriptor to be taken into account for leader election: " +
+ "those instances have preference to become leader which have the corresponding descriptor value of 'false'")
+ public static final String LEADER_ELECTION_REPOSITORY_DESCRIPTOR_NAME_KEY = "leaderElectionRepositoryDescriptor";
+ private String leaderElectionRepositoryDescriptor = null;
+
+ protected void activate(final ComponentContext componentContext) {
+ logger.debug("activate: config activated.");
+ configure(componentContext.getProperties());
+ }
+
+ protected void configure(final Dictionary<?, ?> properties) {
+ this.heartbeatTimeout = PropertiesUtil.toLong(
+ properties.get(HEARTBEAT_TIMEOUT_KEY),
+ DEFAULT_HEARTBEAT_TIMEOUT);
+ logger.debug("configure: heartbeatTimeout='{}''", this.heartbeatTimeout);
+ this.heartbeatInterval = PropertiesUtil.toLong(
+ properties.get(HEARTBEAT_INTERVAL_KEY),
+ DEFAULT_HEARTBEAT_INTERVAL);
+ logger.debug("configure: heartbeatInterval='{}''",
+ this.heartbeatInterval);
+ String topologyConnectorUrlStr = PropertiesUtil.toString(
+ properties.get(TOPOLOGY_CONNECTOR_URL_KEY), null);
+ try {
+ this.topologyConnectorUrl = new URL(topologyConnectorUrlStr);
+ logger.debug("configure: topologyConnectorUrl='{}''",
+ this.topologyConnectorUrl);
+ } catch (MalformedURLException e) {
+ logger.error("configure: could not set topologyConnectorUrl: " + e,
+ e);
+ }
+ this.topologyConnectorWhitelist = PropertiesUtil.toString(
+ 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);
+
+ this.leaderElectionRepositoryDescriptor = PropertiesUtil.toString(
+ properties.get(LEADER_ELECTION_REPOSITORY_DESCRIPTOR_NAME_KEY),
+ null);
+ logger.debug("configure: leaderElectionRepositoryDescriptor='{}''",
+ this.leaderElectionRepositoryDescriptor);
+ }
+
+ /**
+ * Returns the timeout (in seconds) after which an instance or voting is considered invalid/timed out
+ * @return the timeout (in seconds) after which an instance or voting is considered invalid/timed out
+ */
+ public long getHeartbeatTimeout() {
+ return heartbeatTimeout;
+ }
+
+ /**
+ * Returns the interval (in seconds) in which heartbeats are sent
+ * @return the interval (in seconds) in which heartbeats are sent
+ */
+ public long getHeartbeatInterval() {
+ return heartbeatInterval;
+ }
+
+ /**
+ * Returns the URL to which to open a topology connector - or null if no topology connector
+ * is configured (default is null)
+ * @return the URL to which to open a topology connector - or null if no topology connector
+ * is configured
+ */
+ public URL getTopologyConnectorURL() {
+ return topologyConnectorUrl;
+ }
+
+ /**
+ * 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;
+ }
+
+ /**
+ * Returns the resource path where cluster instance informations are stored.
+ * @return the resource path where cluster instance informations are stored
+ */
+ public String getClusterInstancesPath() {
+ return discoveryResourcePath + CLUSTERINSTANCES_NODE;
+ }
+
+ /**
+ * Returns the resource path where the established view is stored.
+ * @return the resource path where the established view is stored
+ */
+ public String getEstablishedViewPath() {
+ return discoveryResourcePath + ESTABLISHED_VIEW_NODE;
+ }
+
+ /**
+ * Returns the resource path where ongoing votings are stored.
+ * @return the resource path where ongoing votings are stored
+ */
+ public String getOngoingVotingsPath() {
+ return discoveryResourcePath + ONGOING_VOTING_NODE;
+ }
+
+ /**
+ * Returns the resource path where the previous view is stored.
+ * @return the resource path where the previous view is stored
+ */
+ public String getPreviousViewPath() {
+ return discoveryResourcePath + PREVIOUS_VIEW_NODE;
+ }
+
+ /**
+ * Returns the repository descriptor key which is to be included in the
+ * cluster leader election - or null.
+ * <p>
+ * When set, the value (treated as a boolean) of the repository descriptor
+ * is prepended to the leader election id.
+ * @return the repository descriptor key which is to be included in the
+ * cluster leader election - or null
+ */
+ public String getLeaderElectionRepositoryDescriptor() {
+ return leaderElectionRepositoryDescriptor;
+ }
+}
Propchange: sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/Config.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/Config.java
------------------------------------------------------------------------------
svn:keywords = author date id revision rev url
Propchange: sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/Config.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/DiscoveryServiceImpl.java
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/DiscoveryServiceImpl.java?rev=1470424&view=auto
==============================================================================
--- sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/DiscoveryServiceImpl.java (added)
+++ sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/DiscoveryServiceImpl.java Mon Apr 22 09:27:35 2013
@@ -0,0 +1,521 @@
+/*
+ * 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.impl;
+
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import javax.jcr.Node;
+import javax.jcr.Property;
+import javax.jcr.PropertyIterator;
+import javax.jcr.RepositoryException;
+
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.ReferencePolicy;
+import org.apache.felix.scr.annotations.Service;
+import org.apache.sling.api.resource.LoginException;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ResourceResolver;
+import org.apache.sling.api.resource.ResourceResolverFactory;
+import org.apache.sling.discovery.ClusterView;
+import org.apache.sling.discovery.DiscoveryAware;
+import org.apache.sling.discovery.DiscoveryService;
+import org.apache.sling.discovery.InstanceDescription;
+import org.apache.sling.discovery.PropertyProvider;
+import org.apache.sling.discovery.TopologyEvent;
+import org.apache.sling.discovery.TopologyEvent.Type;
+import org.apache.sling.discovery.TopologyView;
+import org.apache.sling.discovery.impl.cluster.ClusterViewService;
+import org.apache.sling.discovery.impl.common.heartbeat.HeartbeatHandler;
+import org.apache.sling.discovery.impl.common.resource.ResourceHelper;
+import org.apache.sling.discovery.impl.topology.TopologyViewImpl;
+import org.apache.sling.discovery.impl.topology.announcement.AnnouncementRegistry;
+import org.apache.sling.discovery.impl.topology.connector.ConnectorRegistry;
+import org.apache.sling.discovery.impl.topology.connector.TopologyConnectorClientInformation.OriginInfo;
+import org.apache.sling.settings.SlingSettingsService;
+import org.osgi.framework.Constants;
+import org.osgi.service.component.ComponentContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * This implementation of the cross-cluster service uses the view manager
+ * implementation for detecting changes in a cluster and only supports one
+ * cluster (of which this instance is part of).
+ */
+@Service(value = { DiscoveryService.class, DiscoveryServiceImpl.class })
+@Component(immediate = true)
+public class DiscoveryServiceImpl implements DiscoveryService {
+
+ private final Logger logger = LoggerFactory.getLogger(this.getClass());
+
+ @Reference
+ private SlingSettingsService settingsService;
+
+ @Reference(cardinality = ReferenceCardinality.OPTIONAL_MULTIPLE, policy = ReferencePolicy.DYNAMIC, referenceInterface = DiscoveryAware.class)
+ private DiscoveryAware[] discoveryAwares = new DiscoveryAware[0];
+
+ /**
+ * All property providers.
+ */
+ @Reference(cardinality = ReferenceCardinality.OPTIONAL_MULTIPLE, policy = ReferencePolicy.DYNAMIC, referenceInterface = PropertyProvider.class, updated = "updatedPropertyProvider")
+ private List<ProviderInfo> providerInfos = new ArrayList<ProviderInfo>();
+
+ /** lock object used for synching bind/unbind and topology event sending **/
+ private final Object lock = new Object();
+
+ /**
+ * whether or not this service is activated - necessary to avoid sending
+ * events to discovery awares before activate is done
+ **/
+ private boolean activated = false;
+
+ @Reference
+ private ResourceResolverFactory resourceResolverFactory;
+
+ @Reference
+ private HeartbeatHandler heartbeatHandler;
+
+ @Reference
+ private AnnouncementRegistry announcementRegistry;
+
+ @Reference
+ private ConnectorRegistry connectorRegistry;
+
+ @Reference
+ private ClusterViewService clusterViewService;
+
+ @Reference
+ private Config config;
+
+ /** the slingId of the local instance **/
+ private String slingId;
+
+ /** the old view previously valid and sent to the discoveryawares **/
+ private TopologyViewImpl oldView = null;
+
+ /**
+ * Activate this service
+ */
+ protected void activate(final ComponentContext context) {
+ logger.debug("DiscoveryServiceImpl activating...");
+
+ if (settingsService == null) {
+ throw new IllegalStateException("settingsService not found");
+ }
+ if (heartbeatHandler == null) {
+ throw new IllegalStateException("heartbeatHandler not found");
+ }
+
+ slingId = settingsService.getSlingId();
+
+ oldView = (TopologyViewImpl) getTopology();
+
+ // make sure the first heartbeat is issued as soon as possible - which
+ // is right after this service starts. since the two (discoveryservice
+ // and heartbeatHandler need to know each other, the discoveryservice
+ // is passed on to the heartbeatHandler in this initialize call).
+ heartbeatHandler.initialize(this,
+ clusterViewService.getIsolatedClusterViewId());
+
+ final DiscoveryAware[] registeredServices;
+ synchronized (lock) {
+ activated = true;
+ registeredServices = this.discoveryAwares;
+ doUpdateProperties();
+
+ }
+ TopologyViewImpl newView = (TopologyViewImpl) getTopology();
+ TopologyEvent event = new TopologyEvent(Type.TOPOLOGY_INIT, null,
+ newView);
+ for (final DiscoveryAware da : registeredServices) {
+ da.handleTopologyEvent(event);
+ }
+ oldView = newView;
+
+ URL topologyConnectorURL = config.getTopologyConnectorURL();
+ if (topologyConnectorURL != null) {
+ connectorRegistry.registerOutgoingConnection(clusterViewService,
+ topologyConnectorURL, OriginInfo.Config);
+ }
+
+ logger.debug("DiscoveryServiceImpl activated.");
+ }
+
+ /**
+ * Deactivate this service
+ */
+ protected void deactivate(final ComponentContext componentContext) {
+ logger.debug("DiscoveryServiceImpl deactivated.");
+ synchronized (lock) {
+ activated = false;
+ }
+ }
+
+ /**
+ * bind a discovery aware
+ */
+ protected void bindDiscoveryAware(final DiscoveryAware clusterAware) {
+
+ logger.debug("bindDiscoveryAware: Binding DiscoveryAware {}",
+ clusterAware);
+
+ boolean activated = false;
+ synchronized (lock) {
+ List<DiscoveryAware> currentList = new ArrayList<DiscoveryAware>(
+ Arrays.asList(discoveryAwares));
+ currentList.add(clusterAware);
+ this.discoveryAwares = currentList
+ .toArray(new DiscoveryAware[currentList.size()]);
+ activated = this.activated;
+ }
+
+ if (activated) {
+ clusterAware.handleTopologyEvent(new TopologyEvent(
+ Type.TOPOLOGY_INIT, null, getTopology()));
+ }
+ }
+
+ /**
+ * Unbind a discovery aware
+ */
+ protected void unbindDiscoveryAware(final DiscoveryAware clusterAware) {
+
+ logger.debug("unbindDiscoveryAware: Releasing DiscoveryAware {}",
+ clusterAware);
+
+ synchronized (lock) {
+ List<DiscoveryAware> currentList = new ArrayList<DiscoveryAware>(
+ Arrays.asList(discoveryAwares));
+ currentList.remove(clusterAware);
+ this.discoveryAwares = currentList
+ .toArray(new DiscoveryAware[currentList.size()]);
+ }
+ }
+
+ /**
+ * Bind a new property provider.
+ */
+ private void bindPropertyProvider(final PropertyProvider propertyProvider,
+ final Map<String, Object> props) {
+ logger.debug("bindPropertyProvider: Binding PropertyProvider {}",
+ propertyProvider);
+
+ final DiscoveryAware[] awares;
+ synchronized (lock) {
+ final ProviderInfo info = new ProviderInfo(propertyProvider, props);
+ this.providerInfos.add(info);
+ Collections.sort(this.providerInfos);
+ this.doUpdateProperties();
+ if (activated) {
+ awares = this.discoveryAwares;
+ } else {
+ awares = null;
+ }
+ }
+ if (awares != null) {
+ handlePotentialTopologyChange();
+ }
+ }
+
+ /**
+ * Update a property provider.
+ */
+ @SuppressWarnings("unused")
+ private void updatedPropertyProvider(
+ final PropertyProvider propertyProvider,
+ final Map<String, Object> props) {
+ logger.debug("bindPropertyProvider: Updating PropertyProvider {}",
+ propertyProvider);
+
+ this.unbindPropertyProvider(propertyProvider, props, false);
+ this.bindPropertyProvider(propertyProvider, props);
+ }
+
+ /**
+ * Unbind a property provider
+ */
+ @SuppressWarnings("unused")
+ private void unbindPropertyProvider(
+ final PropertyProvider propertyProvider,
+ final Map<String, Object> props) {
+ this.unbindPropertyProvider(propertyProvider, props, true);
+ }
+
+ /**
+ * Unbind a property provider
+ */
+ private void unbindPropertyProvider(
+ final PropertyProvider propertyProvider,
+ final Map<String, Object> props, final boolean inform) {
+ logger.debug("unbindPropertyProvider: Releasing PropertyProvider {}",
+ propertyProvider);
+
+ final DiscoveryAware[] awares;
+ synchronized (lock) {
+ final ProviderInfo info = new ProviderInfo(propertyProvider, props);
+ this.providerInfos.remove(info);
+ this.doUpdateProperties();
+ if (activated) {
+ awares = this.discoveryAwares;
+ } else {
+ awares = null;
+ }
+ }
+ if (inform && awares != null) {
+ handlePotentialTopologyChange();
+ }
+ }
+
+ /**
+ * Update the properties by inquiring the PropertyProvider's current values.
+ * <p>
+ * This method is invoked regularly by the heartbeatHandler.
+ * The properties are stored in the repository under Config.getClusterInstancesPath()
+ * and announced in the topology.
+ * <p>
+ * @see Config#getClusterInstancesPath()
+ */
+ private void doUpdateProperties() {
+ if (resourceResolverFactory == null) {
+ // cannot update the properties then..
+ logger.debug("doUpdateProperties: too early to update the properties. resourceResolverFactory not yet set.");
+ return;
+ } else {
+ logger.debug("doUpdateProperties: updating properties now..");
+ }
+
+ final Map<String, String> newProps = new HashMap<String, String>();
+ for (final ProviderInfo info : this.providerInfos) {
+ info.refreshProperties();
+ newProps.putAll(info.properties);
+ }
+
+ ResourceResolver resourceResolver = null;
+ try {
+ resourceResolver = resourceResolverFactory
+ .getAdministrativeResourceResolver(null);
+
+ Resource myInstance = ResourceHelper
+ .getOrCreateResource(
+ resourceResolver,
+ config.getClusterInstancesPath()
+ + "/" + slingId + "/properties");
+
+ Node node = myInstance.adaptTo(Node.class);
+
+ PropertyIterator pit = node.getProperties();
+ while (pit.hasNext()) {
+ Property p = pit.nextProperty();
+ if (newProps.containsKey(p.getName())) {
+ // perfect
+ continue;
+ } else if (p.getName().equals("jcr:primaryType")) {
+ // ignore
+ continue;
+ } else {
+ // remove
+ p.remove();
+ }
+ }
+
+ for (Iterator<Entry<String, String>> it = newProps.entrySet()
+ .iterator(); it.hasNext();) {
+ Entry<String, String> entry = it.next();
+ logger.debug("doUpdateProperties: " + entry.getKey() + "="
+ + entry.getValue());
+ node.setProperty(entry.getKey(), entry.getValue());
+ }
+
+ node.getSession().save();
+ } catch (LoginException e) {
+ logger.error(
+ "handleEvent: could not log in administratively: " + e, e);
+ throw new RuntimeException("Could not log in to repository (" + e
+ + ")", e);
+ } catch (RepositoryException e) {
+ logger.error("handleEvent: got a RepositoryException: " + e, e);
+ throw new RuntimeException(
+ "Exception while talking to repository (" + e + ")", e);
+ } finally {
+ if (resourceResolver != null) {
+ resourceResolver.close();
+ }
+ }
+
+ logger.debug("doUpdateProperties: updating properties done.");
+ }
+
+ /**
+ * @see DiscoveryService#getTopology()
+ */
+ public TopologyView getTopology() {
+ TopologyViewImpl topology = new TopologyViewImpl();
+
+ if (clusterViewService == null) {
+ throw new IllegalStateException(
+ "DiscoveryService not yet initialized with IClusterViewService");
+ }
+
+ ClusterView localClusterView = clusterViewService.getClusterView();
+
+ List<InstanceDescription> localInstances = localClusterView
+ .getInstances();
+ topology.addInstances(localInstances);
+
+ Collection<InstanceDescription> attachedInstances = announcementRegistry
+ .listInstances();
+ topology.addInstances(attachedInstances);
+
+ // TODO: isCurrent() might be wrong!!!
+
+ return topology;
+ }
+
+ /**
+ * Update the properties and sent a topology event if applicable
+ */
+ public void updateProperties() {
+ synchronized (lock) {
+ doUpdateProperties();
+ handlePotentialTopologyChange();
+ }
+ }
+
+ /**
+ * Internal handle method which checks if anything in the topology has
+ * changed and informs the DiscoveryAwares if such a change occurred.
+ * <p>
+ * All changes should go through this method. This method keeps track of
+ * oldView/newView as well.
+ */
+ private void handlePotentialTopologyChange() {
+ synchronized (lock) {
+ if (oldView == null) {
+ throw new IllegalStateException("oldView must not be null");
+ }
+ TopologyViewImpl newView = (TopologyViewImpl) getTopology();
+ TopologyViewImpl oldView = this.oldView;
+
+ Type difference = newView.compareTopology(oldView);
+ if (difference == null) {
+ // then dont send any event then
+ return;
+ }
+
+ oldView.markOld();
+ for (final DiscoveryAware da : discoveryAwares) {
+ da.handleTopologyEvent(new TopologyEvent(difference, oldView,
+ newView));
+ }
+ this.oldView = newView;
+ }
+ }
+
+ /**
+ * Internal class caching some provider infos like service id and ranking.
+ */
+ private final static class ProviderInfo implements Comparable<ProviderInfo> {
+
+ public final PropertyProvider provider;
+ public final Map<String, Object> serviceProps;
+ public final int ranking;
+ public final long serviceId;
+ public final Map<String, String> properties = new HashMap<String, String>();
+
+ public ProviderInfo(final PropertyProvider provider,
+ final Map<String, Object> serviceProps) {
+ this.provider = provider;
+ this.serviceProps = serviceProps;
+ final Object sr = serviceProps.get(Constants.SERVICE_RANKING);
+ if (sr == null || !(sr instanceof Integer)) {
+ this.ranking = 0;
+ } else {
+ this.ranking = (Integer) sr;
+ }
+ this.serviceId = (Long) serviceProps.get(Constants.SERVICE_ID);
+ refreshProperties();
+ }
+
+ public void refreshProperties() {
+ properties.clear();
+ final Object namesObj = serviceProps
+ .get(PropertyProvider.PROPERTY_PROPERTIES);
+ if (namesObj instanceof String) {
+ final String val = provider.getProperty((String) namesObj);
+ if (val != null) {
+ this.properties.put((String) namesObj, val);
+ }
+ } else if (namesObj instanceof String[]) {
+ for (final String name : (String[]) namesObj) {
+ final String val = provider.getProperty(name);
+ if (val != null) {
+ this.properties.put(name, val);
+ }
+ }
+ }
+ }
+
+ /**
+ * @see java.lang.Comparable#compareTo(java.lang.Object)
+ */
+ public int compareTo(final ProviderInfo o) {
+ // Sort by rank in ascending order.
+ if (this.ranking < o.ranking) {
+ return -1; // lower rank
+ } else if (this.ranking > o.ranking) {
+ return 1; // higher rank
+ }
+ // If ranks are equal, then sort by service id in descending order.
+ return (this.serviceId < o.serviceId) ? 1 : -1;
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ if (obj instanceof ProviderInfo) {
+ return ((ProviderInfo) obj).serviceId == this.serviceId;
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return provider.hashCode();
+ }
+ }
+
+ /**
+ * Handle the fact that the topology has likely changed
+ */
+ public void handleTopologyChanged() {
+ logger.debug("handleTopologyChanged: informing the discoveryawares...");
+ handlePotentialTopologyChange();
+ }
+
+}
Propchange: sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/DiscoveryServiceImpl.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/DiscoveryServiceImpl.java
------------------------------------------------------------------------------
svn:keywords = author date id revision rev url
Propchange: sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/DiscoveryServiceImpl.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/InfrastructurePropertyProvider.java
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/InfrastructurePropertyProvider.java?rev=1470424&view=auto
==============================================================================
--- sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/InfrastructurePropertyProvider.java (added)
+++ sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/InfrastructurePropertyProvider.java Mon Apr 22 09:27:35 2013
@@ -0,0 +1,77 @@
+/*
+ * 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.impl;
+
+import java.util.Calendar;
+
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Properties;
+import org.apache.felix.scr.annotations.Property;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.Service;
+import org.apache.sling.discovery.PropertyProvider;
+import org.apache.sling.settings.SlingSettingsService;
+import org.osgi.service.component.ComponentContext;
+
+/**
+ * Example, default PropertyProvider which provides a few interesting static, as
+ * well as a volatile, properties.
+ * <p>
+ * The volatile property is called 'infrastructure.lastPropertiesUpdate' and
+ * allows to conclude when the properties were last read and propagated through
+ * the topology
+ */
+@Component
+@Service(value = { PropertyProvider.class })
+@Properties({ @Property(name = PropertyProvider.PROPERTY_PROPERTIES, value = {
+ "infrastructure.slingId", "infrastructure.slingHome",
+ "infrastructure.lastPropertiesUpdate", "infrastructure.port" }) })
+public class InfrastructurePropertyProvider implements PropertyProvider {
+
+ @Reference
+ private SlingSettingsService settingsService;
+
+ /** the http port on which this instance is listening - provided as infrastructure.port property **/
+ private String port = "";
+
+ /**
+ * Activate this property provider - this reads and stores the http port on which this instance is listening
+ */
+ protected void activate(final ComponentContext cc) {
+ port = cc.getBundleContext().getProperty("org.osgi.service.http.port");
+
+ }
+
+ /**
+ * Serve the properties
+ */
+ public String getProperty(final String name) {
+ if (name.equals("infrastructure.slingId")) {
+ return settingsService.getSlingId();
+ } else if (name.equals("infrastructure.slingHome")) {
+ return settingsService.getSlingHomePath();
+ } else if (name.equals("infrastructure.lastPropertiesUpdate")) {
+ return Calendar.getInstance().getTime().toString();
+ } else if (name.equals("infrastructure.port")) {
+ return port;
+ }
+ return null;
+ }
+
+}
Propchange: sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/InfrastructurePropertyProvider.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/InfrastructurePropertyProvider.java
------------------------------------------------------------------------------
svn:keywords = author date id revision rev url
Propchange: sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/InfrastructurePropertyProvider.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/TopologyWebConsolePlugin.java
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/TopologyWebConsolePlugin.java?rev=1470424&view=auto
==============================================================================
--- sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/TopologyWebConsolePlugin.java (added)
+++ sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/TopologyWebConsolePlugin.java Mon Apr 22 09:27:35 2013
@@ -0,0 +1,722 @@
+/*
+ * 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.impl;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.net.URL;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Collection;
+import java.util.Dictionary;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.StringTokenizer;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.Service;
+import org.apache.felix.webconsole.AbstractWebConsolePlugin;
+import org.apache.felix.webconsole.WebConsoleConstants;
+import org.apache.sling.discovery.ClusterView;
+import org.apache.sling.discovery.DiscoveryAware;
+import org.apache.sling.discovery.DiscoveryService;
+import org.apache.sling.discovery.InstanceDescription;
+import org.apache.sling.discovery.InstanceFilter;
+import org.apache.sling.discovery.TopologyEvent;
+import org.apache.sling.discovery.TopologyView;
+import org.apache.sling.discovery.TopologyEvent.Type;
+import org.apache.sling.discovery.impl.cluster.ClusterViewService;
+import org.apache.sling.discovery.impl.topology.announcement.Announcement;
+import org.apache.sling.discovery.impl.topology.announcement.AnnouncementRegistry;
+import org.apache.sling.discovery.impl.topology.announcement.AnnouncementRegistry.ListScope;
+import org.apache.sling.discovery.impl.topology.connector.ConnectorRegistry;
+import org.apache.sling.discovery.impl.topology.connector.TopologyConnectorClientInformation;
+import org.apache.sling.discovery.impl.topology.connector.TopologyConnectorClientInformation.OriginInfo;
+import org.apache.sling.discovery.impl.topology.connector.TopologyConnectorServlet;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Simple webconsole which gives an overview of the topology visible by the
+ * discovery service
+ */
+@Service(value = { DiscoveryAware.class })
+@Component(immediate = true)
+@SuppressWarnings("serial")
+public class TopologyWebConsolePlugin extends AbstractWebConsolePlugin implements DiscoveryAware {
+
+ private final Logger logger = LoggerFactory.getLogger(this.getClass());
+
+ /** the url part where topology joins are posted to **/
+ private static final String JOIN = "/join";
+
+ /** the url part where topology disconnects are posted to **/
+ private static final String DISCONNECT = "/disconnect/";
+
+ /** the truncated log of topology events, filtered by property change types. shown in webconsole **/
+ private final List<String> propertyChangeLog = new LinkedList<String>();
+
+ /** the truncated log of topology events, shown in webconsole **/
+ private final List<String> topologyLog = new LinkedList<String>();
+
+ /** the date format used in the truncated log of topology events **/
+ private final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+
+ /** the service registry where this webconsole is registered. used for deactivate/unregister **/
+ private ServiceRegistration serviceRegistration;
+
+ @Reference
+ private ClusterViewService clusterViewService;
+
+ @Reference
+ private AnnouncementRegistry announcementRegistry;
+
+ @Reference
+ private ConnectorRegistry connectorRegistry;
+
+ @Reference
+ private DiscoveryService discoveryService;
+
+ @Override
+ public String getLabel() {
+ return "topology";
+ }
+
+ @Override
+ public String getTitle() {
+ return "Topology Management";
+ }
+
+ @Activate
+ @Override
+ public void activate(final BundleContext bundleContext) {
+ super.activate(bundleContext);
+ logger.info("activate: activating...");
+ Dictionary<String, Object> props = new Hashtable<String, Object>();
+ props.put(
+ org.osgi.framework.Constants.SERVICE_DESCRIPTION,
+ "MEEEE Web Console Plugin to display Background servlets and ExecutionEngine status");
+ props.put(org.osgi.framework.Constants.SERVICE_VENDOR,
+ "The Apache Software Foundation");
+ props.put(org.osgi.framework.Constants.SERVICE_PID, getClass()
+ .getName());
+ props.put(WebConsoleConstants.PLUGIN_LABEL, getLabel());
+
+ serviceRegistration = bundleContext.registerService(
+ WebConsoleConstants.SERVICE_NAME, this, props);
+ }
+
+ @Deactivate
+ @Override
+ public void deactivate() {
+ super.deactivate();
+ if (serviceRegistration != null) {
+ serviceRegistration.unregister();
+ serviceRegistration = null;
+ }
+ }
+
+ @Override
+ protected void renderContent(final HttpServletRequest req, final HttpServletResponse res)
+ throws ServletException, IOException {
+ Object rawRoot = req.getAttribute(WebConsoleConstants.ATTR_PLUGIN_ROOT);
+ if (!(rawRoot instanceof String)) {
+ throw new ServletException("Illegal attr: "
+ + WebConsoleConstants.ATTR_PLUGIN_ROOT);
+ }
+
+ String root = rawRoot.toString();
+ String pathInfo = req.getRequestURI().substring(root.length());
+
+ final PrintWriter pw = res.getWriter();
+
+ if (pathInfo.equals("")) {
+ renderOverview(pw, discoveryService.getTopology());
+ } else {
+ StringTokenizer st = new StringTokenizer(pathInfo, "/");
+ final String nodeId = st.nextToken();
+ renderProperties(pw, nodeId);
+ }
+ }
+
+ /**
+ * Render the properties page of a particular instance
+ */
+ private void renderProperties(final PrintWriter pw, final String nodeId) {
+ logger.debug("renderProperties: nodeId=" + nodeId);
+ Set<InstanceDescription> instances = discoveryService.getTopology()
+ .findInstances(new InstanceFilter() {
+
+ public boolean accept(InstanceDescription instance) {
+ String slingId = instance.getSlingId();
+ logger.debug("renderProperties/picks: slingId="
+ + slingId);
+ return (slingId.equals(nodeId));
+ }
+ });
+
+ if (instances != null && instances.size() == 1) {
+ InstanceDescription instance = instances.iterator().next();
+ pw.println("Properties of " + instance.getSlingId() + ":<br/>");
+
+ pw.println("<table class=\"adapters nicetable ui-widget\">");
+ pw.println("<thead>");
+ pw.println("<tr>");
+ pw.println("<th class=\"header ui-widget-header\">Key</th>");
+ pw.println("<th class=\"header ui-widget-header\">Value</th>");
+ pw.println("</tr>");
+ pw.println("</thead>");
+ pw.println("<tbody>");
+ boolean odd = true;
+ for (Iterator<Entry<String, String>> it = instance.getProperties()
+ .entrySet().iterator(); it.hasNext();) {
+ Entry<String, String> entry = it.next();
+ String oddEven = odd ? "odd" : "even";
+ odd = !odd;
+ pw.println("<tr class=\"" + oddEven + " ui-state-default\">");
+
+ pw.println("<td>" + entry.getKey() + "</td>");
+ pw.println("<td>" + entry.getValue() + "</td>");
+
+ pw.println("</tr>");
+ }
+ pw.println("</tbody>");
+ pw.println("</table>");
+ }
+ }
+
+ /**
+ * Render the overview of the entire topology
+ */
+ private void renderOverview(final PrintWriter pw, final TopologyView topology) {
+ pw.println("<p class=\"statline ui-state-highlight\">Configuration</p>");
+ pw.println("<br/>");
+ pw.println("<a href=\"/system/console/configMgr/org.apache.sling.discovery.impl.Config\">Configure Discovery Service</a>");
+ pw.println("<br/>");
+ pw.println("<br/>");
+ pw.println("<p class=\"statline ui-state-highlight\">Topology</p>");
+ pw.println("<div class=\"ui-widget-header ui-corner-top buttonGroup\" style=\"height: 15px;\">");
+ pw.println("<span style=\"float: left; margin-left: 1em;\">Instances in the topology</span>");
+ pw.println("</div>");
+ pw.println("<table class=\"adapters nicetable ui-widget\">");
+ pw.println("<thead>");
+ pw.println("<tr>");
+ pw.println("<th class=\"header ui-widget-header\">Sling id (click for properties)</th>");
+ pw.println("<th class=\"header ui-widget-header\">ClusterView id</th>");
+ pw.println("<th class=\"header ui-widget-header\">Own (this) instance</th>");
+ pw.println("<th class=\"header ui-widget-header\">Leader instance</th>");
+ pw.println("<th class=\"header ui-widget-header\">In local cluster</th>");
+ pw.println("<th class=\"header ui-widget-header\">Announced by</th>");
+ pw.println("</tr>");
+ pw.println("</thead>");
+ pw.println("<tbody>");
+
+ Set<ClusterView> clusters = topology.getClusterViews();
+ ClusterView myCluster = topology.getLocalInstance().getClusterView();
+ boolean odd = true;
+ renderCluster(pw, myCluster, odd);
+
+ for (Iterator<ClusterView> it = clusters.iterator(); it.hasNext();) {
+ ClusterView clusterView = it.next();
+ if (clusterView.equals(myCluster)) {
+ // skip - I already rendered that
+ continue;
+ }
+ odd = !odd;
+ renderCluster(pw, clusterView, odd);
+ }
+
+ pw.println("</tbody>");
+ pw.println("</table>");
+
+ pw.println("<br/>");
+ pw.println("<br/>");
+ pw.println("<p class=\"statline ui-state-highlight\">Connectors</p>");
+ listIncomingTopologyConnectors(pw);
+ listOutgoingTopologyConnectors(pw);
+
+ pw.println("<form action=\"/system/console/topology"
+ + JOIN
+ + "\" method=\"POST\" style=\"display: block; width: auto; min-height: 50px;\" scrolltop=\"0\" scrollleft=\"0\">");
+
+ String url = "http://localhost:4502"
+ + TopologyConnectorServlet.TOPOLOGY_CONNECTOR_PATH;
+
+ pw.println("<br/>");
+ pw.println("<br/>");
+ pw.println("<p class=\"input\">"
+ + "Join topology at: "
+ + "<label>"
+ + "<input class=\"ui-state-default ui-corner-all inputText\" type=\"text\" value=\""
+ + url + "\" name=\"connectorUrl\" style=\"width: 300px\"/>"
+ + "</label>");
+ pw.println("<input class=\"ui-state-default ui-corner-all\" type=\"submit\" value=\"Join\">");
+ pw.println("</form>");
+ pw.println("<p class=\"statline ui-state-highlight\">Topology Change History</p>");
+ pw.println("<pre>");
+ for (Iterator<String> it = topologyLog
+ .iterator(); it.hasNext();) {
+ String aLogEntry = it.next();
+ pw.println(aLogEntry);
+ }
+ pw.println("</pre>");
+ pw.println("<br/>");
+ pw.println("<p class=\"statline ui-state-highlight\">Property Change History</p>");
+ pw.println("<pre>");
+ for (Iterator<String> it = propertyChangeLog
+ .iterator(); it.hasNext();) {
+ String aLogEntry = it.next();
+ pw.println(aLogEntry);
+ }
+ pw.println("</pre>");
+ pw.println("</br>");
+ }
+
+ /**
+ * Render a particular cluster (into table rows)
+ */
+ private void renderCluster(final PrintWriter pw, final ClusterView cluster, final boolean odd) {
+ Collection<Announcement> announcements = null;
+ for (Iterator<InstanceDescription> it = cluster.getInstances()
+ .iterator(); it.hasNext();) {
+ InstanceDescription instanceDescription = it.next();
+ String oddEven = odd ? "odd" : "even";
+ pw.println("<tr class=\"" + oddEven + " ui-state-default\">");
+ boolean isLocal = instanceDescription.isLocal();
+ String slingId = instanceDescription.getSlingId();
+ slingId = "<a href=\"/system/console/topology/" + slingId + "\">"
+ + slingId + "</a>";
+ if (isLocal) {
+ slingId = "<b>" + slingId + "</b>";
+ }
+ pw.println("<td>" + slingId + "</td>");
+ pw.println("<td>"
+ + (instanceDescription.getClusterView() == null ? "null"
+ : instanceDescription.getClusterView().getId())
+ + "</td>");
+ pw.println("<td>" + (isLocal ? "<b>true</b>" : "false") + "</td>");
+ pw.println("<td>"
+ + (instanceDescription.isLeader() ? "<b>true</b>" : "false")
+ + "</td>");
+ if (clusterViewService.contains(instanceDescription.getSlingId())) {
+ pw.println("<td>local</td>");
+ pw.println("<td>n/a</td>");
+ } else {
+ pw.println("<td>remote</td>");
+ if (announcements == null) {
+ announcements = announcementRegistry
+ .listAnnouncements(ListScope.AllInSameCluster);
+ }
+ Announcement parentAnnouncement = null;
+ for (Iterator<Announcement> it2 = announcements.iterator(); it2
+ .hasNext();) {
+ Announcement announcement = it2.next();
+ for (Iterator<InstanceDescription> it3 = announcement
+ .listInstances().iterator(); it3.hasNext();) {
+ InstanceDescription announcedInstance = it3.next();
+ if (announcedInstance.getSlingId().equals(
+ instanceDescription.getSlingId())) {
+ parentAnnouncement = announcement;
+ break;
+ }
+ }
+ }
+ if (parentAnnouncement != null) {
+ pw.println("<td>" + parentAnnouncement.getOwnerId()
+ + "</td>");
+ } else {
+ pw.println("<td><b>error</b></td>");
+ }
+ }
+ pw.println("</tr>");
+ }
+
+ }
+
+ /**
+ * Render the outgoing topology connectors - including the header-div and table
+ */
+ private void listOutgoingTopologyConnectors(final PrintWriter pw) {
+ boolean odd = false;
+ pw.println("<div class=\"ui-widget-header ui-corner-top buttonGroup\" style=\"height: 15px;\">");
+ pw.println("<span style=\"float: left; margin-left: 1em;\">Outgoing topology connectors</span>");
+ pw.println("</div>");
+ pw.println("<table class=\"adapters nicetable ui-widget\">");
+ pw.println("<thead>");
+ pw.println("<tr>");
+ pw.println("<th class=\"header ui-widget-header\">Connector url</th>");
+ pw.println("<th class=\"header ui-widget-header\">Connected to slingId</th>");
+ pw.println("<th class=\"header ui-widget-header\">Origin info</th>");
+ pw.println("<th class=\"header ui-widget-header\">Persistent</th>");
+ // pw.println("<th class=\"header ui-widget-header\">Fallback connector urls</th>");
+ pw.println("<th class=\"header ui-widget-header\">Disconnect</th>");
+ pw.println("</tr>");
+ pw.println("</thead>");
+ pw.println("<tbody>");
+
+ Collection<TopologyConnectorClientInformation> outgoingConnections = connectorRegistry
+ .listOutgoingConnections();
+ for (Iterator<TopologyConnectorClientInformation> it = outgoingConnections
+ .iterator(); it.hasNext();) {
+ TopologyConnectorClientInformation topologyConnectorClient = it
+ .next();
+ String oddEven = odd ? "odd" : "even";
+ odd = !odd;
+ pw.println("<tr class=\"" + oddEven + " ui-state-default\">");
+ pw.println("<td>"
+ + topologyConnectorClient.getConnectorUrl().toString()
+ + "</td>");
+ String remoteSlingId = topologyConnectorClient.getRemoteSlingId();
+ if (topologyConnectorClient.isConnected() && remoteSlingId != null) {
+ pw.println("<td>" + remoteSlingId + "</td>");
+ } else {
+ pw.println("<td><b>not connected</b></td>");
+ }
+ if (topologyConnectorClient.getOriginInfo() == OriginInfo.Config) {
+ pw.println("<td>");
+ pw.println("<a href=\"/system/console/configMgr/org.apache.sling.discovery.impl.Config\">Config</a>");
+ pw.println("</td>");
+ pw.println("<td>persistent</td>");
+ } else {
+ pw.println("<td>" + topologyConnectorClient.getOriginInfo()
+ + "</td>");
+ if (topologyConnectorClient.getOriginInfo() == OriginInfo.WebConsole) {
+ pw.println("<td>not persistent</td>");
+ } else {
+ pw.println("<td>n/a</td>");
+ }
+ }
+ // //TODO fallback urls are not yet implemented!
+ // String fallbackConnectorUrls;
+ // List<String> urls = topologyConnectorClient
+ // .listFallbackConnectorUrls();
+ // if (urls == null || urls.size() == 0) {
+ // fallbackConnectorUrls = "n/a";
+ // } else {
+ // fallbackConnectorUrls = "";
+ // for (Iterator<String> it2 = urls.iterator(); it2.hasNext();) {
+ // String aFallbackConnectorUrl = it2.next();
+ // fallbackConnectorUrls = fallbackConnectorUrls
+ // + aFallbackConnectorUrl + "<br/>";
+ // }
+ // }
+ // pw.println("<td>" + fallbackConnectorUrls + "</td>");
+ pw.println("<td>");
+ final String id = topologyConnectorClient.getId();
+ pw.println("<form id=\"" + id
+ + "\" method=\"post\" action=\"/system/console/topology"
+ + DISCONNECT + id + "\">");
+ pw.println("<input type=\"hidden\" name=\"name\" value=\"value\" />");
+ pw.println(" <a onclick=\"document.getElementById('" + id
+ + "').submit();\">click here to disconnect</a>");
+ pw.println("</form>");
+ pw.println("</td>");
+ pw.println("</tr>");
+ }
+
+ pw.println("</tbody>");
+ pw.println("</table>");
+ }
+
+ /**
+ * Render the incoming topology connectors - including the header-div and table
+ */
+ private void listIncomingTopologyConnectors(final PrintWriter pw) {
+ boolean odd = false;
+ pw.println("<div class=\"ui-widget-header ui-corner-top buttonGroup\" style=\"height: 15px;\">");
+ pw.println("<span style=\"float: left; margin-left: 1em;\">Incoming topology connectors</span>");
+ pw.println("</div>");
+ pw.println("<table class=\"adapters nicetable ui-widget\">");
+ pw.println("<thead>");
+ pw.println("<tr>");
+ pw.println("<th class=\"header ui-widget-header\">Owner slingId</th>");
+ pw.println("<th class=\"header ui-widget-header\">Server info</th>");
+ pw.println("<th class=\"header ui-widget-header\">Origin info</th>");
+ pw.println("<th class=\"header ui-widget-header\">Persistent</th>");
+ pw.println("</tr>");
+ pw.println("</thead>");
+ pw.println("<tbody>");
+
+ Collection<Announcement> outgoingConnections = announcementRegistry
+ .listAnnouncements(ListScope.OnlyInherited);
+ for (Iterator<Announcement> it = outgoingConnections.iterator(); it
+ .hasNext();) {
+ Announcement incomingAnnouncement = it.next();
+ String oddEven = odd ? "odd" : "even";
+ odd = !odd;
+
+ pw.println("<tr class=\"" + oddEven + " ui-state-default\">");
+ pw.println("<td>" + incomingAnnouncement.getOwnerId() + "</td>");
+ if (incomingAnnouncement.getServerInfo() != null) {
+ pw.println("<td>" + incomingAnnouncement.getServerInfo()
+ + "</td>");
+ } else {
+ pw.println("<td><i>n/a</i></td>");
+
+ }
+
+ if (incomingAnnouncement.getOriginInfo() == OriginInfo.Config) {
+ pw.println("<td>Config (remote)</td>");
+ pw.println("<td>persistent</td>");
+ } else {
+ pw.println("<td>" + incomingAnnouncement.getOriginInfo()
+ + " (remote)</td>");
+ if (incomingAnnouncement.getOriginInfo() == OriginInfo.WebConsole) {
+ pw.println("<td>not persistent</td>");
+ } else {
+ pw.println("<td>n/a</td>");
+ }
+ }
+ pw.println("</tr>");
+ }
+
+ pw.println("</tbody>");
+ pw.println("</table>");
+ pw.println("<br/>");
+ pw.println("<br/>");
+ }
+
+ @Override
+ protected void doPost(final HttpServletRequest req, final HttpServletResponse resp)
+ throws ServletException, IOException {
+ Object rawRoot = req.getAttribute(WebConsoleConstants.ATTR_PLUGIN_ROOT);
+ if (!(rawRoot instanceof String)) {
+ throw new ServletException("Illegal attr: "
+ + WebConsoleConstants.ATTR_PLUGIN_ROOT);
+ }
+
+ String root = rawRoot.toString();
+ String pathInfo = req.getRequestURI().substring(root.length());
+ String connectorUrl = req.getParameter("connectorUrl");
+ if (JOIN.equals(pathInfo)) {
+ logger.debug("doPost: " + JOIN + " called with connectorUrl="
+ + connectorUrl);
+ try {
+ connectorRegistry.registerOutgoingConnection(
+ clusterViewService, new URL(connectorUrl),
+ OriginInfo.WebConsole);
+ resp.sendRedirect(root);
+ } catch (Exception e) {
+ logger.error("doPost: 500: " + e);
+ resp.sendRedirect(root);
+ }
+ } else if (pathInfo != null && pathInfo.startsWith(DISCONNECT)) {
+ logger.debug("doPost: " + DISCONNECT + " called with full info: "
+ + pathInfo);
+ String id = pathInfo.substring(DISCONNECT.length());
+ logger.debug("doPost: id=" + id);
+ connectorRegistry.unregisterOutgoingConnection(id);
+ resp.sendRedirect(root);
+ } else {
+ logger.error("doPost: invalid POST to " + pathInfo);
+ resp.sendError(404);
+ }
+ }
+
+ /**
+ * keep a truncated history of the log events for information purpose (to be shown in the webconsole)
+ */
+ public void handleTopologyEvent(final TopologyEvent event) {
+
+ if (event.getType() == Type.PROPERTIES_CHANGED) {
+
+ Set<InstanceDescription> newInstances = event.getNewView()
+ .getInstances();
+ StringBuffer sb = new StringBuffer();
+ for (Iterator<InstanceDescription> it = newInstances.iterator(); it
+ .hasNext();) {
+ final InstanceDescription newInstanceDescription = it.next();
+ InstanceDescription oldInstanceDescription = findInstance(
+ event.getOldView(), newInstanceDescription.getSlingId());
+ if (oldInstanceDescription == null) {
+ logger.error("handleTopologyEvent: got a property changed but did not find instance "
+ + newInstanceDescription
+ + " in oldview.. event="
+ + event);
+ addEventLog(event.getType(), event.getType().toString());
+ return;
+ }
+
+ Map<String, String> oldProps = oldInstanceDescription
+ .getProperties();
+ Map<String, String> newProps = newInstanceDescription
+ .getProperties();
+ StringBuffer diff = diff(oldProps, newProps);
+ if (diff.length() > 0) {
+ if (sb.length() != 0) {
+ sb.append(", ");
+ }
+ sb.append("on instance "
+ + newInstanceDescription.getSlingId() + ": " + diff);
+ }
+ }
+
+ addEventLog(event.getType(), sb.toString());
+ } else if (event.getType() == Type.TOPOLOGY_INIT) {
+ StringBuffer details = new StringBuffer();
+ for (Iterator<InstanceDescription> it = event.getNewView()
+ .getInstances().iterator(); it.hasNext();) {
+ InstanceDescription newInstance = it.next();
+ if (details.length() != 0) {
+ details.append(", ");
+ }
+ details.append(newInstance.getSlingId());
+ }
+ addEventLog(event.getType(),
+ "view: " + shortViewInfo(event.getNewView()) + ". "
+ + details);
+ } else {
+ if (event.getOldView() == null) {
+ addEventLog(event.getType(),
+ "new view: " + shortViewInfo(event.getNewView()));
+ } else {
+ StringBuffer details = new StringBuffer();
+ for (Iterator<InstanceDescription> it = event.getNewView()
+ .getInstances().iterator(); it.hasNext();) {
+ InstanceDescription newInstance = it.next();
+ if (findInstance(event.getOldView(),
+ newInstance.getSlingId()) == null) {
+ if (details.length() != 0) {
+ details.append(", ");
+ }
+ details.append(newInstance.getSlingId() + " joined");
+ }
+ }
+ for (Iterator<InstanceDescription> it = event.getOldView()
+ .getInstances().iterator(); it.hasNext();) {
+ InstanceDescription oldInstance = it.next();
+ if (findInstance(event.getNewView(),
+ oldInstance.getSlingId()) == null) {
+ if (details.length() != 0) {
+ details.append(", ");
+ }
+ details.append(oldInstance.getSlingId() + " left");
+ }
+ }
+
+ addEventLog(
+ event.getType(),
+ "old view: " + shortViewInfo(event.getOldView())
+ + ", new view: "
+ + shortViewInfo(event.getNewView()) + ". "
+ + details);
+ }
+ }
+ }
+
+ /**
+ * find a particular instance in the topology
+ */
+ private InstanceDescription findInstance(final TopologyView view,
+ final String slingId) {
+ Set<InstanceDescription> foundInstances = view
+ .findInstances(new InstanceFilter() {
+
+ public boolean accept(InstanceDescription instance) {
+ return instance.getSlingId().equals(slingId);
+ }
+ });
+ if (foundInstances.size() == 1) {
+ return foundInstances.iterator().next();
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * add a log entry and truncate the log entries if necessary
+ */
+ private synchronized void addEventLog(final Type type, final String info) {
+ final String dateStr = sdf.format(Calendar.getInstance().getTime());
+ final String logEntry = dateStr + ": " + type + ". " + info;
+
+ if (type == Type.PROPERTIES_CHANGED) {
+ propertyChangeLog.add(logEntry);
+ while (propertyChangeLog.size() > 12) {
+ propertyChangeLog.remove(0);
+ }
+ } else {
+ topologyLog.add(logEntry);
+ while (topologyLog.size() > 12) {
+ topologyLog.remove(0);
+ }
+ }
+
+ }
+
+ /**
+ * compile a short information string of the topology, including
+ * number of clusters and instances
+ */
+ private String shortViewInfo(final TopologyView view) {
+ int clusters = view.getClusterViews().size();
+ int instances = view.getInstances().size();
+ return ((clusters == 1) ? "1 cluster" : clusters + " clusters") + ", "
+ + ((instances == 1) ? "1 instance" : instances + " instances");
+ }
+
+ /**
+ * calculate the difference between two sets of properties
+ */
+ private StringBuffer diff(final Map<String, String> oldProps,
+ final Map<String, String> newProps) {
+ final Set<String> oldKeys = new HashSet<String>(oldProps.keySet());
+ final Set<String> newKeys = new HashSet<String>(newProps.keySet());
+
+ StringBuffer sb = new StringBuffer();
+
+ for (Iterator<String> it = oldKeys.iterator(); it.hasNext();) {
+ String oldKey = it.next();
+ if (newKeys.contains(oldKey)) {
+ if (oldProps.get(oldKey).equals(newProps.get(oldKey))) {
+ // perfect
+ } else {
+ sb.append("(" + oldKey + " changed from "
+ + oldProps.get(oldKey) + " to "
+ + newProps.get(oldKey) + ")");
+ }
+ newKeys.remove(oldKey);
+ } else {
+ sb.append("(" + oldKey + " was removed)");
+ }
+ it.remove();
+ }
+ for (Iterator<String> it = newKeys.iterator(); it.hasNext();) {
+ String newKey = it.next();
+ sb.append("(" + newKey + " was added)");
+ }
+
+ return sb;
+ }
+}
Propchange: sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/TopologyWebConsolePlugin.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/TopologyWebConsolePlugin.java
------------------------------------------------------------------------------
svn:keywords = author date id revision rev url
Propchange: sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/TopologyWebConsolePlugin.java
------------------------------------------------------------------------------
svn:mime-type = text/plain