/* * JBoss, Home of Professional Open Source. * Copyright 2009, Red Hat Middleware LLC, and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.system.server.profileservice.repository.clustered.sync; import java.util.Iterator; import org.jboss.system.server.profileservice.repository.clustered.metadata.RepositoryContentMetadata; import org.jboss.system.server.profileservice.repository.clustered.metadata.RepositoryItemMetadata; import org.jboss.system.server.profileservice.repository.clustered.metadata.RepositoryRootMetadata; /** * Abstract base class to support implementations of {@link SynchronizationPolicy}. * <p> * Implements the various RepositorySynchronizationPolicy * <i>acceptXXX</i> methods by checking if a Boolean property has been set * dictating the response; if not delegates the call to one of the abstract * protected methods that subclasses implement. * * @author Brian Stansberry * * @version $Revision: $ * */ public abstract class AbstractSynchronizationPolicy implements SynchronizationPolicy { /** * Default value for {@link #getRemovalTrackingTime()}, equal to 30 days. */ public static final long DEFAULT_REMOVAL_TRACKING_TIME = 30l * 24l * 60l * 60l * 1000l; private long removalTrackingTime = DEFAULT_REMOVAL_TRACKING_TIME; private Boolean allowJoinAdditions; private Boolean allowJoinReincarnations; private Boolean allowJoinUpdates; private Boolean allowJoinRemovals; private Boolean allowMergeAdditions; private Boolean allowMergeReincarnations; private Boolean allowMergeUpdates; private Boolean allowMergeRemovals; private boolean developerMode = false; // ------------------------------------------------------------- Properties /** * Gets any fixed response to * {@link #acceptJoinAddition(RepositoryItemMetadata, RepositoryItemMetadata)}. * * @return a fixed response, or <code>null</code> if there is no fixed * response and the call should be delegated to * {@link #acceptAddition(RepositoryItemMetadata, RepositoryItemMetadata, boolean)} */ public Boolean getAllowJoinAdditions() { return allowJoinAdditions; } /** * Sets any fixed response to * {@link #acceptJoinAddition(RepositoryItemMetadata, RepositoryItemMetadata)}. * * @param allow the fixed response, or <code>null</code> if there is no fixed * response and the call should be delegated to * {@link #acceptAddition(RepositoryItemMetadata, RepositoryItemMetadata, boolean)} */ public void setAllowJoinAdditions(Boolean allow) { this.allowJoinAdditions = allow; } /** * Gets any fixed response to * {@link #acceptJoinReincarnation(RepositoryItemMetadata, RepositoryItemMetadata)}. * * @return a fixed response, or <code>null</code> if there is no fixed * response and the call should be delegated to * {@link #acceptReincarnation(RepositoryItemMetadata, RepositoryItemMetadata, boolean)} */ public Boolean getAllowJoinReincarnations() { return allowJoinReincarnations; } /** * Sets any fixed response to * {@link #acceptJoinReincarnation(RepositoryItemMetadata, RepositoryItemMetadata)}. * * @param allow the fixed response, or <code>null</code> if there is no fixed * response and the call should be delegated to * {@link #acceptReincarnation(RepositoryItemMetadata, RepositoryItemMetadata, boolean)} */ public void setAllowJoinReincarnations(Boolean allow) { this.allowJoinReincarnations = allow; } /** * Gets any fixed response to * {@link #acceptJoinUpdate(RepositoryItemMetadata, RepositoryItemMetadata)}. * * @return a fixed response, or <code>null</code> if there is no fixed * response and the call should be delegated to * {@link #acceptUpdate(RepositoryItemMetadata, RepositoryItemMetadata, boolean)} */ public Boolean getAllowJoinUpdates() { return allowJoinUpdates; } /** * Sets any fixed response to * {@link #acceptJoinUpdate(RepositoryItemMetadata, RepositoryItemMetadata)}. * * @param allow the fixed response, or <code>null</code> if there is no fixed * response and the call should be delegated to * {@link #acceptUpdate(RepositoryItemMetadata, RepositoryItemMetadata, boolean)} */ public void setAllowJoinUpdates(Boolean allow) { this.allowJoinUpdates = allow; } /** * Gets any fixed response to * {@link #acceptJoinRemoval(RepositoryItemMetadata, RepositoryItemMetadata)}. * * @return a fixed response, or <code>null</code> if there is no fixed * response and the call should be delegated to * {@link #acceptRemoval(RepositoryItemMetadata, RepositoryItemMetadata, boolean)} */ public Boolean getAllowJoinRemovals() { return allowJoinRemovals; } /** * Sets any fixed response to * {@link #acceptJoinRemoval(RepositoryItemMetadata, RepositoryItemMetadata)}. * * @param allow the fixed response, or <code>null</code> if there is no fixed * response and the call should be delegated to * {@link #acceptRemoval(RepositoryItemMetadata, RepositoryItemMetadata, boolean)} */ public void setAllowJoinRemovals(Boolean allow) { this.allowJoinRemovals = allow; } /** * Gets any fixed response to * {@link #acceptMergeAddition(RepositoryItemMetadata)}. * * @return a fixed response, or <code>null</code> if there is no fixed * response and the call should be delegated to * {@link #acceptAddition(RepositoryItemMetadata, RepositoryItemMetadata, boolean)} */ public Boolean getAllowMergeAdditions() { return allowMergeAdditions; } /** * Sets any fixed response to * {@link #acceptMergeAddition(RepositoryItemMetadata)}. * * @param allow the fixed response, or <code>null</code> if there is no fixed * response and the call should be delegated to * {@link #acceptAddition(RepositoryItemMetadata, RepositoryItemMetadata, boolean)} */ public void setAllowMergeAdditions(Boolean allow) { this.allowMergeAdditions = allow; } /** * Gets any fixed response to * {@link #acceptMergeReincarnation(RepositoryItemMetadata, RepositoryItemMetadata)}. * * @return a fixed response, or <code>null</code> if there is no fixed * response and the call should be delegated to * {@link #acceptReincarnation(RepositoryItemMetadata, RepositoryItemMetadata, boolean)} */ public Boolean getAllowMergeReincarnations() { return allowMergeReincarnations; } /** * Sets any fixed response to * {@link #acceptMergeReincarnation(RepositoryItemMetadata, RepositoryItemMetadata)}. * * @param allow the fixed response, or <code>null</code> if there is no fixed * response and the call should be delegated to * {@link #acceptReincarnation(RepositoryItemMetadata, RepositoryItemMetadata, boolean)} */ public void setAllowMergeReincarnations(Boolean allow) { this.allowMergeReincarnations = allow; } /** * Gets any fixed response to * {@link #acceptMergeUpdate(RepositoryItemMetadata, RepositoryItemMetadata)}. * * @return a fixed response, or <code>null</code> if there is no fixed * response and the call should be delegated to * {@link #acceptUpdate(RepositoryItemMetadata, RepositoryItemMetadata, boolean)} */ public Boolean getAllowMergeUpdates() { return allowMergeUpdates; } /** * Sets any fixed response to * {@link #acceptMergeUpdate(RepositoryItemMetadata, RepositoryItemMetadata)}. * * @param allow the fixed response, or <code>null</code> if there is no fixed * response and the call should be delegated to * {@link #acceptUpdate(RepositoryItemMetadata, RepositoryItemMetadata, boolean)} */ public void setAllowMergeUpdates(Boolean allow) { this.allowMergeUpdates = allow; } /** * Gets any fixed response to * {@link #acceptMergeRemoval(RepositoryItemMetadata, RepositoryItemMetadata)}. * * @return a fixed response, or <code>null</code> if there is no fixed * response and the call should be delegated to * {@link #acceptRemoval(RepositoryItemMetadata, RepositoryItemMetadata, boolean)} */ public Boolean getAllowMergeRemovals() { return allowMergeRemovals; } /** * Sets any fixed response to * {@link #acceptMergeRemoval(RepositoryItemMetadata, RepositoryItemMetadata)}. * * @param allow the fixed response, or <code>null</code> if there is no fixed * response and the call should be delegated to * {@link #acceptRemoval(RepositoryItemMetadata, RepositoryItemMetadata, boolean)} */ public void setAllowMergeRemovals(Boolean allow) { this.allowMergeRemovals = allow; } /** * Gets whether this policy is in a very lenient "developer mode" in which * case it will return <code>true</code> to all <i>acceptXXX</i> calls. * The purpose of this is to eliminate any need for development servers * to coordinate system timestamps. * * @return <code>true</code> if the policy is in developer mode. */ public boolean isDeveloperMode() { return developerMode; } /** * Sets whether this policy is in a very lenient "developer mode" in which * case it will return <code>true</code> to all <i>acceptXXX</i> calls. * The purpose of this is to eliminate any need for development servers * to coordinate system timestamps. * * @param developerMode <code>true</code> if the policy should be in developer mode. */ public void setDeveloperMode(boolean developerMode) { this.developerMode = developerMode; } /** * Gets how long in ms this policy should remembered removed items for * use in detecting reincarnations. Default is {@link #DEFAULT_REMOVAL_TRACKING_TIME}. * * @return the number of ms, or a number less than 1 to indicate removed * items should not be remembered. */ public long getRemovalTrackingTime() { return removalTrackingTime; } /** * Sets how long in ms this policy should remembered removed items for * use in detecting reincarnations. Default is {@link #DEFAULT_REMOVAL_TRACKING_TIME}. * * @param removalTrackingTime the number of ms, or a number less than 1 to * indicate removed items should not be remembered. */ public void setRemovalTrackingTime(long removalTrackingTime) { this.removalTrackingTime = removalTrackingTime; } // ---------------------------------------- RepositorySynchronizationPolicy public boolean acceptJoinAddition(RepositoryItemMetadata toAdd, RepositoryItemMetadata joinersPrevious) { if (allowJoinAdditions != null) { return allowJoinAdditions.booleanValue(); } validateParams("toAdd", toAdd, true, null, false); return acceptAddition(toAdd, joinersPrevious, false); } public boolean acceptJoinReincarnation(RepositoryItemMetadata reincarnation, RepositoryItemMetadata current) { if (developerMode) { return true; } else if (allowJoinReincarnations != null) { return allowJoinReincarnations.booleanValue(); } validateParams("reincarnation", reincarnation, true, current, true); return acceptReincarnation(reincarnation, current, false); } public boolean acceptJoinRemoval(RepositoryItemMetadata current, RepositoryItemMetadata joinersItem) { if (developerMode) { return true; } else if (allowJoinRemovals != null) { return allowJoinRemovals.booleanValue(); } validateParams("toRemove", joinersItem, false, current, true); return acceptRemoval(current, joinersItem, false); } public boolean acceptJoinUpdate(RepositoryItemMetadata update, RepositoryItemMetadata current) { if (developerMode) { return true; } else if (allowJoinUpdates != null) { return allowJoinUpdates.booleanValue(); } validateParams("update", update, true, current, true); return acceptUpdate(update, current, true); } public boolean acceptMergeAddition(RepositoryItemMetadata toAdd) { if (developerMode) { return true; } else if (allowMergeAdditions != null) { return allowMergeAdditions.booleanValue(); } validateParams("toAdd", toAdd, true, null, false); return acceptAddition(toAdd, null, true); } public boolean acceptMergeReincarnation(RepositoryItemMetadata reincarnation, RepositoryItemMetadata current) { if (developerMode) { return true; } else if (allowMergeReincarnations != null) { return allowMergeReincarnations.booleanValue(); } validateParams("reincarnation", reincarnation, true, current, true); return acceptReincarnation(reincarnation, current, true); } public boolean acceptMergeRemoval(RepositoryItemMetadata current, RepositoryItemMetadata mergersView) { if (developerMode) { return true; } else if (allowMergeRemovals != null) { return allowMergeRemovals.booleanValue(); } validateParams("toRemove", mergersView, false, current, true); return acceptRemoval(current, mergersView, true); } public boolean acceptMergeUpdate(RepositoryItemMetadata update, RepositoryItemMetadata current) { if (developerMode) { return true; } else if (allowMergeUpdates != null) { return allowMergeUpdates.booleanValue(); } validateParams("update", update, true, current, true); return acceptUpdate(update, current, true); } public boolean purgeRemovedItems(RepositoryContentMetadata content) { if (content == null) { return false; } boolean purged = false; long oldest = this.removalTrackingTime < 1 ? 0 : System.currentTimeMillis() - this.removalTrackingTime; for (RepositoryRootMetadata rrmd : content.getRepositories()) { for (Iterator<RepositoryItemMetadata> it = rrmd.getContent().iterator(); it.hasNext(); ) { RepositoryItemMetadata rimd = it.next(); if (rimd.isRemoved() && rimd.getTimestamp() < oldest) { it.remove(); purged = true; } } } return purged; } // ------------------------------------------------------------- Protected protected abstract boolean acceptAddition(RepositoryItemMetadata toAdd, RepositoryItemMetadata joinersPrevious, boolean merge); protected abstract boolean acceptReincarnation(RepositoryItemMetadata reincarnation, RepositoryItemMetadata current, boolean merge); protected abstract boolean acceptRemoval(RepositoryItemMetadata current, RepositoryItemMetadata sendersView, boolean merge); protected abstract boolean acceptUpdate(RepositoryItemMetadata update, RepositoryItemMetadata current, boolean merge); // ---------------------------------------------------------------- Private /** Utility to throw IAE if required params are null */ private static void validateParams(String changeName, RepositoryItemMetadata change, boolean requireChange, RepositoryItemMetadata current, boolean requireCurrent) { if (change == null && requireChange) { throw new IllegalArgumentException("Null " + changeName); } if (requireCurrent && current == null) { throw new IllegalArgumentException("Null current"); } } }