/* * 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.commons.providers.util.ResourceHelper; import org.apache.sling.discovery.impl.Config; 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 - never returns null */ public static List<VotingView> listOpenNonWinningVotings( final ResourceResolver resourceResolver, final Config config) { if (config==null) { logger.info("listOpenNonWinningVotings: config is null, bundle likely deactivated."); return new ArrayList<VotingView>(); } final String ongoingVotingsPath = config.getOngoingVotingsPath(); final Resource ongoingVotingsResource = resourceResolver .getResource(ongoingVotingsPath); if (ongoingVotingsResource == null) { // it is legal that at this stage there is no ongoingvotings node yet // for example when there was never a voting yet logger.debug("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); String matchesLiveView; try { matchesLiveView = c.matchesLiveView(config); } catch (Exception e) { logger.error("listOpenNonWinningVotings: could not compare voting with live view: "+e, e); continue; } boolean ongoingVoting = c.isOngoingVoting(config); boolean hasNoVotes = c.hasNoVotes(); boolean isWinning = c.isWinning(); if (matchesLiveView == null && ongoingVoting && !hasNoVotes && !isWinning) { if (logger.isDebugEnabled()) { logger.debug("listOpenNonWinningVotings: found an open voting: " + aChild + ", properties=" + ResourceHelper.getPropertiesForLogging(aChild)); } result.add(c); } else { if (logger.isDebugEnabled()) { logger.debug("listOpenNonWinningVotings: a non-open voting: " + aChild + ", matches live: " + matchesLiveView + ", is ongoing: " + ongoingVoting + ", has no votes: " + hasNoVotes + ", is winning: " + isWinning + ", properties=" + ResourceHelper.getPropertiesForLogging(aChild)); } } } if (logger.isDebugEnabled()) { logger.debug("listOpenNonWinningVotings: votings found: " + result.size()); } return result; } /** * List all the votings that have timed out * @return the list of matching votings */ public static List<VotingView> listTimedoutVotings( final ResourceResolver resourceResolver, final Config config) { final String ongoingVotingsPath = config.getOngoingVotingsPath(); final Resource ongoingVotingsResource = resourceResolver .getResource(ongoingVotingsPath); if (ongoingVotingsResource == null) { logger.info("listTimedoutVotings: no ongoing votings parent resource found"); // TOOD - is this expected? 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.isTimedoutVoting(config)) { if (logger.isDebugEnabled()) { logger.debug("listTimedoutVotings: found a timed-out voting: " + aChild + ", properties=" + ResourceHelper.getPropertiesForLogging(aChild)); } result.add(c); } } if (logger.isDebugEnabled()) { logger.debug("listTimedoutVotings: 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) { // it is legal that at this stage there is no ongoingvotings node yet // for example when there was never a voting yet logger.debug("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); boolean ongoing = c.isOngoingVoting(config); boolean winning = c.isWinning(); if (ongoing && winning) { if (logger.isDebugEnabled()) { logger.debug("getWinningVoting: a winning voting: " + aChild); } result.add(c); } else { logger.debug("getWinningVote: not winning: vote="+aChild+" is ongoing="+ongoing+", winning="+winning); } } 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 List<VotingView> getYesVotingsOf(final ResourceResolver resourceResolver, final Config config, final String slingId) { if (resourceResolver == null) { throw new IllegalArgumentException("resourceResolver must not be null"); } if (config == null) { throw new IllegalArgumentException("config must not be null"); } if (slingId == null || slingId.length() == 0) { throw new IllegalArgumentException("slingId must not be null or empty"); } final String ongoingVotingsPath = config.getOngoingVotingsPath(); final Resource ongoingVotingsResource = resourceResolver .getResource(ongoingVotingsPath); if (ongoingVotingsResource == null) { return null; } final Iterable<Resource> children = ongoingVotingsResource.getChildren(); if (children == null) { return null; } 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.hasVotedYes(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 // which can happen if the local instance is initiator of one // and has voted on another voting return result; } else { return null; } } public static List<VotingView> listVotings(ResourceResolver resourceResolver, Config config) { if (config==null) { logger.info("listVotings: config is null, bundle likely deactivated."); return new ArrayList<VotingView>(); } final String ongoingVotingsPath = config.getOngoingVotingsPath(); final Resource ongoingVotingsResource = resourceResolver .getResource(ongoingVotingsPath); if (ongoingVotingsResource == null) { // it is legal that at this stage there is no ongoingvotings node yet // for example when there was never a voting yet logger.debug("listVotings: 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>(); while (it.hasNext()) { Resource aChild = it.next(); VotingView c = new VotingView(aChild); result.add(c); } if (logger.isDebugEnabled()) { logger.debug("listVotings: votings found: " + result.size()); } return result; } }