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 [2/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...
Added: sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/cluster/ClusterViewService.java
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/cluster/ClusterViewService.java?rev=1470424&view=auto
==============================================================================
--- sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/cluster/ClusterViewService.java (added)
+++ sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/cluster/ClusterViewService.java Mon Apr 22 09:27:35 2013
@@ -0,0 +1,50 @@
+/*
+ * 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.cluster;
+
+import java.util.Collection;
+
+import org.apache.sling.discovery.ClusterView;
+import org.apache.sling.discovery.InstanceDescription;
+
+/**
+ * The ClusterViewService is responsible and provides access to the view
+ * established in a JcR cluster.
+ */
+public interface ClusterViewService {
+
+ /** the sling id of the local instance **/
+ String getSlingId();
+
+ /** the current cluster view **/
+ ClusterView getClusterView();
+
+ /**
+ * the view id of the cluster view when isolated - ie before any view is
+ * established
+ **/
+ String getIsolatedClusterViewId();
+
+ /** checks whether the cluster view contains a particular sling id **/
+ boolean contains(String slingId);
+
+ /** checks whether the cluster contains any of the provided instances **/
+ boolean containsAny(Collection<InstanceDescription> listInstances);
+
+}
Propchange: sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/cluster/ClusterViewService.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/cluster/ClusterViewService.java
------------------------------------------------------------------------------
svn:keywords = author date id revision rev url
Propchange: sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/cluster/ClusterViewService.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/cluster/ClusterViewServiceImpl.java
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/cluster/ClusterViewServiceImpl.java?rev=1470424&view=auto
==============================================================================
--- sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/cluster/ClusterViewServiceImpl.java (added)
+++ sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/cluster/ClusterViewServiceImpl.java Mon Apr 22 09:27:35 2013
@@ -0,0 +1,176 @@
+/*
+ * 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.cluster;
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.UUID;
+
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Reference;
+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.InstanceDescription;
+import org.apache.sling.discovery.impl.Config;
+import org.apache.sling.discovery.impl.common.View;
+import org.apache.sling.discovery.impl.common.ViewHelper;
+import org.apache.sling.discovery.impl.common.resource.EstablishedClusterView;
+import org.apache.sling.discovery.impl.common.resource.IsolatedInstanceDescription;
+import org.apache.sling.settings.SlingSettingsService;
+import org.osgi.service.component.ComponentContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Default implementation of the ClusterViewService interface.
+ * <p>
+ * This class is a reader only - it accesses the repository to read the
+ * currently established view
+ */
+@Service
+@Component
+public class ClusterViewServiceImpl implements ClusterViewService {
+
+ private final Logger logger = LoggerFactory.getLogger(this.getClass());
+
+ @Reference
+ private SlingSettingsService settingsService;
+
+ @Reference
+ private ResourceResolverFactory resourceResolverFactory;
+
+ @Reference
+ private Config config;
+
+ /** the cluster view representing the isolated mode - ie only my own instance in one cluster. used at bootstrap **/
+ private ClusterView isolatedClusterView;
+
+ /** the cluster view id of the isolatedClusterView **/
+ private String isolatedClusterViewId = UUID.randomUUID().toString();
+
+ private IsolatedInstanceDescription ownInstance;
+
+ public String getIsolatedClusterViewId() {
+ return isolatedClusterViewId;
+ }
+
+ protected void activate(final ComponentContext context) {
+ ResourceResolver resourceResolver = null;
+ try {
+ resourceResolver = resourceResolverFactory
+ .getAdministrativeResourceResolver(null);
+ Resource instanceResource = resourceResolver
+ .getResource(config.getClusterInstancesPath() + "/"
+ + getSlingId());
+ ownInstance = new IsolatedInstanceDescription(instanceResource,
+ isolatedClusterViewId, getSlingId());
+ isolatedClusterView = ownInstance.getClusterView();
+ } catch (LoginException e) {
+ logger.error("Could not do a login: " + e, e);
+ throw new RuntimeException("Could not do a login", e);
+ } finally {
+ if (resourceResolver != null) {
+ resourceResolver.close();
+ }
+ }
+ }
+
+ public String getSlingId() {
+ return settingsService.getSlingId();
+ }
+
+ public boolean contains(final String slingId) {
+ List<InstanceDescription> localInstances = getClusterView()
+ .getInstances();
+ for (Iterator<InstanceDescription> it = localInstances.iterator(); it
+ .hasNext();) {
+ InstanceDescription aLocalInstance = it.next();
+ if (aLocalInstance.getSlingId().equals(slingId)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ public boolean containsAny(Collection<InstanceDescription> listInstances) {
+ for (Iterator<InstanceDescription> it = listInstances.iterator(); it
+ .hasNext();) {
+ InstanceDescription instanceDescription = it.next();
+ if (contains(instanceDescription.getSlingId())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public ClusterView getClusterView() {
+ ResourceResolver resourceResolver = null;
+ try {
+ resourceResolver = resourceResolverFactory
+ .getAdministrativeResourceResolver(null);
+
+ View view = ViewHelper.getEstablishedView(resourceResolver, config);
+ if (view == null) {
+ logger.debug("getEstablishedView: no view established at the moment. isolated mode");
+ Resource instanceResource = resourceResolver
+ .getResource(config.getClusterInstancesPath() + "/"
+ + getSlingId());
+ ownInstance.readProperties(instanceResource);
+ return isolatedClusterView;
+ }
+
+ EstablishedClusterView clusterViewImpl = new EstablishedClusterView(
+ config, view, getSlingId());
+ boolean foundLocal = false;
+ for (Iterator<InstanceDescription> it = clusterViewImpl
+ .getInstances().iterator(); it.hasNext();) {
+ InstanceDescription instance = it.next();
+ if (instance.isLocal()) {
+ foundLocal = true;
+ }
+ }
+ if (foundLocal) {
+ return clusterViewImpl;
+ } else {
+ logger.error("getEstablishedView: the existing established view does not incude the local instance yet! Assming isolated mode.");
+ Resource instanceResource = resourceResolver
+ .getResource(config.getClusterInstancesPath() + "/"
+ + getSlingId());
+ ownInstance.readProperties(instanceResource);
+ return isolatedClusterView;
+ }
+ } catch (LoginException e) {
+ logger.error(
+ "handleEvent: could not log in administratively: " + e, e);
+ return null;
+ } finally {
+ if (resourceResolver != null) {
+ resourceResolver.close();
+ }
+ }
+
+ }
+
+}
Propchange: sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/cluster/ClusterViewServiceImpl.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/cluster/ClusterViewServiceImpl.java
------------------------------------------------------------------------------
svn:keywords = author date id revision rev url
Propchange: sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/cluster/ClusterViewServiceImpl.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/cluster/voting/VotingHandler.java
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/cluster/voting/VotingHandler.java?rev=1470424&view=auto
==============================================================================
--- sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/cluster/voting/VotingHandler.java (added)
+++ sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/cluster/voting/VotingHandler.java Mon Apr 22 09:27:35 2013
@@ -0,0 +1,381 @@
+/*
+ * 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.cluster.voting;
+
+import java.util.Calendar;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+
+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.api.SlingConstants;
+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.api.resource.ValueMap;
+import org.apache.sling.discovery.impl.Config;
+import org.apache.sling.discovery.impl.common.resource.ResourceHelper;
+import org.apache.sling.settings.SlingSettingsService;
+import org.osgi.framework.Constants;
+import org.osgi.service.component.ComponentContext;
+import org.osgi.service.event.Event;
+import org.osgi.service.event.EventConstants;
+import org.osgi.service.event.EventHandler;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The osgi event handler responsible for following any votings and vote
+ * accordingly
+ */
+@Component(immediate = true)
+@Service(value = EventHandler.class)
+@Properties({
+ @Property(name = Constants.SERVICE_DESCRIPTION, value = "New Voting Event Listener."),
+ @Property(name = EventConstants.EVENT_TOPIC, value = {
+ SlingConstants.TOPIC_RESOURCE_ADDED,
+ SlingConstants.TOPIC_RESOURCE_CHANGED,
+ SlingConstants.TOPIC_RESOURCE_REMOVED }) })
+// ,
+// @Property(name = EventConstants.EVENT_FILTER, value = "(path="
+// + org.apache.sling.discovery.viewmgr.Constants.ROOT_PATH + ")") })
+public class VotingHandler implements EventHandler {
+
+ private Logger logger = LoggerFactory.getLogger(this.getClass());
+
+ @Reference
+ private SlingSettingsService slingSettingsService;
+
+ @Reference
+ private ResourceResolverFactory resolverFactory;
+
+ @Reference
+ private Config config;
+
+ /** the sling id of the local instance **/
+ private String slingId;
+
+ protected void activate(final ComponentContext context)
+ throws RepositoryException {
+ slingId = slingSettingsService.getSlingId();
+ logger = LoggerFactory.getLogger(this.getClass().getCanonicalName()
+ + "." + slingId);
+ }
+
+ /**
+ * handle repository changes and react to ongoing votings
+ */
+ public void handleEvent(final Event event) {
+ String resourcePath = (String) event.getProperty("path");
+ String ongoingVotingsPath = config.getOngoingVotingsPath();
+
+ if (resourcePath == null) {
+ // not of my business
+ return;
+ }
+ if (!resourcePath.startsWith(ongoingVotingsPath)) {
+ // not of my business
+ return;
+ }
+
+ ResourceResolver resourceResolver = null;
+ try {
+ resourceResolver = resolverFactory
+ .getAdministrativeResourceResolver(null);
+ } catch (LoginException e) {
+ logger.error(
+ "handleEvent: could not log in administratively: " + e, e);
+ return;
+ }
+ try {
+ analyzeVotings(resourceResolver);
+ } catch (RepositoryException e) {
+ logger.error(
+ "handleEvent: got a RepositoryException during votings analysis: "
+ + e, e);
+ } finally {
+ if (resourceResolver != null) {
+ resourceResolver.close();
+ }
+ }
+ }
+
+ /**
+ * Analyze any ongoing voting in the repository
+ */
+ private void analyzeVotings(final ResourceResolver resourceResolver)
+ throws RepositoryException {
+ VotingView winningVote = VotingHelper.getWinningVoting(
+ resourceResolver, config);
+ if (winningVote != null) {
+ if (winningVote.isInitiatedBy(slingId)) {
+ logger.debug("analyzeVotings: my voting was winning. I'll mark it as established then! "
+ + winningVote);
+ promote(resourceResolver, winningVote.getResource());
+ } else {
+ logger.debug("analyzeVotings: there is a winning vote. No need to vote any further. Expecting it to get promoted to established: "
+ + winningVote);
+ }
+ }
+
+ List<VotingView> ongoingVotings = VotingHelper
+ .listOpenNonWinningVotings(resourceResolver,
+ config);
+
+ Resource clusterNodesRes = resourceResolver
+ .getResource(config.getClusterInstancesPath());
+
+ Iterator<VotingView> it = ongoingVotings.iterator();
+ while (it.hasNext()) {
+ VotingView ongoingVotingRes = it.next();
+ if (winningVote != null && !winningVote.equals(ongoingVotingRes)
+ && !ongoingVotingRes.hasVotedOrIsInitiator(slingId)) {
+ // there is a winning vote, it doesn't equal
+ // ongoingVotingRes, and I have not voted on
+ // ongoingVotingRes yet.
+ // so I vote no there now
+ ongoingVotingRes.vote(slingId, false);
+ it.remove();
+ } else if (!ongoingVotingRes.matchesLiveView(clusterNodesRes,
+ config)) {
+ logger.warn("analyzeVotings: encountered a voting which does not match mine. Voting no: "
+ + ongoingVotingRes);
+ ongoingVotingRes.vote(slingId, false);
+ it.remove();
+ } else if (ongoingVotingRes.isInitiatedBy(slingId)
+ && ongoingVotingRes.hasNoVotes()) {
+ logger.debug("analyzeVotings: there were no votes for my voting, so I have to remove it: "
+ + ongoingVotingRes);
+ ongoingVotingRes.remove();
+ it.remove();
+ }
+ }
+
+ if (winningVote != null) {
+ logger.debug("analyzeVotings: done with vote-handling. there was a winner: "
+ + winningVote);
+ return;
+ }
+
+ if (ongoingVotings.size() == 0) {
+ return;
+ }
+
+ if (ongoingVotings.size() == 1) {
+ VotingView votingResource = ongoingVotings.get(0);
+ if (votingResource.isInitiatedBy(slingId)) {
+ logger.debug("analyzeVotings: only one voting found, and it is mine. I dont have to vote therefore: "
+ + votingResource);
+ return;
+ } // else:
+ logger.debug("analyzeVotings: only one voting found for which I did not yet vote - and it is not mine. I'll vote yes then: "
+ + votingResource);
+ votingResource.vote(slingId, true);
+ }
+
+ // otherwise there is more than one voting going on, all matching my
+ // view of the cluster
+ Collections.sort(ongoingVotings, new Comparator<VotingView>() {
+
+ public int compare(VotingView o1, VotingView o2) {
+ if (o1 == o2) {
+ return 0;
+ }
+ if (o1 == null && o2 != null) {
+ return 1;
+ }
+ if (o2 == null && o1 != null) {
+ return -1;
+ }
+ // now both are non-null
+ return (o1.getViewId().compareTo(o2.getViewId()));
+ }
+ });
+
+ // having sorted, the lowest view should now win!
+ // first lets check if I have voted yes already
+ VotingView myYesVoteResource = VotingHelper.getYesVotingOf(resourceResolver,
+ config, slingId);
+ VotingView lowestVoting = ongoingVotings.get(0);
+
+ if (myYesVoteResource != null && lowestVoting.equals(myYesVoteResource)) {
+ // all fine. then I've voted for the lowest viewId - which is
+ // the whole idea
+ logger.debug("analyzeVotings: my voted for view is currently already the lowest id. which is good. I dont have to change any voting. "
+ + myYesVoteResource);
+ } else if (myYesVoteResource == null) {
+ // I've not voted yet - so I should vote for the lowestVoting
+ logger.debug("analyzeVotings: I apparently have not yet voted. So I shall vote now for the lowest id which is: "
+ + lowestVoting);
+ lowestVoting.vote(slingId, true);
+ } else {
+ // otherwise I've already voted, but not for the lowest. which
+ // is a shame.
+ // I shall change my mind!
+ logger.warn("analyzeVotings: I've already voted - but it so happened that there was a lower voting created after I voted... so I shall change my vote from "
+ + myYesVoteResource + " to " + lowestVoting);
+ myYesVoteResource.vote(slingId, null);
+ lowestVoting.vote(slingId, true);
+ }
+ logger.debug("analyzeVotings: all done now. I've voted yes for "
+ + lowestVoting);
+ }
+
+ /**
+ * Promote a particular voting to be the new established view
+ */
+ private void promote(final ResourceResolver resourceResolver,
+ final Resource winningVoteResource) throws RepositoryException {
+ final Resource previousViewsResource = ResourceHelper
+ .getOrCreateResource(
+ resourceResolver,
+ config.getPreviousViewPath());
+ final Resource establishedViewsResource = ResourceHelper
+ .getOrCreateResource(
+ resourceResolver,
+ config.getEstablishedViewPath());
+ final Resource ongoingVotingsResource = ResourceHelper
+ .getOrCreateResource(
+ resourceResolver,
+ config.getOngoingVotingsPath());
+
+ logger.debug("promote: previousViewsResource="
+ + previousViewsResource.getPath());
+ logger.debug("promote: establishedViewsResource="
+ + establishedViewsResource.getPath());
+ logger.debug("promote: ongoingVotingsResource="
+ + ongoingVotingsResource.getPath());
+ logger.debug("promote: winningVoteResource="
+ + winningVoteResource.getPath());
+
+ final Session session = establishedViewsResource.adaptTo(Node.class)
+ .getSession();
+
+ // step 1: remove any nodes under previousViews
+ final Iterator<Resource> it1 = previousViewsResource.getChildren().iterator();
+ while (it1.hasNext()) {
+ Resource previousView = it1.next();
+ previousView.adaptTo(Node.class).remove();
+ }
+
+ // step 2: retire the existing established view.
+ // Note that there must always only be one. But if there's more, retire
+ // them all now.
+ final Iterator<Resource> it = establishedViewsResource.getChildren()
+ .iterator();
+ boolean first = true;
+ while (it.hasNext()) {
+ Resource retiredView = it.next();
+ if (first) {
+ first = !first;
+ logger.debug("promote: moving the old established view to previous views: "
+ + retiredView.getPath());
+ session.move(
+ retiredView.getPath(),
+ config.getPreviousViewPath()
+ + "/" + retiredView.getName());
+ } else {
+ logger.debug("promote: retiring an erroneously additionally established node "
+ + retiredView.getPath());
+ retiredView.adaptTo(Node.class).remove();
+ }
+ }
+
+ // step 3: move the winning vote resource under the
+ // establishedViewsResource
+
+ // 3a: set the leaderid
+ final Iterator<Resource> it2 = winningVoteResource.getChild("members")
+ .getChildren().iterator();
+ String leaderElectionId = null;
+ String leaderid = null;
+ while (it2.hasNext()) {
+ Resource aMember = it2.next();
+ String leid = aMember.adaptTo(ValueMap.class).get(
+ "leaderElectionId", String.class);
+ if (leaderElectionId == null
+ || (leid != null && leid.compareTo(leaderElectionId) < 0)) {
+ leaderElectionId = leid;
+ leaderid = aMember.getName();
+ }
+ }
+ logger.debug("promote: leader is " + leaderid
+ + " - with leaderElectionId=" + leaderElectionId);
+ winningVoteResource.adaptTo(Node.class).setProperty("leaderId",
+ leaderid);
+ winningVoteResource.adaptTo(Node.class).setProperty("leaderElectionId",
+ leaderElectionId);
+ winningVoteResource.adaptTo(Node.class).setProperty("promotedAt",
+ Calendar.getInstance());
+
+ // 3b: move the result under /established
+ final String newEstablishedViewPath = establishedViewsResource.getPath()
+ + "/" + winningVoteResource.getName();
+ logger.debug("promote: promote to new established node "
+ + newEstablishedViewPath);
+ session.move(winningVoteResource.getPath(), newEstablishedViewPath);
+
+ // step 4: delete all ongoing votings...
+ final Iterable<Resource> ongoingVotingsChildren = ongoingVotingsResource
+ .getChildren();
+ if (ongoingVotingsChildren != null) {
+ Iterator<Resource> it4 = ongoingVotingsChildren.iterator();
+ while (it4.hasNext()) {
+ Resource anOngoingVoting = it4.next();
+ anOngoingVoting.adaptTo(Node.class).remove();
+ }
+ }
+
+ // step 5: make sure there are no duplicate ongoingVotings nodes
+ // created. if so, cleanup
+ final Iterator<Resource> it5 = ongoingVotingsResource.getParent()
+ .getChildren().iterator();
+ while (it5.hasNext()) {
+ Resource resource = it5.next();
+ if (!resource
+ .getPath()
+ .startsWith(
+ config.getOngoingVotingsPath())) {
+ continue;
+ }
+ if (resource
+ .getPath()
+ .equals(config.getOngoingVotingsPath())) {
+ // then it's [0] so to speak .. which we're not cleaning up
+ continue;
+ }
+ logger.warn("promote: cleaning up a duplicate ongoingVotingPath: "
+ + resource.getPath());
+ resource.adaptTo(Node.class).remove();
+ }
+
+ logger.debug("promote: done with promotiong. saving.");
+ session.save();
+ }
+}
\ No newline at end of file
Propchange: sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/cluster/voting/VotingHandler.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/cluster/voting/VotingHandler.java
------------------------------------------------------------------------------
svn:keywords = author date id revision rev url
Propchange: sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/cluster/voting/VotingHandler.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/cluster/voting/VotingHelper.java
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/cluster/voting/VotingHelper.java?rev=1470424&view=auto
==============================================================================
--- sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/cluster/voting/VotingHelper.java (added)
+++ sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/cluster/voting/VotingHelper.java Mon Apr 22 09:27:35 2013
@@ -0,0 +1,154 @@
+/*
+ * 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.cluster.voting;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ResourceResolver;
+import org.apache.sling.discovery.impl.Config;
+import org.apache.sling.discovery.impl.common.resource.ResourceHelper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Helper class for voting
+ */
+public class VotingHelper {
+
+ private final static Logger logger = LoggerFactory
+ .getLogger(VotingHelper.class);
+
+ /**
+ * List all the votings that are currently 'open' but 'not winning'.
+ * <p>
+ * 'Open' means that they have not expired yet, have zero no-votes,
+ * and match the view that this instance has of the cluster.
+ * <p>
+ * 'Not winning' means that a voting still did not receive a vote
+ * from everybody
+ * @return the list of matching votings
+ */
+ public static List<VotingView> listOpenNonWinningVotings(
+ final ResourceResolver resourceResolver, final Config config) {
+ final String ongoingVotingsPath = config.getOngoingVotingsPath();
+ final Resource ongoingVotingsResource = resourceResolver
+ .getResource(ongoingVotingsPath);
+ if (ongoingVotingsResource == null) {
+ logger.warn("listOpenNonWinningVotings: no ongoing votings parent resource found");
+ return new ArrayList<VotingView>();
+ }
+ final Iterable<Resource> children = ongoingVotingsResource.getChildren();
+ final Iterator<Resource> it = children.iterator();
+ final List<VotingView> result = new LinkedList<VotingView>();
+ if (!it.hasNext()) {
+ return result;
+ }
+ while (it.hasNext()) {
+ Resource aChild = it.next();
+ VotingView c = new VotingView(aChild);
+ if (c.matchesLiveView(config)
+ && c.isOngoingVoting(config) && !c.hasNoVotes()
+ && !c.isWinning()) {
+ logger.debug("listOpenNonWinningVotings: found a valid voting: "
+ + aChild
+ + ", properties="
+ + ResourceHelper.getPropertiesForLogging(aChild));
+ result.add(c);
+ } else {
+ logger.debug("listOpenNonWinningVotings: found an invalid voting: "
+ + aChild
+ + ", properties="
+ + ResourceHelper.getPropertiesForLogging(aChild));
+ }
+ }
+ logger.debug("listOpenNonWinningVotings: votings found: "
+ + result.size());
+ return result;
+ }
+
+ /**
+ * Return the still valid (ongoing) and winning (received a yes vote
+ * from everybody) voting
+ * @return the valid and winning voting
+ */
+ public static VotingView getWinningVoting(
+ final ResourceResolver resourceResolver, final Config config) {
+ String ongoingVotingsPath = config.getOngoingVotingsPath();
+ Resource ongoingVotingsResource = resourceResolver
+ .getResource(ongoingVotingsPath);
+ if (ongoingVotingsResource == null) {
+ logger.warn("getWinningVoting: no ongoing votings parent resource found");
+ return null;
+ }
+ Iterable<Resource> children = ongoingVotingsResource.getChildren();
+ Iterator<Resource> it = children.iterator();
+ List<VotingView> result = new LinkedList<VotingView>();
+ while (it.hasNext()) {
+ Resource aChild = it.next();
+ VotingView c = new VotingView(aChild);
+ if (c.isOngoingVoting(config) && c.isWinning()) {
+ logger.debug("getWinningVoting: a winning voting: " + c);
+ result.add(c);
+ }
+ }
+ if (result.size() == 1) {
+ return result.get(0);
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Returns the voting for which the given slingId has vote yes or was the
+ * initiator (which is equal to yes).
+ * @param slingId the instance for which its yes vote should be looked up
+ * @return the voting for which the given slingId has votes yes or was the
+ * initiator
+ */
+ public static VotingView getYesVotingOf(final ResourceResolver resourceResolver,
+ final Config config,
+ final String slingId) {
+ final String ongoingVotingsPath = config.getOngoingVotingsPath();
+ final Resource ongoingVotingsResource = resourceResolver
+ .getResource(ongoingVotingsPath);
+ final Iterable<Resource> children = ongoingVotingsResource.getChildren();
+ final Iterator<Resource> it = children.iterator();
+ final List<VotingView> result = new LinkedList<VotingView>();
+ while (it.hasNext()) {
+ Resource aChild = it.next();
+ VotingView c = new VotingView(aChild);
+ if (c.hasVotedOrIsInitiator(slingId)) {
+ result.add(c);
+ }
+ }
+ if (result.size() >= 1) {
+ // if result.size() is higher than 1, that means that there is more
+ // than 1 yes vote
+ // from myself - which is a bug!
+ return result.get(0);
+ } else {
+ return null;
+ }
+ }
+
+}
Propchange: sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/cluster/voting/VotingHelper.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/cluster/voting/VotingHelper.java
------------------------------------------------------------------------------
svn:keywords = author date id revision rev url
Propchange: sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/cluster/voting/VotingHelper.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/cluster/voting/VotingView.java
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/cluster/voting/VotingView.java?rev=1470424&view=auto
==============================================================================
--- sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/cluster/voting/VotingView.java (added)
+++ sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/cluster/voting/VotingView.java Mon Apr 22 09:27:35 2013
@@ -0,0 +1,284 @@
+/*
+ * 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.cluster.voting;
+
+import java.util.Calendar;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.Set;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.Value;
+
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ResourceResolver;
+import org.apache.sling.api.resource.ValueMap;
+import org.apache.sling.discovery.impl.Config;
+import org.apache.sling.discovery.impl.common.View;
+import org.apache.sling.discovery.impl.common.resource.ResourceHelper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * DAO for an ongoing voting, providing a few helper methods
+ */
+public class VotingView extends View {
+
+ /**
+ * use static logger to avoid frequent initialization as is potentially the
+ * case with ClusterViewResource.
+ **/
+ private final static Logger logger = LoggerFactory
+ .getLogger(VotingView.class);
+
+ /**
+ * Create a new voting with the given list of instances, the given
+ * voting/view id and the given slingid of the initiator.
+ * @param newViewId the new voting/view id
+ * @param initiatorId the slingid of the initiator
+ * @param liveInstances the list of live instances to add to the voting
+ * @return a DAO object representing the voting
+ */
+ public static VotingView newVoting(final ResourceResolver resourceResolver,
+ final Config config,
+ final String newViewId, String initiatorId, final Set<String> liveInstances)
+ throws RepositoryException {
+ final Resource votingResource = ResourceHelper.createResource(
+ resourceResolver, config.getOngoingVotingsPath() + "/"
+ + newViewId);
+ final Node node = votingResource.adaptTo(Node.class);
+ node.setProperty("votingStart", Calendar.getInstance());
+ final Node membersNode = node.addNode("members");
+ final Iterator<String> it = liveInstances.iterator();
+ while (it.hasNext()) {
+ String memberId = it.next();
+ Node newMember = membersNode.addNode(memberId);
+ if (memberId.equals(initiatorId)) {
+ newMember.setProperty("initiator", true);
+ }
+ Resource instanceResource = ResourceHelper.getOrCreateResource(
+ resourceResolver, config.getClusterInstancesPath() + "/"
+ + memberId);
+ String leaderElectionId = instanceResource.adaptTo(ValueMap.class)
+ .get("leaderElectionId", String.class);
+ newMember.setProperty("leaderElectionId", leaderElectionId);
+ }
+ node.getSession().save();
+ return new VotingView(votingResource);
+ }
+
+ /**
+ * Construct a voting view based on the given resource
+ * @param viewResource the resource which is the place the voting is kept
+ */
+ public VotingView(final Resource viewResource) {
+ super(viewResource);
+ }
+
+ @Override
+ public String toString() {
+ final Resource members = getResource().getChild("members");
+ String initiatorId = null;
+ final StringBuffer sb = new StringBuffer();
+ if (members != null) {
+ Iterator<Resource> it = members.getChildren().iterator();
+ while (it.hasNext()) {
+ Resource r = it.next();
+ if (sb.length() != 0) {
+ sb.append(", ");
+ }
+ sb.append(r.getName());
+ ValueMap properties = r.adaptTo(ValueMap.class);
+ if (properties != null) {
+ Boolean initiator = properties.get("initiator",
+ Boolean.class);
+ if (initiator != null && initiator) {
+ initiatorId = r.getName();
+ }
+ }
+ }
+ }
+ return "a VotingView[viewId=" + getViewId() + ", initiator="
+ + initiatorId + ", members=" + sb + "]";
+ }
+
+ /**
+ * Checks whether this voting is still ongoing - that is, whether
+ * a valid votingStart is set and whether that's within the heartbeat timeout configured
+ * @param config
+ * @return
+ */
+ public boolean isOngoingVoting(final Config config) {
+ final ValueMap properties = getResource().adaptTo(ValueMap.class);
+ if (properties == null) {
+ // no properties, odd. then it's not a valid voting.
+ return false;
+ }
+ final Date votingStartDate = properties.get("votingStart", Date.class);
+ if (votingStartDate == null) {
+ logger.debug("isOngoingVoting: got a voting without votingStart. Likely in creation: "
+ + getResource());
+ return false;
+ }
+ final long votingStart = votingStartDate.getTime();
+ final long now = System.currentTimeMillis();
+ final long diff = now - votingStart;
+ return diff < 1000 * config.getHeartbeatTimeout();
+ }
+
+ /**
+ * Checks whether there are any no votes on this voting
+ * @return true if there are any no votes on this voting
+ */
+ public boolean hasNoVotes() {
+ final Iterator<Resource> it = getResource().getChild("members").getChildren()
+ .iterator();
+ while (it.hasNext()) {
+ Resource aMemberRes = it.next();
+ ValueMap properties = aMemberRes.adaptTo(ValueMap.class);
+ Boolean vote = properties.get("vote", Boolean.class);
+ if (vote != null && !vote) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Checks whether the given slingId has voted yes or is the initiator of this voting
+ * @param slingId the sling id to check for
+ * @return true if the given slingId has voted yes or is the initiator of this voting
+ */
+ public boolean hasVotedOrIsInitiator(final String slingId) {
+ final Resource memberResource = getResource().getChild("members").getChild(
+ slingId);
+ if (memberResource == null) {
+ return false;
+ }
+ final ValueMap properties = memberResource.adaptTo(ValueMap.class);
+ if (properties == null) {
+ return false;
+ }
+ final Boolean initiator = properties.get("initiator", Boolean.class);
+ if (initiator != null && initiator) {
+ return true;
+ }
+ final Boolean vote = properties.get("vote", Boolean.class);
+ return vote != null && vote;
+ }
+
+ /**
+ * Checks whether this voting was initiated by the given slingId
+ * @return whether this voting was initiated by the given slingId
+ */
+ public boolean isInitiatedBy(final String slingId) {
+ final Resource memberResource = getResource().getChild("members").getChild(
+ slingId);
+ if (memberResource == null) {
+ return false;
+ }
+ final ValueMap properties = memberResource.adaptTo(ValueMap.class);
+ if (properties == null) {
+ return false;
+ }
+ final Boolean initiator = properties.get("initiator", Boolean.class);
+ return (initiator != null && initiator);
+ }
+
+ /**
+ * add a vote from the given slingId to this voting
+ * @param slingId the slingId which is voting
+ * @param vote true for a yes-vote, false for a no-vote
+ */
+ public void vote(final String slingId, final Boolean vote) {
+ logger.debug("vote: slingId=" + slingId + ", vote=" + vote);
+ final Resource memberResource = getResource().getChild("members").getChild(
+ slingId);
+ if (memberResource == null) {
+ logger.error("vote: no memberResource found for slingId=" + slingId
+ + ", resource=" + getResource());
+ return;
+ }
+ final Node memberNode = memberResource.adaptTo(Node.class);
+ Session session = null;
+ try {
+ session = memberNode.getSession();
+ if (vote == null) {
+ memberNode.setProperty("vote", (Value) null);
+ } else {
+ memberNode.setProperty("vote", vote);
+ }
+ session.save();
+ } catch (RepositoryException re) {
+ logger.error("RepositoryException while voting: " + re, re);
+ if (session != null) {
+ try {
+ session.refresh(false);
+ } catch (RepositoryException e) {
+ logger.error(
+ "RepositoryException after trying to refresh session after another RepositoryException. "
+ + re, re);
+ }
+ }
+ }
+ }
+
+ /**
+ * Checks whether this voting is winning - winning is when it has
+ * votes from each of the members and all are yes votes
+ * @return true if this voting is winning
+ */
+ public boolean isWinning() {
+ final Resource members = getResource().getChild("members");
+ final Iterable<Resource> children = members.getChildren();
+ final Iterator<Resource> it = children.iterator();
+ boolean isWinning = false;
+ while (it.hasNext()) {
+ Resource aMemberRes = it.next();
+ ValueMap properties = aMemberRes.adaptTo(ValueMap.class);
+ Boolean initiator = properties.get("initiator", Boolean.class);
+ Boolean vote = properties.get("vote", Boolean.class);
+ if (initiator != null && initiator) {
+ isWinning = true;
+ continue;
+ }
+ if (vote != null && vote) {
+ isWinning = true;
+ continue;
+ }
+ return false;
+ }
+ return isWinning;
+ }
+
+ /**
+ * Checks if this voting matches the current live view
+ */
+ public boolean matchesLiveView(final Config config) {
+ Resource clusterNodesRes = getResource().getResourceResolver()
+ .getResource(config.getClusterInstancesPath());
+ if (clusterNodesRes == null) {
+ return false;
+ }
+ return matchesLiveView(clusterNodesRes, config);
+ }
+
+}
Propchange: sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/cluster/voting/VotingView.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/cluster/voting/VotingView.java
------------------------------------------------------------------------------
svn:keywords = author date id revision rev url
Propchange: sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/cluster/voting/VotingView.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/common/DefaultClusterViewImpl.java
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/common/DefaultClusterViewImpl.java?rev=1470424&view=auto
==============================================================================
--- sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/common/DefaultClusterViewImpl.java (added)
+++ sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/common/DefaultClusterViewImpl.java Mon Apr 22 09:27:35 2013
@@ -0,0 +1,128 @@
+/*
+ * 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.common;
+
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.apache.sling.discovery.ClusterView;
+import org.apache.sling.discovery.InstanceDescription;
+
+/**
+ * Default implementation of the ClusterView interface.
+ * <p>
+ */
+public class DefaultClusterViewImpl implements ClusterView {
+
+ /** the id of this cluster view **/
+ private final String id;
+
+ /** the list of instances as part of this cluster **/
+ private final List<InstanceDescription> instances = new LinkedList<InstanceDescription>();
+
+ public DefaultClusterViewImpl(final String id) {
+ if (id == null || id.length() == 0) {
+ throw new IllegalArgumentException("id must not be null");
+ }
+ this.id = id;
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ if (obj == null || !(obj instanceof DefaultClusterViewImpl)) {
+ return false;
+ }
+ final DefaultClusterViewImpl other = (DefaultClusterViewImpl) obj;
+ if (!this.id.equals(other.id)) {
+ return false;
+ }
+ if (!this.getLeader().equals(other.getLeader())) {
+ return false;
+ }
+ if (this.instances.size() != other.instances.size()) {
+ return false;
+ }
+ for (Iterator<InstanceDescription> it = instances.iterator(); it
+ .hasNext();) {
+ InstanceDescription instance = it.next();
+ if (!other.instances.contains(instance)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return id.hashCode();
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ /**
+ * Add the given instance to this cluster and set the cluster on the instance (back pointer)
+ * @param instance the instance to add to this cluster
+ */
+ public void addInstanceDescription(final DefaultInstanceDescriptionImpl instance) {
+ if (instances.contains(instance)) {
+ throw new IllegalArgumentException("cannot add same instance twice");
+ }
+ if (instance.isLeader() && doGetLeader() != null) {
+ throw new IllegalArgumentException(
+ "cannot add another leader. there already is one");
+ }
+ instances.add(instance);
+ instance.setClusterView(this);
+ }
+
+ public List<InstanceDescription> getInstances() {
+ if (instances.size() == 0) {
+ throw new IllegalStateException("no instance was ever added");
+ }
+ return Collections.unmodifiableList(instances);
+ }
+
+ public InstanceDescription getLeader() {
+ final InstanceDescription result = doGetLeader();
+ if (result != null) {
+ return result;
+ }
+ throw new IllegalStateException("no leader was added");
+ }
+
+ /**
+ * Lookup the leader of this cluster
+ * @return the leader of this cluster - should never return null
+ */
+ private InstanceDescription doGetLeader() {
+ for (Iterator<InstanceDescription> it = instances.iterator(); it
+ .hasNext();) {
+ InstanceDescription anInstance = it.next();
+ if (anInstance.isLeader()) {
+ return anInstance;
+ }
+ }
+ return null;
+ }
+
+}
Propchange: sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/common/DefaultClusterViewImpl.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/common/DefaultClusterViewImpl.java
------------------------------------------------------------------------------
svn:keywords = author date id revision rev url
Propchange: sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/common/DefaultClusterViewImpl.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/common/DefaultInstanceDescriptionImpl.java
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/common/DefaultInstanceDescriptionImpl.java?rev=1470424&view=auto
==============================================================================
--- sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/common/DefaultInstanceDescriptionImpl.java (added)
+++ sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/common/DefaultInstanceDescriptionImpl.java Mon Apr 22 09:27:35 2013
@@ -0,0 +1,162 @@
+/*
+ * 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.common;
+
+import java.util.Collections;
+import java.util.Map;
+
+import org.apache.sling.discovery.ClusterView;
+import org.apache.sling.discovery.InstanceDescription;
+
+/**
+ * Base implementation for the InstanceDescription interface.
+ * <p>
+ * Allows creation of the object with clusterview and/or properties null - to be
+ * set later but before usage!
+ * <p>
+ */
+public class DefaultInstanceDescriptionImpl implements InstanceDescription {
+
+ /** the cluster view of which this instance is part of **/
+ private ClusterView clusterView;
+
+ /** whether this instance is the leader in the cluster **/
+ private boolean isLeader;
+
+ /** whether this instance is the local/own one **/
+ private boolean isLocal;
+
+ /** the sling id of this instance **/
+ private String slingId;
+
+ /** the properties of this instance **/
+ private Map<String, String> properties;
+
+ public DefaultInstanceDescriptionImpl(final DefaultClusterViewImpl clusterView,
+ final boolean isLeader, final boolean isOwn, final String slingId,
+ final Map<String, String> properties) {
+ // slingId must not be null - clusterView and properties can be though
+ if (slingId == null || slingId.length() == 0) {
+ throw new IllegalArgumentException("slingId must not be null");
+ }
+ this.isLeader = isLeader;
+ this.isLocal = isOwn;
+ this.slingId = slingId;
+ this.properties = properties;
+ if (clusterView != null) {
+ clusterView.addInstanceDescription(this);
+ if (this.clusterView == null) {
+ throw new IllegalStateException(
+ "clusterView should have been set by now");
+ }
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "an InstanceDescription[slindId=" + slingId + ", isLeader="
+ + isLeader + ", isOwn=" + isLocal + "]";
+ }
+
+ @Override
+ public int hashCode() {
+ return slingId.hashCode();
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ if (obj == null || !(obj instanceof DefaultInstanceDescriptionImpl)) {
+ return false;
+ }
+ final DefaultInstanceDescriptionImpl other = (DefaultInstanceDescriptionImpl) obj;
+ if (!this.slingId.equals(other.slingId)) {
+ return false;
+ }
+ if (!this.slingId.equals(other.slingId)) {
+ return false;
+ }
+ if (!properties.equals(other.properties)) {
+ return false;
+ }
+ if (!this.getClusterView().getId()
+ .equals(other.getClusterView().getId())) {
+ return false;
+ }
+ return true;
+ }
+
+ public ClusterView getClusterView() {
+ if (clusterView == null) {
+ throw new IllegalStateException("clusterView was never set");
+ }
+ return clusterView;
+ }
+
+ /**
+ * Sets the cluster on this instance
+ * @param clusterView
+ */
+ protected void setClusterView(ClusterView clusterView) {
+ if (this.clusterView != null) {
+ throw new IllegalStateException("can only set clusterView once");
+ }
+ if (clusterView == null) {
+ throw new IllegalArgumentException("clusterView must not be null");
+ }
+ this.clusterView = clusterView;
+ }
+
+ public boolean isLeader() {
+ return isLeader;
+ }
+
+ public boolean isLocal() {
+ return isLocal;
+ }
+
+ public String getSlingId() {
+ return slingId;
+ }
+
+ public String getProperty(final String name) {
+ if (properties == null) {
+ throw new IllegalStateException("properties were never set");
+ }
+ return properties.get(name);
+ }
+
+ public Map<String, String> getProperties() {
+ if (properties == null) {
+ throw new IllegalStateException("properties were never set");
+ }
+ return Collections.unmodifiableMap(properties);
+ }
+
+ /**
+ * Sets the properties of this instance
+ * @param properties
+ */
+ protected void setProperties(final Map<String, String> properties) {
+ if (properties == null) {
+ throw new IllegalArgumentException("properties must not be null");
+ }
+ this.properties = properties;
+ }
+
+}
Propchange: sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/common/DefaultInstanceDescriptionImpl.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/common/DefaultInstanceDescriptionImpl.java
------------------------------------------------------------------------------
svn:keywords = author date id revision rev url
Propchange: sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/common/DefaultInstanceDescriptionImpl.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/common/View.java
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/common/View.java?rev=1470424&view=auto
==============================================================================
--- sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/common/View.java (added)
+++ sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/common/View.java Mon Apr 22 09:27:35 2013
@@ -0,0 +1,141 @@
+/*
+ * 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.common;
+
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.discovery.impl.Config;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * DAO for a view stored in the repository.
+ */
+public class View {
+
+ /**
+ * use static logger to avoid frequent initialization as is potentially the
+ * case with ClusterViewResource.
+ **/
+ private final static Logger logger = LoggerFactory.getLogger(View.class);
+
+ /** the underlying resource which represents this view **/
+ private final Resource resource;
+
+ public View(final Resource resource) {
+ this.resource = resource;
+ }
+
+ /**
+ * Returns the underlying resource of this view.
+ * @return the underlying resource of this view
+ */
+ public Resource getResource() {
+ return resource;
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ if (obj == null) {
+ return false;
+ } else if (!(obj instanceof View)) {
+ return false;
+ }
+ View other = (View) obj;
+ return getResource().getPath().equals(other.getResource().getPath());
+ }
+
+ @Override
+ public int hashCode() {
+ return getResource().getPath().hashCode();
+ }
+
+ /**
+ * Returns the id of this view.
+ * @return the id of this view
+ */
+ public String getViewId() {
+ return getResource().getName();
+ }
+
+ /**
+ * Checks whether this view matches the 'live view' as represented in the clusterInstances resource
+ * @param clusterInstancesRes the clusterInstances resource against which to check
+ * @return
+ */
+ public boolean matchesLiveView(final Resource clusterInstancesRes, final Config config) {
+ return matches(ViewHelper.determineLiveInstances(clusterInstancesRes,
+ config));
+ }
+
+ /**
+ * Compare this view with the given set of slingIds
+ * @param view a set of slingIds against which to compare this view
+ * @return true if this view matches the given set of slingIds
+ */
+ public boolean matches(final Set<String> view) {
+ final Set<String> viewCopy = new HashSet<String>(view);
+ final Resource members = resource.getChild("members");
+ if (members == null) {
+ return false;
+ }
+ final Iterator<Resource> it = members.getChildren().iterator();
+ while (it.hasNext()) {
+ Resource aMemberRes = it.next();
+
+ if (!viewCopy.remove(aMemberRes.getName())) {
+ return false;
+ }
+ }
+ // now the ViewCopy set must be empty to represent a match
+ return (viewCopy.size() == 0);
+ }
+
+ /**
+ * Delete this view from the repository
+ */
+ public void remove() {
+ final Node myNode = getResource().adaptTo(Node.class);
+ Session session = null;
+ try {
+ session = myNode.getSession();
+ myNode.remove();
+ session.save();
+ session = null;
+ } catch (RepositoryException e) {
+ logger.error("remove: Could not remove node: " + e, e);
+ } finally {
+ if (session != null) {
+ try {
+ session.refresh(false);
+ } catch (RepositoryException e1) {
+ logger.error("remove: Could not refresh session: " + e1, e1);
+ }
+ }
+ }
+ }
+
+}
Propchange: sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/common/View.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/common/View.java
------------------------------------------------------------------------------
svn:keywords = author date id revision rev url
Propchange: sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/common/View.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/common/ViewHelper.java
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/common/ViewHelper.java?rev=1470424&view=auto
==============================================================================
--- sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/common/ViewHelper.java (added)
+++ sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/common/ViewHelper.java Mon Apr 22 09:27:35 2013
@@ -0,0 +1,133 @@
+/*
+ * 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.common;
+
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+import javax.jcr.RepositoryException;
+
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ResourceResolver;
+import org.apache.sling.api.resource.ValueMap;
+import org.apache.sling.discovery.impl.Config;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/** helper for views **/
+public class ViewHelper {
+
+ private static final Logger logger = LoggerFactory
+ .getLogger(ViewHelper.class);
+
+ /**
+ * Return the list of cluster instances that are 'live', ie that have
+ * sent a heartbeat within the configured heartbeat timeout
+ * @param clusterInstancesResource
+ * @param config
+ * @return
+ */
+ public static Set<String> determineLiveInstances(
+ final Resource clusterInstancesResource, final Config config) {
+ final Set<String> myView = new HashSet<String>();
+ final Iterator<Resource> it = clusterInstancesResource.getChildren()
+ .iterator();
+ while (it.hasNext()) {
+ Resource aClusterInstance = it.next();
+ if (isHeartBeatCurrent(aClusterInstance, config)) {
+ myView.add(aClusterInstance.getName());
+ }
+ }
+ return myView;
+ }
+
+ /**
+ * Chck if the given resource has a heartbeat sent within the
+ * configured heartbeat timeout
+ * @param aClusterInstanceResource
+ * @param config
+ * @return
+ */
+ private static boolean isHeartBeatCurrent(
+ Resource aClusterInstanceResource, final Config config) {
+ final ValueMap properties = aClusterInstanceResource.adaptTo(ValueMap.class);
+ final Date lastHeartbeat = properties.get("lastHeartbeat", Date.class);
+ final long now = System.currentTimeMillis();
+ if (lastHeartbeat == null) {
+ return false;
+ }
+ final long then = lastHeartbeat.getTime();
+ final long diff = now - then;
+ return (diff < 1000 * config.getHeartbeatTimeout());
+ }
+
+ /**
+ * Return the currently established cluster view - or null if there is no
+ * cluster view established at the moment.
+ *
+ * @param resourceResolver
+ * @return
+ */
+ public static View getEstablishedView(final ResourceResolver resourceResolver, final Config config) {
+ final Resource establishedParent = resourceResolver
+ .getResource(config.getEstablishedViewPath());
+ if (establishedParent == null) {
+ return null;
+ }
+ final Iterable<Resource> children = establishedParent.getChildren();
+ if (children == null) {
+ return null;
+ }
+ final Iterator<Resource> it = children.iterator();
+ if (!it.hasNext()) {
+ return null;
+ }
+ Resource establishedView = it.next();
+ if (!it.hasNext()) {
+ return new View(establishedView);
+ }
+ // emergency cleanup in case there is more than one established view:
+ while (true) {
+ logger.error("getEstablishedView: more than one established view encountered! Removing: "
+ + establishedView);
+ new View(establishedView).remove();
+ if (!it.hasNext()) {
+ return null;
+ }
+ establishedView = it.next();
+ }
+ }
+
+ /**
+ * Check if the established view matches the given set of slingIds
+ */
+ public static boolean establishedViewMatches(
+ final ResourceResolver resourceResolver, final Config config, final Set<String> view)
+ throws RepositoryException {
+ final View establishedView = ViewHelper.getEstablishedView(resourceResolver, config);
+ if (establishedView == null) {
+ return false;
+ } else {
+ return (establishedView.matches(view));
+ }
+ }
+
+}
Propchange: sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/common/ViewHelper.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/common/ViewHelper.java
------------------------------------------------------------------------------
svn:keywords = author date id revision rev url
Propchange: sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/common/ViewHelper.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/common/heartbeat/HeartbeatHandler.java
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/common/heartbeat/HeartbeatHandler.java?rev=1470424&view=auto
==============================================================================
--- sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/common/heartbeat/HeartbeatHandler.java (added)
+++ sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/common/heartbeat/HeartbeatHandler.java Mon Apr 22 09:27:35 2013
@@ -0,0 +1,325 @@
+/*
+ * 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.common.heartbeat;
+
+import java.util.Calendar;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.UUID;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+
+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.sling.api.resource.LoginException;
+import org.apache.sling.api.resource.PersistenceException;
+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.commons.scheduler.Scheduler;
+import org.apache.sling.discovery.impl.Config;
+import org.apache.sling.discovery.impl.DiscoveryServiceImpl;
+import org.apache.sling.discovery.impl.cluster.voting.VotingHelper;
+import org.apache.sling.discovery.impl.cluster.voting.VotingView;
+import org.apache.sling.discovery.impl.common.ViewHelper;
+import org.apache.sling.discovery.impl.common.resource.ResourceHelper;
+import org.apache.sling.discovery.impl.topology.announcement.AnnouncementRegistry;
+import org.apache.sling.discovery.impl.topology.connector.ConnectorRegistry;
+import org.apache.sling.settings.SlingSettingsService;
+import org.osgi.service.component.ComponentContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The heartbeat handler is responsible and capable of issuing both local and
+ * remote heartbeats and registers a periodic job with the scheduler for doing so.
+ * <p>
+ * Local heartbeats are stored in the repository. Remote heartbeats are POSTs to
+ * remote TopologyConnectorServlets.
+ */
+@Service(value = { HeartbeatHandler.class })
+@Component
+public class HeartbeatHandler implements Runnable {
+
+ private final Logger logger = LoggerFactory.getLogger(this.getClass());
+
+ /** the name used for the period job with the scheduler **/
+ private static final String NAME = "discovery.impl.heartbeat.runner";
+
+ @Reference
+ private SlingSettingsService slingSettingsService;
+
+ @Reference
+ private ResourceResolverFactory resourceResolverFactory;
+
+ @Reference
+ private ConnectorRegistry connectorRegistry;
+
+ @Reference
+ private AnnouncementRegistry announcementRegistry;
+
+ @Reference
+ private Scheduler scheduler;
+
+ @Reference
+ private Config config;
+
+ /** the discovery service reference is used to get properties updated before heartbeats are sent **/
+ private DiscoveryServiceImpl discoveryService;
+
+ /** the sling id of the local instance **/
+ private String slingId;
+
+ /** the id which is to be used for the next voting **/
+ private String nextVotingId = UUID.randomUUID().toString();
+
+ /** whether or not to reset the leaderElectionId at next heartbeat time **/
+ private boolean resetLeaderElectionId = false;
+
+ @Activate
+ protected void activate(ComponentContext context)
+ throws RepositoryException {
+ slingId = slingSettingsService.getSlingId();
+ // on activate the resetLeaderElectionId is set to true to ensure that
+ // the 'leaderElectionId' property is reset on next heartbeat issuance.
+ // the idea being that a node which leaves the cluster should not
+ // become leader on next join - and by resetting the leaderElectionId
+ // to the current time, this is ensured.
+ resetLeaderElectionId = true;
+ }
+
+ @Deactivate
+ protected void deactivate() {
+ scheduler.removeJob(NAME);
+ }
+
+ /**
+ * The initialize method is called by the DiscoveryServiceImpl.activate
+ * as we require the discoveryService (and the discoveryService has
+ * a reference on us - but we cant have circular references in osgi).
+ * <p>
+ * The initialVotingId is used to avoid an unnecessary topologyChanged event
+ * when switching form isolated to established view but with only the local
+ * instance in the view.
+ */
+ public void initialize(final DiscoveryServiceImpl discoveryService,
+ final String initialVotingId) {
+ this.discoveryService = discoveryService;
+ this.nextVotingId = initialVotingId;
+ issueHeartbeat();
+
+ try {
+ scheduler.addPeriodicJob(NAME, this,
+ null, config.getHeartbeatInterval(), false);
+ } catch (Exception e) {
+ logger.error("activate: Could not start heartbeat runner: " + e, e);
+ }
+ }
+
+ public void run() {
+ // issue a heartbeat
+ issueHeartbeat();
+
+ // check the view
+ checkView();
+ }
+
+ /** Get or create a ResourceResolver **/
+ private ResourceResolver getResourceResolver() throws LoginException {
+ if (resourceResolverFactory == null) {
+ logger.error("getResourceResolver: resourceResolverFactory is null!");
+ return null;
+ }
+ return resourceResolverFactory.getAdministrativeResourceResolver(null);
+ }
+
+ /** Calcualte the local cluster instance path **/
+ private String getLocalClusterNodePath() {
+ return config.getClusterInstancesPath() + "/" + slingId;
+ }
+
+ /**
+ * Issue a heartbeat.
+ * <p>
+ * This action consists of first updating the local properties,
+ * then issuing a cluster-local heartbeat (within the repository)
+ * and then a remote heartbeat (to all the topology connectors
+ * which announce this part of the topology to others)
+ */
+ private void issueHeartbeat() {
+ if (discoveryService == null) {
+ logger.error("issueHeartbeat: discoveryService is null");
+ } else {
+ discoveryService.updateProperties();
+ }
+ issueClusterLocalHeartbeat();
+ issueRemoteHeartbeats();
+ }
+
+ /** Issue a remote heartbeat using the topology connectors **/
+ private void issueRemoteHeartbeats() {
+ if (connectorRegistry == null) {
+ logger.error("issueRemoteHeartbeats: connectorRegistry is null");
+ return;
+ }
+ connectorRegistry.pingOutgoingConnections();
+ }
+
+ /** Issue a cluster local heartbeat (into the repository) **/
+ private void issueClusterLocalHeartbeat() {
+ ResourceResolver resourceResolver = null;
+ final String myClusterNodePath = getLocalClusterNodePath();
+ try {
+ resourceResolver = getResourceResolver();
+ if (resourceResolver == null) {
+ logger.error("issueClusterLocalHeartbeat: no resourceresolver available!");
+ return;
+ }
+
+ final Resource resource = ResourceHelper.getOrCreateResource(
+ resourceResolver, myClusterNodePath);
+ final Node node = resource.adaptTo(Node.class);
+ node.setProperty("lastHeartbeat", Calendar.getInstance());
+ if (resetLeaderElectionId || !node.hasProperty("leaderElectionId")) {
+ int maxLongLength = String.valueOf(Long.MAX_VALUE).length();
+ String currentTimeMillisStr = String.format("%0"
+ + maxLongLength + "d", System.currentTimeMillis());
+
+ String prefix = "0";
+
+ String leaderElectionRepositoryDescriptor = config.getLeaderElectionRepositoryDescriptor();
+ if (leaderElectionRepositoryDescriptor!=null && leaderElectionRepositoryDescriptor.length()!=0) {
+ // when this property is configured, check the value of the repository descriptor
+ // and if that value is set, include it in the leader election id
+
+ final Session session = resourceResolver.adaptTo(Session.class);
+ if ( session != null ) {
+ String value = session.getRepository()
+ .getDescriptor(leaderElectionRepositoryDescriptor);
+ if (value != null && value.equalsIgnoreCase("true")) {
+ prefix = "1";
+ }
+ }
+ }
+ node.setProperty("leaderElectionId", prefix + "_"
+ + currentTimeMillisStr + "_" + slingId);
+ resetLeaderElectionId = false;
+ }
+ resourceResolver.commit();
+
+ } catch (LoginException e) {
+ logger.error("issueHeartbeat: could not log in administratively: "
+ + e, e);
+ } catch (PersistenceException e) {
+ logger.error("issueHeartbeat: Got a PersistenceException: "
+ + myClusterNodePath + " " + e, e);
+ } catch (RepositoryException e) {
+ logger.error("issueHeartbeat: Got a RepositoryExceptionnnn: "
+ + myClusterNodePath + " " + e, e);
+ } finally {
+ if (resourceResolver != null) {
+ resourceResolver.close();
+ }
+ }
+ }
+
+ /** Check whether the established view matches the reality, ie matches the
+ * heartbeats
+ */
+ private void checkView() {
+ // check the remotes first
+ if (announcementRegistry == null) {
+ logger.error("announcementRegistry is null");
+ return;
+ }
+ announcementRegistry.checkExpiredAnnouncements();
+
+ ResourceResolver resourceResolver = null;
+ try {
+ resourceResolver = getResourceResolver();
+ doCheckView(resourceResolver);
+ } catch (LoginException e) {
+ logger.error("checkView: could not log in administratively: " + e,
+ e);
+ } catch (RepositoryException e) {
+ logger.error(
+ "checkView: encountered a repository exception during view check: "
+ + e, e);
+ } finally {
+ if (resourceResolver != null) {
+ resourceResolver.close();
+ }
+ }
+ }
+
+ /** do the established-against-heartbeat view check using the given resourceResolver. **/
+ private void doCheckView(final ResourceResolver resourceResolver)
+ throws RepositoryException {
+
+ final VotingView winningVoting = VotingHelper.getWinningVoting(
+ resourceResolver, config);
+ int numOpenNonWinningVotes = VotingHelper.listOpenNonWinningVotings(
+ resourceResolver, config).size();
+ if (winningVoting != null || (numOpenNonWinningVotes > 0)) {
+ // then there are votings pending and I shall wait for them to
+ // settle
+ logger.debug("doCheckView: "
+ + numOpenNonWinningVotes
+ + " ongoing votings, no one winning yet - I shall wait for them to settle.");
+ return;
+ }
+
+ final Resource clusterNodesRes = ResourceHelper.getOrCreateResource(
+ resourceResolver, config.getClusterInstancesPath());
+ final Set<String> liveInstances = ViewHelper.determineLiveInstances(
+ clusterNodesRes, config);
+
+ if (ViewHelper.establishedViewMatches(resourceResolver, config, liveInstances)) {
+ // that's the normal case. the established view matches what we're
+ // seeing.
+ // all happy and fine
+ logger.debug("doCheckView: no pending nor winning votes. view is fine. we're all happy.");
+ return;
+ }
+ logger.debug("doCheckView: no pending nor winning votes. But: view does not match established or no established yet. Initiating a new voting");
+ Iterator<String> it = liveInstances.iterator();
+ while (it.hasNext()) {
+ logger.debug("doCheckView: one of the live instances is: "
+ + it.next());
+ }
+
+ // we seem to be the first to realize that the currently established
+ // view doesnt match
+ // the currently live instances.
+
+ // initiate a new voting
+ String votingId;
+ synchronized (this) {
+ votingId = nextVotingId;
+ nextVotingId = UUID.randomUUID().toString();
+ }
+ VotingView.newVoting(resourceResolver, config, votingId, slingId, liveInstances);
+ }
+
+}
Propchange: sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/common/heartbeat/HeartbeatHandler.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/common/heartbeat/HeartbeatHandler.java
------------------------------------------------------------------------------
svn:keywords = author date id revision rev url
Propchange: sling/trunk/contrib/extensions/discovery/impl/src/main/java/org/apache/sling/discovery/impl/common/heartbeat/HeartbeatHandler.java
------------------------------------------------------------------------------
svn:mime-type = text/plain