[jboss-cvs] JBossAS SVN: r86553 - in trunk: cluster/src/etc and 14 other directories.
jboss-cvs-commits at lists.jboss.org
jboss-cvs-commits at lists.jboss.org
Tue Mar 31 18:10:16 EDT 2009
Author: bstansberry at jboss.com
Date: 2009-03-31 18:10:16 -0400 (Tue, 31 Mar 2009)
New Revision: 86553
Added:
trunk/cluster/src/etc/farm-deployment-jboss-beans.xml
trunk/cluster/src/main/org/jboss/profileservice/
trunk/cluster/src/main/org/jboss/profileservice/cluster/
trunk/cluster/src/main/org/jboss/profileservice/cluster/repository/
trunk/cluster/src/main/org/jboss/profileservice/cluster/repository/DefaultRepositoryClusteringHandler.java
trunk/cluster/src/main/org/jboss/profileservice/cluster/repository/DefaultSynchronizationPolicy.java
trunk/system/src/main/org/jboss/system/server/profileservice/StaticClusteredProfileFactory.java
trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/
trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/ClusteredDeploymentRepository.java
trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/ClusteredDeploymentRepositoryFactory.java
trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/ImmutableClusteredDeploymentRepository.java
trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/RepositoryClusteringHandler.java
trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/local/
trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/local/AbstractContentMetadataPersister.java
trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/local/AbstractLocalContentManager.java
trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/local/ContentMetadataPersister.java
trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/local/JAXBRepositoryContentMetadataPersister.java
trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/local/LocalContentManager.java
trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/local/LocalContentManagerFactory.java
trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/local/file/
trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/local/file/AbstractLocalContentChangeAction.java
trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/local/file/DirectoryTimestampUpdateAction.java
trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/local/file/FileBasedSynchronizationActionContext.java
trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/local/file/FileReadAction.java
trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/local/file/FileUtil.java
trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/local/file/FileWriteAction.java
trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/local/file/FilesystemLocalContentManager.java
trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/local/file/FilesystemLocalContentManagerFactory.java
trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/local/file/InitiateRmdirAction.java
trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/local/file/MkDirAction.java
trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/local/file/RemoveFileAction.java
trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/metadata/
trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/metadata/AbstractSortedMetadataContainer.java
trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/metadata/ClusteredProfileSourceMetaData.java
trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/metadata/HotDeploymentClusteredProfileSourceMetaData.java
trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/metadata/Identifiable.java
trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/metadata/ImmutableClusteredProfileSourceMetaData.java
trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/metadata/RepositoryContentMetadata.java
trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/metadata/RepositoryItemMetadata.java
trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/metadata/RepositoryRootMetadata.java
trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/
trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/AbstractContentMetadataMutatorAction.java
trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/AbstractContentModificationGenerator.java
trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/AbstractSynchronizationAction.java
trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/AbstractSynchronizationPolicy.java
trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/ByteChunk.java
trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/ContentModification.java
trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/ImmutableSynchronizationPolicy.java
trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/InconsistentRepositoryStructureException.java
trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/LocalContentModificationGenerator.java
trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/NoOpRepositorySynchronizationWriteAction.java
trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/NoOpSynchronizationAction.java
trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/RemoteContentModificationGenerator.java
trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/RemoteRemovalAction.java
trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/RemovalMetadataInsertionAction.java
trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/SimpleSynchronizationRemoteAction.java
trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/StreamReadAction.java
trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/SynchronizationAction.java
trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/SynchronizationActionContext.java
trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/SynchronizationId.java
trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/SynchronizationInitiationAction.java
trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/SynchronizationPolicy.java
trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/SynchronizationReadAction.java
trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/SynchronizationRemoteAction.java
trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/SynchronizationWriteAction.java
trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/TwoPhaseCommitAction.java
trunk/system/src/tests/org/jboss/test/server/profileservice/clustered/
trunk/system/src/tests/org/jboss/test/server/profileservice/clustered/test/
trunk/system/src/tests/org/jboss/test/server/profileservice/clustered/test/JAXBRepositoryContentMetadataPersisterUnitTestCase.java
trunk/system/src/tests/org/jboss/test/server/profileservice/clustered/test/MockSynchronizationPolicy.java
trunk/system/src/tests/org/jboss/test/server/profileservice/clustered/test/RemoteContentModificationGeneratorUnitTestCase.java
Modified:
trunk/cluster/.classpath
trunk/cluster/pom.xml
Log:
[JBAS-5552] ClusteredDeploymentRepository
Modified: trunk/cluster/.classpath
===================================================================
--- trunk/cluster/.classpath 2009-03-31 22:07:34 UTC (rev 86552)
+++ trunk/cluster/.classpath 2009-03-31 22:10:16 UTC (rev 86553)
@@ -2,7 +2,9 @@
<classpath>
<classpathentry excluding="org/jbossmx/" kind="src" path="src/main"/>
<classpathentry kind="src" path="src/examples"/>
+ <classpathentry kind="src" path="src/tests"/>
<classpathentry exported="true" kind="src" path="/server"/>
+ <classpathentry combineaccessrules="false" kind="src" path="/system"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry exported="true" kind="lib" path="/thirdparty/jgroups/lib/jgroups.jar" sourcepath="/JGroups/src"/>
<classpathentry exported="true" kind="lib" path="/thirdparty/apache-logging/lib/commons-logging.jar"/>
@@ -15,6 +17,6 @@
<classpathentry kind="lib" path="/thirdparty/jboss/common-logging-spi/lib/jboss-logging-spi.jar"/>
<classpathentry kind="lib" path="/thirdparty/jboss/common-core/lib/jboss-common-core.jar" sourcepath="/thirdparty/jboss/common-core/lib/jboss-common-core-sources.jar"/>
<classpathentry kind="lib" path="/thirdparty/jboss/microcontainer/lib/jboss-dependency.jar" sourcepath="/thirdparty/jboss/microcontainer/lib/jboss-dependency-sources.jar"/>
- <classpathentry kind="lib" path="/thirdparty/jboss/jboss-bootstrap/lib/jboss-bootstrap.jar"/>
+ <classpathentry kind="lib" path="/thirdparty/jboss/jboss-bootstrap/lib/jboss-bootstrap.jar" sourcepath="/thirdparty/jboss/jboss-bootstrap/lib/jboss-bootstrap-sources.jar"/>
<classpathentry kind="output" path="output/eclipse-classes"/>
</classpath>
Modified: trunk/cluster/pom.xml
===================================================================
--- trunk/cluster/pom.xml 2009-03-31 22:07:34 UTC (rev 86552)
+++ trunk/cluster/pom.xml 2009-03-31 22:10:16 UTC (rev 86553)
@@ -241,6 +241,11 @@
<dependency>
<groupId>org.jboss.remoting</groupId>
<artifactId>jboss-remoting</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.jboss.integration</groupId>
+ <artifactId>jboss-profileservice-spi</artifactId>
</dependency>
</dependencies>
Added: trunk/cluster/src/etc/farm-deployment-jboss-beans.xml
===================================================================
--- trunk/cluster/src/etc/farm-deployment-jboss-beans.xml (rev 0)
+++ trunk/cluster/src/etc/farm-deployment-jboss-beans.xml 2009-03-31 22:10:16 UTC (rev 86553)
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<deployment xmlns="urn:jboss:bean-deployer:2.0">
+
+ <bean name="FarmProfileRepositoryClusteringHandler"
+ class="org.jboss.profileservice.cluster.repository.DefaultRepositoryClusteringHandler">
+
+ <property name="partition"><inject bean="HAPartition"/></property>
+ <property name="profileName">farm</property>
+ <property name="synchronizationPolicy"><inject bean="FarmProfileSynchronizationPolicy"/></property>
+ </bean>
+
+ <bean name="FarmProfileSynchronizationPolicy"
+ class="org.jboss.profileservice.cluster.repository.DefaultSynchronizationPolicy"/>
+
+</deployment>
Property changes on: trunk/cluster/src/etc/farm-deployment-jboss-beans.xml
___________________________________________________________________
Name: svn:keywords
+
Added: trunk/cluster/src/main/org/jboss/profileservice/cluster/repository/DefaultRepositoryClusteringHandler.java
===================================================================
--- trunk/cluster/src/main/org/jboss/profileservice/cluster/repository/DefaultRepositoryClusteringHandler.java (rev 0)
+++ trunk/cluster/src/main/org/jboss/profileservice/cluster/repository/DefaultRepositoryClusteringHandler.java 2009-03-31 22:10:16 UTC (rev 86553)
@@ -0,0 +1,1301 @@
+/*
+ * 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.profileservice.cluster.repository;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.jboss.ha.framework.interfaces.ClusterNode;
+import org.jboss.ha.framework.interfaces.DistributedReplicantManager;
+import org.jboss.ha.framework.interfaces.HAPartition;
+import org.jboss.ha.framework.interfaces.DistributedReplicantManager.ReplicantListener;
+import org.jboss.ha.framework.server.lock.LocalAndClusterLockManager;
+import org.jboss.ha.framework.server.lock.TimeoutException;
+import org.jboss.kernel.spi.dependency.KernelControllerContext;
+import org.jboss.kernel.spi.dependency.KernelControllerContextAware;
+import org.jboss.logging.Logger;
+import org.jboss.profileservice.spi.ProfileKey;
+import org.jboss.system.server.profileservice.repository.clustered.RepositoryClusteringHandler;
+import org.jboss.system.server.profileservice.repository.clustered.local.LocalContentManager;
+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.sync.ByteChunk;
+import org.jboss.system.server.profileservice.repository.clustered.sync.ContentModification;
+import org.jboss.system.server.profileservice.repository.clustered.sync.ImmutableSynchronizationPolicy;
+import org.jboss.system.server.profileservice.repository.clustered.sync.InconsistentRepositoryStructureException;
+import org.jboss.system.server.profileservice.repository.clustered.sync.LocalContentModificationGenerator;
+import org.jboss.system.server.profileservice.repository.clustered.sync.RemoteContentModificationGenerator;
+import org.jboss.system.server.profileservice.repository.clustered.sync.SynchronizationAction;
+import org.jboss.system.server.profileservice.repository.clustered.sync.SynchronizationId;
+import org.jboss.system.server.profileservice.repository.clustered.sync.SynchronizationInitiationAction;
+import org.jboss.system.server.profileservice.repository.clustered.sync.SynchronizationPolicy;
+import org.jboss.system.server.profileservice.repository.clustered.sync.SynchronizationReadAction;
+import org.jboss.system.server.profileservice.repository.clustered.sync.SynchronizationRemoteAction;
+import org.jboss.system.server.profileservice.repository.clustered.sync.SynchronizationWriteAction;
+import org.jboss.virtual.VirtualFile;
+
+/**
+ * {@link RepositoryClusteringHandler} implementation that uses {@link HAPartition}.
+ *
+ * @author Brian Stansberry
+ *
+ * @version $Revision: $
+ */
+public class DefaultRepositoryClusteringHandler
+ implements RepositoryClusteringHandler, KernelControllerContextAware
+{
+ private static final Logger log = Logger.getLogger(DefaultRepositoryClusteringHandler.class);
+
+ private static final Class<?>[] JOIN_SYNCHRONIZE_TYPES =
+ new Class[]{RepositoryContentMetadata.class, RepositoryContentMetadata.class};
+
+ private static final Class<?>[] MERGE_SYNCHRONIZE_TYPES =
+ new Class[]{RepositoryContentMetadata.class};
+
+ private static final Class<?>[] INITIATE_SYNCHRONIZATION_TYPES =
+ new Class[]{SynchronizationId.class, List.class};
+
+ private static final Class<?>[] TX_TYPES = new Class[]{SynchronizationId.class};
+
+ private static final Class<?>[] EXECUTE_MOD_TYPES =
+ new Class[]{SynchronizationId.class, RepositoryItemMetadata.class, boolean.class};
+
+ private static final Class<?>[] PULL_BYTES_TYPES =
+ new Class[]{SynchronizationId.class, RepositoryItemMetadata.class};
+
+ private static final Class<?>[] PUSH_BYTES_TYPES =
+ new Class[]{SynchronizationId.class, RepositoryItemMetadata.class, ByteChunk.class};
+
+ public static final long DEFAULT_TIMEOUT = 60000;
+
+ private String serviceHAName;
+ private HAPartition partition;
+ private String profileDomain;
+ private String profileServer;
+ private String profileName;
+ private boolean immutable;
+ private LocalAndClusterLockManager lockSupport;
+ private SynchronizationPolicy synchronizationPolicy;
+ private LocalContentManager<?> contentManager;
+ private RpcTarget rpcTarget = new RpcTarget();
+ private boolean inSync = false;
+ private long lockTimeout = DEFAULT_TIMEOUT;
+ private long methodCallTimeout = DEFAULT_TIMEOUT;
+ private volatile ActiveSynchronization activeSynchronization;
+ private final DRMListener drmListener = new DRMListener();
+ private List<ClusterNode> serviceView;
+ private boolean initialized;
+
+
+ public HAPartition getPartition()
+ {
+ return partition;
+ }
+
+ public void setPartition(HAPartition partition)
+ {
+ checkUnitialized();
+ this.partition = partition;
+ }
+
+ public String getProfileDomain()
+ {
+ return profileDomain;
+ }
+
+ public void setProfileDomain(String profileDomain)
+ {
+ checkUnitialized();
+ this.profileDomain = profileDomain;
+ }
+
+ public String getProfileServer()
+ {
+ return profileServer;
+ }
+
+ public void setProfileServer(String profileServer)
+ {
+ checkUnitialized();
+ this.profileServer = profileServer;
+ }
+
+ public String getProfileName()
+ {
+ return profileName;
+ }
+
+ public void setProfileName(String profileName)
+ {
+ checkUnitialized();
+ this.profileName = profileName;
+ }
+
+ /**
+ * Gets the name under which we register ourself with the HAPartition.
+ *
+ * @return the name
+ */
+ public String getServiceHAName()
+ {
+ return serviceHAName;
+ }
+
+ /**
+ * Sets the name under which we register ourself with the HAPartition.
+ *
+ * @param serviceHAName the name
+ */
+ public void setServiceHAName(String serviceHAName)
+ {
+ checkUnitialized();
+ this.serviceHAName = serviceHAName;
+ }
+
+ public SynchronizationPolicy getSynchronizationPolicy()
+ {
+ return synchronizationPolicy;
+ }
+
+ public void setSynchronizationPolicy(SynchronizationPolicy synchronizationPolicy)
+ {
+ checkUnitialized();
+ if (!immutable)
+ {
+ this.synchronizationPolicy = synchronizationPolicy;
+ }
+ }
+
+ public boolean isImmutable()
+ {
+ return immutable;
+ }
+
+ public void setImmutable(boolean immutable)
+ {
+ checkUnitialized();
+ if (immutable && !this.immutable)
+ {
+ setSynchronizationPolicy(new ImmutableSynchronizationPolicy());
+ }
+ this.immutable = immutable;
+ }
+
+ public long getLockTimeout()
+ {
+ return lockTimeout;
+ }
+
+ public void setLockTimeout(long lockTimeout)
+ {
+ this.lockTimeout = lockTimeout;
+ }
+
+ public long getMethodCallTimeout()
+ {
+ return methodCallTimeout;
+ }
+
+ public void setMethodCallTimeout(long methodCallTimeout)
+ {
+ this.methodCallTimeout = methodCallTimeout;
+ }
+
+
+ // -------------------------------------------- RepositoryClusteringHandler
+
+ public void initialize(LocalContentManager<?> persister) throws Exception
+ {
+ if (persister == null)
+ {
+ throw new IllegalArgumentException("Null persister");
+ }
+
+ if (this.partition == null)
+ {
+ throw new IllegalStateException("Null partition; must inject an " +
+ "HAPartition before invoking initialize");
+ }
+ if (this.serviceHAName == null)
+ {
+ throw new IllegalStateException("Null serviceHAName; must inject a " +
+ "serviceHAName before invoking initialize");
+ }
+ if (this.synchronizationPolicy == null)
+ {
+ throw new IllegalStateException("Null synchronizationPolicy; must " +
+ "inject a RepositorySynchronizationPolicy before invoking initialize");
+ }
+
+ this.contentManager = persister;
+
+ String lockServiceName = this.serviceHAName + "-ClusterLock";
+ this.lockSupport = new LocalAndClusterLockManager(lockServiceName, partition);
+
+ this.lockSupport.start();
+
+ this.partition.registerRPCHandler(getServiceHAName(), rpcTarget);
+
+ DistributedReplicantManager drm = this.partition.getDistributedReplicantManager();
+ drm.add(getServiceHAName(), this.partition.getClusterNode());
+ this.serviceView = drm.lookupReplicantsNodes(getServiceHAName());
+ drm.registerListener(getServiceHAName(), drmListener);
+
+ this.initialized = true;
+ }
+
+ public void shutdown() throws Exception
+ {
+ DistributedReplicantManager drm = this.partition.getDistributedReplicantManager();
+ drm.unregisterListener(getServiceHAName(), drmListener);
+ drm.remove(getServiceHAName());
+ this.partition.unregisterRPCHandler(getServiceHAName(), rpcTarget);
+ this.lockSupport.stop();
+
+ this.contentManager = null;
+
+ this.initialized = false;
+ }
+
+ public String getPartitionName()
+ {
+ return this.partition == null ? null : this.partition.getPartitionName();
+ }
+
+ public String getLocalNodeName()
+ {
+ ClusterNode localNode = this.partition == null ? null : this.partition.getClusterNode();
+ return localNode == null ? null : localNode.getName();
+ }
+
+ public ProfileKey getProfileKey()
+ {
+ return new ProfileKey(profileDomain, profileServer, profileName);
+ }
+
+ public RepositoryContentMetadata synchronizeContent(boolean pullFromCluster)
+ throws InconsistentRepositoryStructureException, IOException
+ {
+ List<ContentModification> modifications = null;
+ RepositoryContentMetadata localCurrentContent = this.contentManager.getCurrentContentMetadata();
+
+ if (pullFromCluster)
+ {
+ // Normal join case
+ modifications = getModificationsFromCluster(localCurrentContent);
+ }
+ else if (!inSync)
+ {
+ // Merge case
+ modifications = getModificationsFromCluster(null);
+ }
+ else
+ {
+ // Periodic scan case
+ modifications = getLocalModifications(localCurrentContent);
+ }
+
+ if (modifications != null)
+ {
+ installModifications(modifications, localCurrentContent);
+ }
+ else
+ {
+ // No one else out there. Install the localCurrentContent as official.
+ this.contentManager.installCurrentContentMetadata();
+ }
+
+ RepositoryContentMetadata result = this.contentManager.getOfficialContentMetadata();
+ inSync = true;
+ return result;
+ }
+
+ public VirtualFile addDeploymentContent(String vfsPath, InputStream contentIS) throws IOException
+ {
+ RepositoryItemMetadata item = this.contentManager.getItemForAddition(vfsPath);
+ RepositoryContentMetadata updated = this.contentManager.getContentMetadataForAdd(item, contentIS);
+ RepositoryContentMetadata official = this.contentManager.getOfficialContentMetadata();
+ LocalContentModificationGenerator generator = new LocalContentModificationGenerator();
+ List<ContentModification> modifications;
+ try
+ {
+ modifications = generator.getModificationList(official, updated);
+ }
+ catch (InconsistentRepositoryStructureException e)
+ {
+ throw new IllegalStateException("Incompatible structure change", e);
+ }
+
+ installModifications(modifications, updated);
+
+ return this.contentManager.getVirtualFileForItem(item);
+ }
+
+ public void removeDeploymentContent(VirtualFile vf) throws Exception
+ {
+ RepositoryContentMetadata updated = this.contentManager.getContentMetadataForRemove(vf);
+ RepositoryContentMetadata official = this.contentManager.getOfficialContentMetadata();
+ LocalContentModificationGenerator generator = new LocalContentModificationGenerator();
+ List<ContentModification> modifications;
+ try
+ {
+ modifications = generator.getModificationList(official, updated);
+ }
+ catch (InconsistentRepositoryStructureException e)
+ {
+ throw new IllegalStateException("Incompatible structure change", e);
+ }
+
+ installModifications(modifications, updated);
+ }
+
+ public boolean lockGlobally()
+ {
+ try
+ {
+ lockSupport.lockGlobally(getServiceHAName(), lockTimeout);
+ return true;
+ }
+ catch (TimeoutException e)
+ {
+ log.info("Unable to acquire global lock: " + e.getLocalizedMessage());
+ }
+ catch (InterruptedException e)
+ {
+ log.info("Interrupted while obtaining global lock: " + e.getLocalizedMessage());
+ Thread.currentThread().interrupt();
+ }
+ return false;
+ }
+
+ public boolean lockLocally()
+ {
+ try
+ {
+ lockSupport.lockLocally(getServiceHAName(), lockTimeout);
+ return true;
+ }
+ catch (TimeoutException e)
+ {
+ log.info("Unable to acquire local lock: " + e.getLocalizedMessage());
+ }
+ catch (InterruptedException e)
+ {
+ log.info("Interrupted while obtaining global lock: " + e.getLocalizedMessage());
+ Thread.currentThread().interrupt();
+ }
+ return false;
+ }
+
+ public void unlockGlobally()
+ {
+ lockSupport.unlockGlobally(getServiceHAName());
+ }
+
+ public void unlockLocally()
+ {
+ lockSupport.unlockLocally(getServiceHAName());
+ }
+
+ // -------------------------------------------- KernelControllerContextAware
+
+ /**
+ * Registers the context name as the {@link #setServiceHAName(String) serviceHAName}
+ * if it is not already set.
+ *
+ * {@inheritDoc}
+ */
+ public void setKernelControllerContext(KernelControllerContext context) throws Exception
+ {
+ if (context != null && serviceHAName == null)
+ {
+ setServiceHAName(context.getName().toString());
+ }
+ }
+
+ /**
+ * This implementation is a no-op.
+ *
+ * {@inheritDoc}
+ */
+ public void unsetKernelControllerContext(KernelControllerContext context) throws Exception
+ {
+ // no-op
+ }
+
+ // -------------------------------------------------------------- Protected
+
+ /**
+ * For benefit of unit tests, exposes the object we register with the HAPartition.
+ * Not intended for use outside of unit tests.
+ *
+ * TODO see if we can eliminate this by having the unit tests capture
+ * the object registered with the HAPartition.
+ */
+ protected RpcTarget getRpcTarget()
+ {
+ return rpcTarget;
+ }
+
+ /**
+ * For benefit of unit tests, exposes the object we register with the DRM
+ * as a {@link ReplicantListener}. Not intended for use outside of unit tests.
+ *
+ * TODO see if we can eliminate this by having the unit tests capture
+ * the object registered with the DRM.
+ */
+ protected DRMListener getDRMListener()
+ {
+ return drmListener;
+ }
+
+ // ---------------------------------------------------------------- Private
+
+ private List<ContentModification> getModificationsFromCluster(RepositoryContentMetadata localCurrentContent)
+ throws IOException, InconsistentRepositoryStructureException
+ {
+ List<ContentModification> modifications = null;
+
+ RepositoryContentMetadata localBaseMetadata = this.contentManager.getOfficialContentMetadata();
+ if (localBaseMetadata == null)
+ {
+ localBaseMetadata = this.contentManager.createEmptyContentMetadata();
+ }
+
+ List<ClusterNode> nodes = this.partition.getDistributedReplicantManager().lookupReplicantsNodes(getServiceHAName());
+ for (ClusterNode node : nodes)
+ {
+ if (node.equals(this.partition.getClusterNode()))
+ {
+ continue;
+ }
+ try
+ {
+ Object rsp = null;
+ String methodName = null;
+ if (localCurrentContent != null)
+ {
+ methodName = "joinSynchronizeContent";
+ Object[] args = { localBaseMetadata, localCurrentContent };
+ rsp = this.partition.callMethodOnNode(getServiceHAName(), methodName, args, JOIN_SYNCHRONIZE_TYPES, methodCallTimeout, node);
+ }
+ else
+ {
+ methodName = "mergeSynchronizeContent";
+ Object[] args = { localBaseMetadata };
+ rsp = this.partition.callMethodOnNode(getServiceHAName(), methodName, args, MERGE_SYNCHRONIZE_TYPES, methodCallTimeout, node);
+
+ }
+ if (rsp instanceof NotSynchronizedException)
+ {
+ throw (NotSynchronizedException) rsp;
+ }
+ else if (rsp instanceof List)
+ {
+ @SuppressWarnings("unchecked")
+ List<ContentModification> mods = (List<ContentModification>) rsp;
+ modifications = mods;
+ break;
+ }
+ else
+ {
+ log.warn("Unknown response to " + methodName + ": " + rsp);
+ }
+
+ }
+ catch (NotSynchronizedException ignored)
+ {
+ log.debug("Cannot synchronize with " + node + " as it itself is not yet synchronized");
+ }
+ catch (InconsistentRepositoryStructureException e)
+ {
+ throw e;
+ }
+ catch (Throwable t)
+ {
+ rethrowAsUnchecked(t);
+ }
+ }
+ return modifications;
+ }
+
+ private List<ContentModification> getLocalModifications(RepositoryContentMetadata localCurrentContent)
+ {
+ RepositoryContentMetadata official = this.contentManager.getOfficialContentMetadata();
+ LocalContentModificationGenerator generator = new LocalContentModificationGenerator();
+ try
+ {
+ return generator.getModificationList(official, localCurrentContent);
+ }
+ catch (InconsistentRepositoryStructureException e)
+ {
+ throw new IllegalStateException("Incompatible structure change", e);
+ }
+ }
+
+ private void installModifications(List<ContentModification> modifications, RepositoryContentMetadata toInstall)
+ {
+ SynchronizationId<ClusterNode> id =
+ new SynchronizationId<ClusterNode>(this.partition.getClusterNode());
+
+ boolean committed = false;
+ try
+ {
+ // First, inform the cluster of the synchronization, let them
+ // establish their local modifications
+
+ Object[] args = {id, modifications};
+ List<?> rsps = this.partition.callMethodOnCluster(getServiceHAName(),
+ "initiateSynchronization", args, INITIATE_SYNCHRONIZATION_TYPES, true);
+ for (Object rsp : rsps)
+ {
+ if (rsp instanceof NotSynchronizedException)
+ {
+ continue;
+ }
+ else if (rsp instanceof Exception)
+ {
+ throw (Exception) rsp;
+ }
+ }
+
+ // Do it ourself
+ handleInitiateSynchronization(id, modifications, toInstall);
+
+ // Next, cycle through the actions
+ executeSynchronizationActions();
+
+ // Tell the cluster to "prepare" all actions; e.g. move temp files
+ // into the real area, do deletes etc
+
+ rsps = this.partition.callMethodOnCluster(getServiceHAName(), "prepare", new Object[]{id}, TX_TYPES, true);
+ boolean prepareOK = true;
+ for (Object rsp : rsps)
+ {
+ if (rsp instanceof NotSynchronizedException)
+ {
+ continue;
+ }
+ else if (rsp instanceof Exception)
+ {
+ throw (Exception) rsp;
+ }
+ else if (Boolean.TRUE.equals(rsp) == false)
+ {
+ prepareOK = false;
+ break;
+ }
+ }
+
+ if (prepareOK)
+ {
+ // Do it ourself
+ if (handlePrepare(id))
+ {
+ // Tell cluster to finalize
+ this.partition.callMethodOnCluster(getServiceHAName(), "commit", new Object[]{id}, TX_TYPES, true);
+ handleCommit(id);
+ committed = true;
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ rethrowAsUnchecked(e);
+ }
+ finally
+ {
+ if (!committed)
+ {
+ // Roll back
+ try
+ {
+ this.partition.callMethodOnCluster(getServiceHAName(), "rollback", new Object[]{id}, TX_TYPES, true);
+ }
+ catch (Exception e)
+ {
+ log.error("Failed to roll back synchronization " + id + " on remote nodes", e);
+ }
+ finally
+ {
+ handleRollback(id);
+ }
+ }
+ }
+ }
+
+ private void executeSynchronizationActions() throws Exception
+ {
+ ActiveSynchronization active = activeSynchronization;
+ if (active == null)
+ {
+ throw new IllegalStateException("No active synchronization");
+ }
+
+ SynchronizationId<?> id = active.getId();
+ synchronized (active)
+ {
+ for (SynchronizationAction<?> action : active.getActions())
+ {
+ if (!active.isAlive())
+ {
+ throw new RuntimeException("Synchronization " + id + " terminated");
+ }
+
+ if (action instanceof SynchronizationReadAction)
+ {
+ executePush(id, (SynchronizationReadAction<?>) action);
+ }
+ else if (action instanceof SynchronizationWriteAction)
+ {
+ executePull(id, (SynchronizationWriteAction<?>) action);
+ }
+ else if (action instanceof SynchronizationRemoteAction)
+ {
+ executeRemoteAction(id, (SynchronizationRemoteAction<?>) action);
+ }
+ else
+ {
+ action.complete();
+ }
+ }
+ }
+ }
+
+ private void executePull(SynchronizationId<?> id, SynchronizationWriteAction<?> action) throws Exception
+ {
+ List<ClusterNode> peers = this.partition.getDistributedReplicantManager().lookupReplicantsNodes(getServiceHAName());
+ peers.remove(this.partition.getClusterNode());
+
+ boolean ok = false;
+ Exception lastCaught = null;
+ for (ClusterNode peer : peers)
+ {
+ try
+ {
+ if (executePullFromPeer(id, action, peer))
+ {
+ ok = true;
+ break;
+ }
+ }
+ catch (Exception e)
+ {
+ lastCaught = e;
+ }
+ // Reset the action for the next go round
+ action.cancel();
+ }
+
+ if (!ok)
+ {
+ if (lastCaught != null)
+ {
+ throw lastCaught;
+ }
+ else
+ {
+ throw new RuntimeException("No node able to provide item " +
+ action.getRepositoryContentModification().getItem());
+ }
+ }
+
+ action.complete();
+ }
+
+ private boolean executePullFromPeer(SynchronizationId<?> id, SynchronizationWriteAction<?> action, ClusterNode node) throws Exception
+ {
+ int lastRead = 0;
+ while (lastRead > -1)
+ {
+ Object[] args = new Object[]{ id, action.getRepositoryContentModification().getItem()};
+ Object rsp = null;
+ try
+ {
+ rsp = this.partition.callMethodOnNode(getServiceHAName(), "pullBytes", args, PULL_BYTES_TYPES, methodCallTimeout, node);
+ }
+ catch (Throwable t)
+ {
+ rethrowAsException(t);
+ }
+
+ if (rsp instanceof ByteChunk)
+ {
+ ByteChunk chunk = (ByteChunk) rsp;
+ lastRead = chunk.getByteCount();
+ if (lastRead > -1)
+ {
+ action.writeBytes(chunk);
+ }
+ }
+ else if (rsp instanceof NotSynchronizedException)
+ {
+ return false;
+ }
+ else if (rsp instanceof Throwable)
+ {
+ rethrowAsException((Throwable) rsp);
+ }
+ else if (rsp == null)
+ {
+ return false;
+ }
+ else
+ {
+ throw new IllegalStateException("Unknown response " + rsp);
+ }
+ }
+
+ return true;
+ }
+
+ private void executePush(SynchronizationId<?> id, SynchronizationReadAction<?> action) throws Exception
+ {
+ int lastRead = 0;
+ while (lastRead > -1)
+ {
+ try
+ {
+ ByteChunk chunk = action.getNextBytes();
+
+ Object[] args = new Object[]{ id, action.getRepositoryContentModification().getItem(), chunk};
+ List<?> rsps = this.partition.callMethodOnCluster(getServiceHAName(), "pushBytes", args, PUSH_BYTES_TYPES, true);
+ for (Object rsp : rsps)
+ {
+ if (rsp instanceof NotSynchronizedException)
+ {
+ continue;
+ }
+ else if (rsp instanceof Throwable)
+ {
+ rethrowAsException((Throwable) rsp);
+ }
+ }
+ lastRead = chunk.getByteCount();
+ }
+ catch (Exception e)
+ {
+ action.cancel();
+ throw e;
+ }
+ }
+ action.complete();
+ }
+
+ private void executeRemoteAction(SynchronizationId<?> id,
+ SynchronizationRemoteAction<?> action) throws Exception
+ {
+ Object[] args = new Object[]{ id, action.getRepositoryContentModification().getItem(), action.isInitiation()};
+ List<?> rsps = null;
+ try
+ {
+ rsps = this.partition.callMethodOnCluster(getServiceHAName(), "executeModification", args, EXECUTE_MOD_TYPES, true);
+ }
+ catch (Exception e)
+ {
+ action.cancel();
+ throw e;
+ }
+
+ action.complete(); // We sent the command, so it is complete even if we fail below
+
+ for (Object rsp : rsps)
+ {
+ if (rsp instanceof NotSynchronizedException)
+ {
+ continue;
+ }
+ else if (rsp instanceof Throwable)
+ {
+ rethrowAsException((Throwable) rsp);
+ }
+ }
+ }
+
+ private void checkUnitialized()
+ {
+ if (initialized)
+ {
+ throw new IllegalStateException("Cannot reconfigure an initialized " +
+ getClass().getSimpleName());
+ }
+ }
+
+ private void handleInitiateSynchronization(SynchronizationId<ClusterNode> id, List<ContentModification> modifications, RepositoryContentMetadata toInstall)
+ {
+ boolean localLed = id.getOriginator().equals(partition.getClusterNode());
+ List<? extends SynchronizationAction<?>> actions =
+ contentManager.initiateSynchronization(id, modifications, toInstall, localLed);
+
+ activeSynchronization = new ActiveSynchronization(id, actions);
+ }
+
+ private boolean handlePrepare(SynchronizationId<ClusterNode> id) throws NotSynchronizedException
+ {
+ ActiveSynchronization active = activeSynchronization;
+ if (active != null)
+ {
+ synchronized (active)
+ {
+ active.validate(id);
+
+ return active.prepare();
+ }
+ }
+ else
+ {
+ throw new NotSynchronizedException();
+ }
+ }
+
+ private void handleCommit(SynchronizationId<ClusterNode> id)
+ {
+ ActiveSynchronization active = activeSynchronization;
+ if (active != null)
+ {
+ synchronized (active)
+ {
+ active.validate(id);
+
+ try
+ {
+ active.commit();
+ }
+ finally
+ {
+ activeSynchronization = null;
+ }
+ }
+ }
+ }
+
+ private void handleRollback(SynchronizationId<ClusterNode> id)
+ {
+ ActiveSynchronization active = activeSynchronization;
+ if (active != null)
+ {
+ synchronized (active)
+ {
+ active.validate(id);
+
+ try
+ {
+ active.rollback();
+ }
+ finally
+ {
+ activeSynchronization = null;
+ }
+ }
+ }
+ }
+
+ private static void rethrowAsException(Throwable t) throws Exception
+ {
+ if (t == null)
+ return;
+ if (t instanceof Exception)
+ {
+ throw (Exception) t;
+ }
+ else if (t instanceof Error)
+ {
+ throw (Error) t;
+ }
+ else
+ {
+ throw new RuntimeException(t);
+ }
+ }
+
+ private static void rethrowAsUnchecked(Throwable t)
+ {
+ if (t == null)
+ return;
+ if (t instanceof RuntimeException)
+ {
+ throw (RuntimeException) t;
+ }
+ else if (t instanceof Error)
+ {
+ throw (Error) t;
+ }
+ else
+ {
+ throw new RuntimeException(t);
+ }
+ }
+
+ // ---------------------------------------------------------------- Classes
+
+ public class RpcTarget
+ {
+ public List<ContentModification> joinSynchronizeContent(
+ RepositoryContentMetadata remoteBaseContent,
+ RepositoryContentMetadata remoteCurrentContent)
+ throws NotSynchronizedException, InconsistentRepositoryStructureException
+ {
+ if (!inSync)
+ {
+ throw new NotSynchronizedException();
+ }
+ RemoteContentModificationGenerator generator =
+ new RemoteContentModificationGenerator(synchronizationPolicy, remoteBaseContent);
+ return generator.getModificationList(contentManager.getOfficialContentMetadata(), remoteCurrentContent);
+ }
+
+ public List<ContentModification> mergeSynchronizeContent(
+ RepositoryContentMetadata remoteCurrentContent)
+ throws NotSynchronizedException, InconsistentRepositoryStructureException
+ {
+ if (!inSync)
+ {
+ throw new NotSynchronizedException();
+ }
+
+ RemoteContentModificationGenerator generator =
+ new RemoteContentModificationGenerator(synchronizationPolicy);
+ return generator.getModificationList(contentManager.getOfficialContentMetadata(), remoteCurrentContent);
+ }
+
+ public void initiateSynchronization(SynchronizationId<ClusterNode> id,
+ List<ContentModification> modifications)
+ throws NotSynchronizedException
+ {
+ if (!inSync)
+ {
+ throw new NotSynchronizedException();
+ }
+
+ // Create the metadata we'll install when we are done
+ RepositoryContentMetadata toInstall = new RepositoryContentMetadata(contentManager.getOfficialContentMetadata());
+
+ handleInitiateSynchronization(id, modifications, toInstall);
+ }
+
+ public void executeModification(SynchronizationId<ClusterNode> id,
+ RepositoryItemMetadata item,
+ boolean initiation)
+ throws NotSynchronizedException
+ {
+ if (!inSync)
+ {
+ throw new NotSynchronizedException();
+ }
+
+ ActiveSynchronization active = activeSynchronization;
+ if (active != null)
+ {
+ synchronized (active)
+ {
+ active.validate(id);
+
+ SynchronizationAction<?> action =
+ (initiation ? active.getInitiationAction(item) : active.getMiscAction(item));
+ if (action != null)
+ {
+ action.complete();
+ }
+ else
+ {
+ throw new IllegalStateException("No action for " + item);
+ }
+ }
+ }
+ else
+ {
+ throw new NotSynchronizedException();
+ }
+ }
+
+ public void pushBytes(SynchronizationId<ClusterNode> id,
+ RepositoryItemMetadata item,
+ ByteChunk chunk)
+ throws NotSynchronizedException, IOException
+ {
+ if (!inSync)
+ {
+ throw new NotSynchronizedException();
+ }
+
+ ActiveSynchronization active = activeSynchronization;
+ if (active != null)
+ {
+ synchronized (active)
+ {
+ active.validate(id);
+
+ SynchronizationWriteAction<?> action = active.getWriteAction(item);
+ if (action != null)
+ {
+ if (chunk.getByteCount() < 0)
+ {
+ action.complete();
+ }
+ else
+ {
+ action.writeBytes(chunk);
+ }
+ }
+ else
+ {
+ throw new IllegalStateException("No action for " + item);
+ }
+ }
+ }
+ else
+ {
+ throw new NotSynchronizedException();
+ }
+ }
+
+ public ByteChunk pullBytes(SynchronizationId<ClusterNode> id,
+ RepositoryItemMetadata item)
+ throws NotSynchronizedException, IOException
+ {
+ if (!inSync)
+ {
+ throw new NotSynchronizedException();
+ }
+
+ ActiveSynchronization active = activeSynchronization;
+ if (active != null)
+ {
+ synchronized (active)
+ {
+ active.validate(id);
+
+ SynchronizationReadAction<?> action = active.getReadAction(item);
+ if (action != null)
+ {
+ return action.getNextBytes();
+ }
+ else
+ {
+ throw new IllegalStateException("No action for " + item);
+ }
+ }
+ }
+ else
+ {
+ throw new NotSynchronizedException();
+ }
+ }
+
+ public boolean prepare(SynchronizationId<ClusterNode> id)
+ throws NotSynchronizedException
+ {
+ if (!inSync)
+ {
+ throw new NotSynchronizedException();
+ }
+
+ return handlePrepare(id);
+ }
+
+ public void commit(SynchronizationId<ClusterNode> id)
+ {
+ if (inSync)
+ {
+ handleCommit(id);
+ }
+ }
+
+ public void rollback(SynchronizationId<ClusterNode> id)
+ throws NotSynchronizedException
+ {
+ if (inSync)
+ {
+ handleRollback(id);
+ }
+ }
+ }
+
+ public class DRMListener implements ReplicantListener
+ {
+ @SuppressWarnings("unchecked")
+ public void replicantsChanged(String key, List newReplicants, int newReplicantsViewId, boolean merge)
+ {
+ List<ClusterNode> oldView = serviceView;
+ serviceView = newReplicants;
+
+ // Abort any ongoing synchronization that if the originator is
+ // out of the view
+ ActiveSynchronization active = activeSynchronization;
+ if (active != null)
+ {
+ synchronized (active)
+ {
+ if (serviceView.contains(active.getId().getOriginator()) == false)
+ {
+ contentManager.rollbackSynchronization(active.getId());
+ activeSynchronization = null;
+ }
+ }
+ }
+
+ if (merge)
+ {
+ ClusterNode master = (ClusterNode) (newReplicants.size() > 0 ? newReplicants.get(0) : null);
+ inSync = (master != null && oldView.contains(master));
+ }
+ }
+ }
+
+ private static class NotSynchronizedException extends Exception
+ {
+ /** The serialVersionUID */
+ private static final long serialVersionUID = -923676063561479453L;
+ }
+
+ private class ActiveSynchronization
+ {
+ private final SynchronizationId<ClusterNode> id;
+ private final List<? extends SynchronizationAction<?>> localActions;
+ private final Map<RepositoryItemMetadata, SynchronizationAction<?>> miscActionsByItem =
+ new HashMap<RepositoryItemMetadata, SynchronizationAction<?>>();
+ private final Map<RepositoryItemMetadata, SynchronizationReadAction<?>> readActionsByItem =
+ new HashMap<RepositoryItemMetadata, SynchronizationReadAction<?>>();
+ private final Map<RepositoryItemMetadata, SynchronizationWriteAction<?>> writeActionsByItem =
+ new HashMap<RepositoryItemMetadata, SynchronizationWriteAction<?>>();
+ private final Map<RepositoryItemMetadata, SynchronizationInitiationAction<?>> initiationActionsByItem =
+ new HashMap<RepositoryItemMetadata, SynchronizationInitiationAction<?>>();
+ private volatile boolean alive = true;
+
+ private ActiveSynchronization(SynchronizationId<ClusterNode> id, List<? extends SynchronizationAction<?>> localActions)
+ {
+ if (id == null)
+ {
+ throw new IllegalArgumentException("Null id");
+ }
+ if (localActions == null)
+ {
+ throw new IllegalArgumentException("Null localActions");
+ }
+ this.id = id;
+ this.localActions = localActions;
+ for (SynchronizationAction<?> action : localActions)
+ {
+ RepositoryItemMetadata item = action.getRepositoryContentModification().getItem();
+ if (action instanceof SynchronizationInitiationAction<?>)
+ {
+ initiationActionsByItem.put(item, (SynchronizationInitiationAction<?>) action);
+ }
+ else if (action instanceof SynchronizationReadAction<?>)
+ {
+ readActionsByItem.put(item, (SynchronizationReadAction<?>) action);
+ }
+ else if (action instanceof SynchronizationWriteAction<?>)
+ {
+ writeActionsByItem.put(item, (SynchronizationWriteAction<?>) action);
+ }
+ else
+ {
+ miscActionsByItem.put(item, action);
+ }
+ }
+ }
+
+ public SynchronizationId<ClusterNode> getId()
+ {
+ return id;
+ }
+
+ public List<? extends SynchronizationAction<?>> getActions()
+ {
+ return localActions;
+ }
+
+ public void validate(SynchronizationId<ClusterNode> id)
+ {
+ if (this.id.equals(id) == false)
+ {
+ throw new IllegalStateException("Invalid id " + id +
+ " another synchronization " + this.getId() +
+ " is in progress");
+ }
+ }
+
+ public SynchronizationReadAction<?> getReadAction(RepositoryItemMetadata item)
+ {
+ return readActionsByItem.get(item);
+ }
+
+ public SynchronizationWriteAction<?> getWriteAction(RepositoryItemMetadata item)
+ {
+ return writeActionsByItem.get(item);
+ }
+
+ public SynchronizationInitiationAction<?> getInitiationAction(RepositoryItemMetadata item)
+ {
+ return initiationActionsByItem.get(item);
+ }
+
+ public SynchronizationAction<?> getMiscAction(RepositoryItemMetadata item)
+ {
+ return miscActionsByItem.get(item);
+ }
+
+ public boolean isAlive()
+ {
+ return alive;
+ }
+
+ public boolean prepare()
+ {
+ if (alive)
+ {
+ return contentManager.prepareSynchronization(id);
+ }
+ return false;
+ }
+
+ public void rollback()
+ {
+ if (alive)
+ {
+ alive = false;
+ synchronized (this)
+ {
+ contentManager.rollbackSynchronization(id);
+ }
+ }
+ }
+
+ public void commit()
+ {
+ if (alive)
+ {
+ synchronized (this)
+ {
+ contentManager.commitSynchronization(id);
+ }
+ alive = false;
+ }
+ }
+ }
+
+}
Property changes on: trunk/cluster/src/main/org/jboss/profileservice/cluster/repository/DefaultRepositoryClusteringHandler.java
___________________________________________________________________
Name: svn:keywords
+
Added: trunk/cluster/src/main/org/jboss/profileservice/cluster/repository/DefaultSynchronizationPolicy.java
===================================================================
--- trunk/cluster/src/main/org/jboss/profileservice/cluster/repository/DefaultSynchronizationPolicy.java (rev 0)
+++ trunk/cluster/src/main/org/jboss/profileservice/cluster/repository/DefaultSynchronizationPolicy.java 2009-03-31 22:10:16 UTC (rev 86553)
@@ -0,0 +1,151 @@
+/*
+ * 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.profileservice.cluster.repository;
+
+import org.jboss.ha.timestamp.TimestampDiscrepancy;
+import org.jboss.ha.timestamp.TimestampDiscrepancyService;
+import org.jboss.system.server.profileservice.repository.clustered.metadata.RepositoryItemMetadata;
+import org.jboss.system.server.profileservice.repository.clustered.sync.AbstractSynchronizationPolicy;
+import org.jboss.system.server.profileservice.repository.clustered.sync.SynchronizationPolicy;
+
+/**
+ * Default implementation of {@link SynchronizationPolicy}.
+ *
+ * @author Brian Stansberry
+ *
+ * @version $Revision: $
+ *
+ */
+public class DefaultSynchronizationPolicy extends AbstractSynchronizationPolicy
+{
+ private TimestampDiscrepancyService timestampService;
+
+ public TimestampDiscrepancyService getTimestampService()
+ {
+ return timestampService;
+ }
+
+ public void setTimestampService(TimestampDiscrepancyService timestampService)
+ {
+ this.timestampService = timestampService;
+ }
+
+
+ // ---------------------------------------------------- Protected Overrides
+
+ /**
+ * Always accepts merge additions; accepts join additions if the
+ * time-discrepancy adjusted timestamp of <code>toAdd</code> is later than
+ * the current time minus {@link #getRemovalTrackingTime() the maximum
+ * period we track item removals}. The idea of the latter being that if
+ * a previous version of <code>toAdd</code> was removed more recently than
+ * that then we should have a record of its removal.
+ */
+ protected boolean acceptAddition(RepositoryItemMetadata toAdd, RepositoryItemMetadata joinersPrevious,
+ boolean merge)
+ {
+ if (merge)
+ {
+ return true;
+ }
+ else
+ {
+ TimestampDiscrepancy addDiscrepancy = timestampService.getTimestampDiscrepancy(toAdd.getOriginatingNode(), false);
+ long adjustedTimestamp = addDiscrepancy.getMinLocalTimestamp(toAdd.getTimestamp());
+ return adjustedTimestamp > System.currentTimeMillis() - getRemovalTrackingTime();
+ }
+ }
+
+ /**
+ * Accepts the reincarnation if the time-discrepancy adjusted timestamp of
+ * <code>reincarnation</code> is at least as recent as the time-discrepancy
+ * adjusted timestamp of the <code>current</code> item.
+ */
+ protected boolean acceptReincarnation(RepositoryItemMetadata reincarnation, RepositoryItemMetadata current,
+ boolean merge)
+ {
+ TimestampDiscrepancy addDiscrepancy = timestampService.getTimestampDiscrepancy(reincarnation.getOriginatingNode(), false);
+ TimestampDiscrepancy deadDiscrepancy = timestampService.getTimestampDiscrepancy(current.getOriginatingNode(), false);
+ return isChangeMoreRecent(reincarnation, current, addDiscrepancy, deadDiscrepancy, false);
+ }
+
+ /**
+ * Rejects removal if <code>sendersView</code> is <code>null</code>, else
+ * accepts the removal if the time-discrepancy adjusted timestamp of
+ * <code>sendersView</code> is at least as recent as the time-discrepancy
+ * adjusted timestamp of the <code>current</code> item.
+ */
+ protected boolean acceptRemoval(RepositoryItemMetadata current, RepositoryItemMetadata sendersView,
+ boolean merge)
+ {
+ if (sendersView == null)
+ {
+ return false;
+ }
+
+ TimestampDiscrepancy senderTimestampDiscrepancy = timestampService.getTimestampDiscrepancy(sendersView.getOriginatingNode(), false);
+ TimestampDiscrepancy currentTimestampDiscrepancy = timestampService.getTimestampDiscrepancy(current.getOriginatingNode(), false);
+
+ return isChangeMoreRecent(sendersView, current, senderTimestampDiscrepancy, currentTimestampDiscrepancy, true);
+ }
+
+ /**
+ * Accepts the removal if the time-discrepancy adjusted timestamp of
+ * <code>update</code> is at least as recent as the time-discrepancy
+ * adjusted timestamp of the <code>current</code> item.
+ */
+ protected boolean acceptUpdate(RepositoryItemMetadata update, RepositoryItemMetadata current,
+ boolean merge)
+ {
+ TimestampDiscrepancy updateDiscrepancy = timestampService.getTimestampDiscrepancy(update.getOriginatingNode(), false);
+ TimestampDiscrepancy currentTimestampDiscrepancy = timestampService.getTimestampDiscrepancy(current.getOriginatingNode(), false);
+
+ return isChangeMoreRecent(update, current, updateDiscrepancy, currentTimestampDiscrepancy, false);
+ }
+
+ // ---------------------------------------------------------------- Private
+
+ private static boolean isChangeMoreRecent(RepositoryItemMetadata toChange, RepositoryItemMetadata current,
+ TimestampDiscrepancy senderTimestampDiscrepancy, TimestampDiscrepancy currentTimestampDiscrepancy, boolean equalAllowed)
+ {
+ if (currentTimestampDiscrepancy == null)
+ {
+ // Just have to hope for the best
+ currentTimestampDiscrepancy = TimestampDiscrepancy.NO_DISCREPANCY;
+ }
+
+ long senderTime, currentTime;
+ if (toChange.getOriginatingNode().equals(current.getOriginatingNode()))
+ {
+ senderTime = toChange.getTimestamp();
+ currentTime = current.getTimestamp();
+ }
+ else
+ {
+ senderTime = senderTimestampDiscrepancy.getMinLocalTimestamp(toChange.getTimestamp());
+ currentTime = currentTimestampDiscrepancy.getMaxLocalTimestamp(current.getTimestamp());
+ }
+ return equalAllowed ? senderTime > currentTime :senderTime > currentTime;
+ }
+
+}
Property changes on: trunk/cluster/src/main/org/jboss/profileservice/cluster/repository/DefaultSynchronizationPolicy.java
___________________________________________________________________
Name: svn:keywords
+
Added: trunk/system/src/main/org/jboss/system/server/profileservice/StaticClusteredProfileFactory.java
===================================================================
--- trunk/system/src/main/org/jboss/system/server/profileservice/StaticClusteredProfileFactory.java (rev 0)
+++ trunk/system/src/main/org/jboss/system/server/profileservice/StaticClusteredProfileFactory.java 2009-03-31 22:10:16 UTC (rev 86553)
@@ -0,0 +1,228 @@
+/*
+ * 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;
+
+import java.net.URI;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.jboss.profileservice.spi.ProfileKey;
+import org.jboss.profileservice.spi.metadata.ProfileMetaData;
+import org.jboss.profileservice.spi.metadata.ProfileSourceMetaData;
+import org.jboss.profileservice.spi.metadata.SubProfileMetaData;
+import org.jboss.system.server.profile.repository.metadata.BasicProfileMetaData;
+import org.jboss.system.server.profile.repository.metadata.BasicSubProfileMetaData;
+import org.jboss.system.server.profileservice.repository.StaticProfileFactory;
+import org.jboss.system.server.profileservice.repository.clustered.metadata.ClusteredProfileSourceMetaData;
+import org.jboss.system.server.profileservice.repository.clustered.metadata.HotDeploymentClusteredProfileSourceMetaData;
+import org.jboss.system.server.profileservice.repository.clustered.metadata.ImmutableClusteredProfileSourceMetaData;
+
+/**
+ * Expands upon the StaticProfileFactory to include a subprofiles
+ * for farmed content.
+ *
+ * @author Brian Stansberry
+ */
+public class StaticClusteredProfileFactory extends StaticProfileFactory
+{
+ /** The bootstrap profile name. */
+ private static final String BOOTSTRAP_NAME = "bootstrap";
+
+ /** The deployers profile name. */
+ private static final String DEPLOYERS_NAME = "deployers";
+
+ /** The deployers profile name. */
+ private static final String APPLICATIONS_NAME = "applications";
+
+ /** The deploy-hasingleton profile name. */
+ private static final String HASINGLETON_NAME = "deploy-hasingleton";
+
+ /** The farm profile name. */
+ private static final String FARM_NAME = "farm";
+
+ /** The hasingleton uris. */
+ private List<URI> hasingletonURIs;
+
+ /** The farm uris. */
+ private List<URI> farmURIs;
+
+// private String partitionName;
+
+ public List<URI> getHASingletonURIs()
+ {
+ return hasingletonURIs;
+ }
+
+ public void setHASingletonURIs(List<URI> hasingletonURIs)
+ {
+ this.hasingletonURIs = hasingletonURIs;
+ }
+
+ public List<URI> getFarmURIs()
+ {
+ return farmURIs;
+ }
+
+ public void setFarmURIs(List<URI> farmURIs)
+ {
+ this.farmURIs = farmURIs;
+ }
+
+// public String getPartitionName()
+// {
+// return partitionName;
+// }
+//
+// public void setPartitionName(String partitionName)
+// {
+// this.partitionName = partitionName;
+// }
+//
+// @Override
+// public void create() throws Exception
+// {
+// super.create();
+//
+// if (this.farmURIs != null || this.hasingletonURIs != null)
+// {
+// if (this.partitionName == null)
+// {
+// throw new IllegalStateException("Null partition name.");
+// }
+// }
+// }
+
+ /**
+ * Create the legacy profiles, based on the injected uris.
+ *
+ * @param rootKey the key for the root profile.
+ * @throws Exception
+ */
+ @Override
+ protected void createProfileMetaData(ProfileKey rootKey, URL url) throws Exception
+ {
+ if(rootKey == null)
+ throw new IllegalArgumentException("Null root profile key.");
+
+ // Create bootstrap profile meta data
+ ProfileKey bootstrapKey = new ProfileKey(BOOTSTRAP_NAME);
+ ProfileMetaData bootstrap = createProfileMetaData(
+ BOOTSTRAP_NAME, false, new URI[] { getBootstrapURI() }, new String[0]);
+ addProfile(bootstrapKey, bootstrap);
+
+ // Create deployers profile meta data
+ ProfileKey deployersKey = new ProfileKey(DEPLOYERS_NAME);
+ ProfileMetaData deployers = createProfileMetaData(
+ DEPLOYERS_NAME, false, new URI[] { getDeployersURI() }, new String[] { BOOTSTRAP_NAME });
+ addProfile(deployersKey, deployers);
+
+ // Create applications profile;
+ ProfileKey applicationsKey = new ProfileKey(APPLICATIONS_NAME);
+ URI[] deployURIs = getApplicationURIs().toArray(new URI[getApplicationURIs().size()]);
+ String[] applicationSubProfiles = new String[] { BOOTSTRAP_NAME, DEPLOYERS_NAME };
+ ProfileMetaData applications = createProfileMetaData(
+ APPLICATIONS_NAME, true, deployURIs, applicationSubProfiles);
+ addProfile(applicationsKey, applications);
+
+ ProfileMetaData farm = null;
+ if (getFarmURIs() != null)
+ {
+ ProfileKey farmKey = new ProfileKey(FARM_NAME);
+ URI[] farmURIs = getFarmURIs().toArray(new URI[getFarmURIs().size()]);
+ String[] farmSubProfiles = new String[] { APPLICATIONS_NAME };
+ farm = createClusteredProfileMetaData(
+ FARM_NAME, true, farmURIs, farmSubProfiles);
+ addProfile(farmKey, farm);
+ }
+
+ String[] rootSubProfiles = farm == null ? new String[]{APPLICATIONS_NAME}
+ : new String[] { FARM_NAME };
+ ProfileMetaData root = createProfileMetaData(
+ rootKey.getName(), true, new URI[0], rootSubProfiles);
+ addProfile(rootKey, root);
+
+ if (getHASingletonURIs() != null)
+ {
+ ProfileKey hasingletonKey = new ProfileKey(HASINGLETON_NAME);
+ URI[] hasingletonURIs = getHASingletonURIs().toArray(new URI[getHASingletonURIs().size()]);
+ // Note HASingleton can't depend on others or it will get undeployed
+ // prematurely
+ String[] hasingletonSubProfiles = new String[0];
+ ProfileMetaData hasingletons = createProfileMetaData(
+ HASINGLETON_NAME, true, hasingletonURIs, hasingletonSubProfiles);
+ addProfile(hasingletonKey, hasingletons);
+ }
+ }
+
+ private ProfileMetaData createClusteredProfileMetaData(String name, boolean hotDeployment, URI[] uris, String[] subProfiles)
+ {
+ // Create profile
+ BasicProfileMetaData metaData = new BasicProfileMetaData();
+ metaData.setName(name);
+ // Create profile sources
+ ProfileSourceMetaData source = createClusteredSource(uris, hotDeployment);
+ metaData.setSource(source);
+
+ List<SubProfileMetaData> profileList = new ArrayList<SubProfileMetaData>();
+ for(String subProfile : subProfiles)
+ {
+ BasicSubProfileMetaData md = new BasicSubProfileMetaData();
+ md.setName(subProfile);
+ profileList.add(md);
+ }
+ metaData.setSubprofiles(profileList);
+
+ return metaData;
+ }
+
+ /**
+ * Create a profile repository source meta data.
+ *
+ * @param uris the uris for the repository
+ * @param hotDeployment to create a hotDeployment profile
+ *
+ * @return the profile source meta data.
+ */
+ protected ProfileSourceMetaData createClusteredSource(URI[] uris, boolean hotDeployment)
+ {
+ ClusteredProfileSourceMetaData source = null;
+ if(hotDeployment)
+ {
+ source = new HotDeploymentClusteredProfileSourceMetaData();
+ }
+ else
+ {
+ source = new ImmutableClusteredProfileSourceMetaData();
+ }
+
+// source.setPartitionName(getPartitionName());
+
+ List<String> sources = new ArrayList<String>();
+ for(URI uri : uris)
+ sources.add(uri.toString());
+ source.setSources(sources);
+ return source;
+ }
+
+}
Property changes on: trunk/system/src/main/org/jboss/system/server/profileservice/StaticClusteredProfileFactory.java
___________________________________________________________________
Name: svn:keywords
+
Added: trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/ClusteredDeploymentRepository.java
===================================================================
--- trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/ClusteredDeploymentRepository.java (rev 0)
+++ trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/ClusteredDeploymentRepository.java 2009-03-31 22:10:16 UTC (rev 86553)
@@ -0,0 +1,814 @@
+/*
+ * 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;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+import org.jboss.deployers.vfs.spi.structure.modified.StructureModificationChecker;
+import org.jboss.profileservice.spi.DeploymentContentFlags;
+import org.jboss.profileservice.spi.ModificationInfo;
+import org.jboss.profileservice.spi.ProfileDeployment;
+import org.jboss.profileservice.spi.ProfileKey;
+import org.jboss.profileservice.spi.ModificationInfo.ModifyStatus;
+import org.jboss.system.server.profileservice.repository.AbstractDeploymentRepository;
+import org.jboss.system.server.profileservice.repository.clustered.local.LocalContentManager;
+import org.jboss.system.server.profileservice.repository.clustered.local.LocalContentManagerFactory;
+import org.jboss.system.server.profileservice.repository.clustered.metadata.RepositoryContentMetadata;
+import org.jboss.virtual.VirtualFile;
+
+/**
+ * @author Brian Stansberry
+ *
+ * @version $Revision: $
+ */
+public class ClusteredDeploymentRepository
+ extends AbstractDeploymentRepository
+{
+ private enum LoadStatus { UNLOADED, LOADING, LOADED };
+
+ /** Handles interaction with the cluster */
+ private RepositoryClusteringHandler clusteringHandler;
+ /** Handles storing our RepositoryContentMetadata */
+// private final ContentMetadataPersister persister;
+ /** Our current view of our content */
+// private RepositoryContentMetadata contentMetadata;
+ /** URIs of the VFS roots we monitor, keyed by their name */
+ private final Map<String, URI> namedURIMap;
+
+ private final Map<ProfileKey, RepositoryClusteringHandler> clusteringHandlers;
+ private final Set<LocalContentManagerFactory<?>> localContentManagerFactories ;
+ private LocalContentManager<?> localContentManager;
+
+// private final Map<Object, OutputStream> tempStreamMap = new ConcurrentHashMap<Object, OutputStream>();
+ /**
+ * Whether we are loaded. Mutations of this field must occur with
+ * at least a local lock.
+ */
+ private volatile LoadStatus loadStatus = LoadStatus.UNLOADED;
+
+ /** The structure modification checker */
+ private StructureModificationChecker checker;
+ private boolean created;
+
+ /**
+ * Create a new ClusteredDeploymentRepository.
+ *
+ * @param key
+ * @param uris
+ * @throws IOException
+ */
+ public ClusteredDeploymentRepository(ProfileKey key, URI[] uris,
+ Map<ProfileKey, RepositoryClusteringHandler> clusteringHandlers,
+ Set<LocalContentManagerFactory<?>> persisterFactories)
+ throws IOException
+ {
+ super(key, uris);
+
+ if (clusteringHandlers == null)
+ {
+ throw new IllegalArgumentException("Null clusteringHandlers");
+ }
+
+ if (persisterFactories == null)
+ {
+ throw new IllegalArgumentException("Null persisterFactories");
+ }
+ this.namedURIMap = getNamedURIMap(uris);
+ this.clusteringHandlers = clusteringHandlers;
+ this.localContentManagerFactories = persisterFactories;
+ }
+
+ // -------------------------------------------------------------- Properties
+
+ public String getPartitionName()
+ {
+ // Prefer to return the value from our handler
+ return this.clusteringHandler == null ? null
+ : this.clusteringHandler.getPartitionName();
+ }
+
+ public RepositoryClusteringHandler getClusteringHandler()
+ {
+ return clusteringHandler;
+ }
+
+ public synchronized boolean registerClusteringHandler(RepositoryClusteringHandler handler)
+ {
+ if (handler == null)
+ {
+ throw new IllegalArgumentException("handler is null");
+ }
+
+ boolean update = this.getProfileKey().equals(handler.getProfileKey());
+ if (update)
+ {
+ this.clusteringHandler = handler;
+ }
+ return update;
+ }
+
+ public synchronized boolean unregisterClusteringHandler(RepositoryClusteringHandler handler)
+ {
+ if (handler == null)
+ {
+ throw new IllegalArgumentException("handler is null");
+ }
+
+ boolean update = this.getProfileKey().equals(handler.getProfileKey());
+
+ if (update)
+ {
+ this.clusteringHandler = null;
+ }
+
+ return update;
+ }
+
+ /**
+ * Get the structure modified checker.
+ *
+ * @return the checker
+ */
+ protected StructureModificationChecker getChecker()
+ {
+ if (checker == null)
+ throw new IllegalArgumentException("Checker must be set");
+
+ return checker;
+ }
+
+ /**
+ * Set the checker.
+ *
+ * @param checker the checker
+ */
+ public void setChecker(StructureModificationChecker checker)
+ {
+ this.checker = checker;
+ }
+
+ // ---------------------------------------------------- DeploymentRepository
+
+ /**
+ * Extends superclass to check whether our configured URIs actually exist,
+ * if so validating that a clustering handler has been injected, throwing
+ * an <code>IllegalStateException</code> if not.
+ *
+ * {@inheritDoc}
+ */
+ @Override
+ public void create() throws Exception
+ {
+ super.create();
+
+ // See if our URIs actually exist. If not we don't care
+ // if we are missing required dependencies
+ boolean needToFunction = false;
+ for (URI uri : this.getRepositoryURIs())
+ {
+ if (getCachedVirtualFile(uri).exists())
+ {
+ needToFunction = true;
+ break;
+ }
+ }
+
+ if (needToFunction)
+ {
+ this.clusteringHandler = this.clusteringHandlers.get(getProfileKey());
+
+ if (this.clusteringHandler == null)
+ {
+ throw new IllegalStateException("Must register RepositoryClusteringHandler " +
+ "before calling create()");
+ }
+
+ for (LocalContentManagerFactory<?> factory : localContentManagerFactories)
+ {
+ if (factory.accepts(namedURIMap.values()))
+ {
+ this.localContentManager = factory.getRepositoryContentPersister(namedURIMap, getProfileKey(), this.clusteringHandler.getLocalNodeName());
+ break;
+ }
+ }
+
+ if (this.localContentManager == null)
+ {
+ throw new IllegalStateException("No registered RepositoryContentPersisterFactory " +
+ "is capable of handling URIs " + namedURIMap.values() +
+ " -- registeredFactories are " + localContentManagerFactories);
+ }
+ }
+
+ this.created = true;
+ }
+
+ public void load() throws Exception
+ {
+ if (!created)
+ {
+ create();
+ }
+
+ if (this.clusteringHandler == null)
+ {
+ // We would have failed in create() if we actually had any valid URIs
+ // configured. So this means there are no valid URIs and we
+ // have nothing to do.
+ return;
+ }
+
+ this.clusteringHandler.initialize(this.localContentManager);
+
+ // Take control of the cluster
+ if (!this.clusteringHandler.lockGlobally())
+ {
+ throw new RuntimeException("Cannot acquire global lock");
+ }
+ try
+ {
+ if (this.loadStatus != LoadStatus.UNLOADED)
+ {
+ log.warn("load() called when repository status is " + this.loadStatus);
+ return;
+ }
+
+ this.loadStatus = LoadStatus.LOADING;
+
+ // Bring our content in line with the cluster
+ clusteringHandler.synchronizeContent(true);
+
+ // Load applications
+ for(URI uri : getRepositoryURIs())
+ {
+ VirtualFile root = getCachedVirtualFile(uri);
+ loadApplications(root);
+ }
+
+ updateLastModfied();
+
+ this.loadStatus = LoadStatus.LOADED;
+ }
+ catch (Throwable t)
+ {
+ // Revert back to unloaded status
+ this.loadStatus = LoadStatus.UNLOADED;
+
+// this.contentMetadata = null;
+ if (t instanceof Exception)
+ {
+ throw (Exception) t;
+ }
+ else if (t instanceof Error)
+ {
+ throw (Error) t;
+ }
+ else
+ {
+ UnknownError unk = new UnknownError("Caught unknown Throwable");
+ unk.initCause(t);
+ throw unk;
+ }
+ }
+ finally
+ {
+ this.clusteringHandler.unlockGlobally();
+
+ if (this.loadStatus != LoadStatus.LOADED)
+ {
+ this.clusteringHandler.shutdown();
+ }
+ }
+ }
+
+ public String addDeploymentContent(String vfsPath, InputStream contentIS) throws IOException
+ {
+ if (this.clusteringHandler == null)
+ {
+ throw new IllegalStateException("Must register RepositoryClusteringHandler before invoking addDeploymentContent()");
+ }
+
+ String repositoryName = null;
+
+ // Take control of the cluster
+ if (!this.clusteringHandler.lockGlobally())
+ {
+ throw new RuntimeException("Cannot acquire global lock");
+ }
+ try
+ {
+ VirtualFile contentVF = this.clusteringHandler.addDeploymentContent(vfsPath, contentIS);
+
+ try
+ {
+ contentVF = getCachedVirtualFile(contentVF.toURI());
+ repositoryName = contentVF.toURI().toString();
+ }
+ catch(URISyntaxException e)
+ {
+ throw new RuntimeException(e); // This should not happen anyway
+ }
+
+ // Lock the content
+ lockDeploymentContent(vfsPath);
+ }
+ finally
+ {
+ this.clusteringHandler.unlockGlobally();
+ }
+
+ return repositoryName;
+ }
+
+ public ProfileDeployment removeDeployment(String vfsPath) throws Exception
+ {
+ ProfileDeployment deployment = getDeployment(vfsPath);
+ VirtualFile root = deployment.getRoot();
+ this.clusteringHandler.removeDeploymentContent(root);
+ return super.removeDeployment(deployment.getName());
+ }
+
+ public Collection<ModificationInfo> getModifiedDeployments() throws Exception
+ {
+ if (this.clusteringHandler == null)
+ {
+ return Collections.emptyList();
+ }
+
+ if (this.loadStatus != LoadStatus.LOADED)
+ {
+ log.debug("Ignoring getModifiedDeployments() call as status is " + this.loadStatus);
+ }
+
+ // Only acquire a local lock until we know whether we actually
+ // have a modification
+ RepositoryContentMetadata baseContent = null;
+ RepositoryContentMetadata latestContent = null;
+ boolean unmodified = false;
+ if (!this.clusteringHandler.lockLocally())
+ {
+ // Don't throw an exception; just log error and indicate no mods
+ log.error("getModifiedDeployments(): Cannot acquire local lock");
+ return Collections.emptySet();
+ }
+ try
+ {
+ baseContent = this.localContentManager.getOfficialContentMetadata();
+ latestContent = this.localContentManager.getCurrentContentMetadata();
+ unmodified = latestContent.equals(baseContent);
+ }
+ finally
+ {
+ this.clusteringHandler.unlockLocally();
+ }
+
+ Collection<ModificationInfo> result = null;
+ if (unmodified)
+ {
+ // Done
+ result = Collections.emptySet();
+ }
+ else
+ {
+ // Something was modified, so take control of the cluster and
+ // bring the cluster in line with our current state
+
+ if (!this.clusteringHandler.lockGlobally())
+ {
+ // Don't throw an exception; just log error and indicate no mods
+ log.error("getModifiedDeployments(): Cannot acquire global lock");
+ return Collections.emptySet();
+ }
+ try
+ {
+ // Tell the clustering handler to synchronize, but without
+ // pulling anything to cluster -- just push our changes
+ latestContent = this.clusteringHandler.synchronizeContent(false);
+
+ return createModificationInfo();
+ }
+ finally
+ {
+ this.clusteringHandler.unlockGlobally();
+ }
+ }
+
+ return result;
+
+ }
+
+ public void unload()
+ {
+ if (this.clusteringHandler == null)
+ {
+ // We would have failed in create() if we actually had any valid URIs
+ // configured. So this means there are no valid URIs and we
+ // have nothing to do.
+ // Just call the superclass method to let it do whatever cleanup
+ // it wants.
+ super.unload();
+ return;
+ }
+
+ if (this.loadStatus != LoadStatus.UNLOADED)
+ {
+ boolean locked = this.clusteringHandler.lockLocally();
+ try
+ {
+ if (!locked)
+ {
+ log.warn("remove(): failed to acquire local lock");
+ }
+ this.loadStatus = LoadStatus.UNLOADED;
+// this.contentMetadata = null;
+
+ super.unload();
+
+ }
+ finally
+ {
+ if (locked)
+ {
+ this.clusteringHandler.unlockLocally();
+ }
+
+ try
+ {
+ this.clusteringHandler.shutdown();
+ }
+ catch (Exception e)
+ {
+ log.error("Caught exception shutting down RepositoryClusteringHandler", e);
+ }
+ }
+ }
+ }
+
+ public void remove() throws Exception
+ {
+ this.clusteringHandler = null;
+ this.localContentManager = null;
+ this.created = false;
+ }
+
+ // -------------------------------------- Legacy RepositoryContentPersister
+
+
+// public TemporaryOutputStreamHandback getTemporaryOutputStream() throws IOException
+// {
+// if (this.loadStatus != LoadStatus.UNLOADED)
+// {
+// File tmp = createTempFile();
+// tmp.deleteOnExit();
+// OutputStream os = new FileOutputStream(tmp);
+// this.tempStreamMap.put(tmp, os);
+// return new TemporaryOutputStreamHandback(tmp, os);
+// }
+// else
+// {
+// // We don't accept changes from the cluster when we are unloaded.
+// // Satisfy the contract with an object that does nothing
+// return new TemporaryOutputStreamHandback(LoadStatus.UNLOADED, new NullOutputStream());
+// }
+// }
+//
+// public void destroyTemporaryOutputStream(TemporaryOutputStreamHandback handback)
+// {
+// if (handback == null)
+// {
+// throw new IllegalArgumentException("handback is null");
+// }
+//
+// FileUtil.safeCloseStream(handback.getOutputStream(), handback.getHandback());
+//
+// Object key = handback.getHandback();
+// if (key instanceof File)
+// {
+// try
+// {
+// ((File) key).delete();
+// }
+// catch (Exception e)
+// {
+// log.trace("Failed to delete temporary file " + key, e);
+// }
+// }
+// }
+//
+// public void installRepositoryContentItem(TemporaryOutputStreamHandback handback,
+// String repositoryRoot, List<String> path, long timestamp) throws IOException
+// {
+// if (handback == null)
+// {
+// throw new IllegalArgumentException("handback is null");
+// }
+//
+// Object key = handback.getHandback();
+// File tmpFile = null;
+// if (key instanceof File)
+// {
+// tmpFile = (File) key;
+// }
+// else if (key == LoadStatus.UNLOADED)
+// {
+// // We weren't ready to deal with this request so we returned
+// // a fake. Ignore this call.
+// log.trace("Current status is " + LoadStatus.UNLOADED + " so ignoring" +
+// " request to add/update " + path + " in " + repositoryRoot);
+// return;
+// }
+// else
+// {
+// throw new IllegalArgumentException("Unknown handback type " + handback);
+// }
+//
+// OutputStream ourOS = tempStreamMap.remove(key);
+// if (ourOS == null)
+// {
+// // Tilt! But to be tidy close the stream
+// FileUtil.safeCloseStream(handback.getOutputStream(), key);
+// throw new IllegalStateException("Unknown handback " + key);
+// }
+//
+// try
+// {
+// FileUtil.safeCloseStream(ourOS, key);
+//
+// URI rootURI = namedURIMap.get(repositoryRoot);
+// if (rootURI == null)
+// {
+// throw new IllegalArgumentException("Unknown root " + repositoryRoot);
+// }
+//
+// File toReplace = new File(rootURI);
+// for (String element : path)
+// {
+// toReplace = new File(toReplace, element);
+// }
+// if (toReplace.exists())
+// {
+// toReplace.delete();
+// }
+//
+// FileUtil.localMove(tmpFile, toReplace );
+// toReplace.setLastModified(timestamp);
+//
+// RepositoryItemMetadata itemMD = new RepositoryItemMetadata();
+// itemMD.setRelativePathElements(path);
+// itemMD.setTimestamp(timestamp);
+//
+// RepositoryRootMetadata rootMD = contentMetadata.getRepositoryRootMetadata(repositoryRoot);
+// rootMD.addItemMetadata(itemMD);
+//
+// try
+// {
+// this.persister.store(getPartitionName(), this.contentMetadata);
+// }
+// catch (Exception e)
+// {
+// log.error("Exception peristent contentMetadata", e);
+// }
+// }
+// finally
+// {
+// if (tmpFile.exists())
+// {
+// if(!tmpFile.delete())
+// {
+// log.info("Could not delete file "+ tmpFile);
+// }
+// }
+// }
+//
+// }
+//
+// public void removeRepositoryContentItem(String repositoryRoot, List<String> path)
+// {
+// if (this.loadStatus == LoadStatus.UNLOADED)
+// {
+// // We don't accept changes from the cluster when we aren't loaded
+// log.trace("Current status is " + LoadStatus.UNLOADED + " so ignoring" +
+// " request to remove " + path + " from " + repositoryRoot);
+// return;
+// }
+//
+// URI rootURI = namedURIMap.get(repositoryRoot);
+// if (rootURI == null)
+// {
+// throw new IllegalArgumentException("Unknown root " + repositoryRoot);
+// }
+//
+// File file = new File(rootURI);
+// for (String element : path)
+// {
+// file = new File(file, element);
+// }
+// if (file.exists())
+// {
+// file.delete();
+// }
+//
+// RepositoryRootMetadata rootMD = contentMetadata.getRepositoryRootMetadata(repositoryRoot);
+// if (rootMD != null)
+// {
+// if (rootMD.removeItemMetadata(path))
+// {
+// try
+// {
+// this.persister.store(getPartitionName(), this.contentMetadata);
+// }
+// catch (Exception e)
+// {
+// log.error("Exception peristing contentMetadata", e);
+// }
+// }
+// }
+// }
+
+ // ----------------------------------------------------------------- Private
+
+ // BES -- retained for now in case I decide to scope the RepositoryClusteringHandler
+ // to a partition rather than to a ProfileKey
+
+// private boolean coordinateClusteringHandler(RepositoryClusteringHandler handler,
+// String partitionName)
+// {
+// boolean updated = false;
+//
+// if (this.clusteringHandler != null)
+// {
+// // We've had a handler injected, so this must be setting the
+// // partition name property. Check for consistency.
+// if (getPartitionName().equals(partitionName) == false)
+// {
+// throw new IllegalStateException("Cannot set partition name to " +
+// partitionName + " it is already set to " + getPartitionName());
+// }
+//
+// if (this.clusteringHandler != handler)
+// {
+// log.debug("Updating handler for partition " + partitionName);
+// this.clusteringHandler = handler;
+// updated = true;
+// }
+// }
+// else if (handler == null)
+// {
+// // No handler set and none passed in. Must be a call to set partitionName.
+// // Use a null-safe equals check to see if it's an update.
+// boolean same = (partitionName == this.partitionName
+// || (partitionName != null && partitionName.equals(this.partitionName)));
+// if (!same)
+// {
+// this.partitionName = partitionName;
+// updated = true;
+// }
+// }
+// else
+// {
+// // It's an attempt to inject a handler. See if it matches our
+// // required partition name
+// String handlerPartition = handler.getPartitionName();
+// if (this.partitionName == null || this.partitionName.equals(handlerPartition))
+// {
+// this.clusteringHandler = handler;
+// updated = true;
+// }
+// else
+// {
+// log.debug("Ignoring injected handler for partition " +
+// handlerPartition + "as we are configured for partition " +
+// this.partitionName);
+// }
+// }
+//
+// return updated;
+// }
+
+ private Map<String, URI> getNamedURIMap(URI[] uris) throws IOException
+ {
+ Map<String, URI> map = new HashMap<String, URI>();
+ if (uris != null)
+ {
+ for (URI uri : uris)
+ {
+ VirtualFile vf = getCachedVirtualFile(uri);
+ map.put(vf.getName(), uri);
+ }
+ }
+
+ return map;
+ }
+
+ /**
+ * FIXME This was cut-and-pasted from MutableDeploymentRepository. Turn
+ * the guts of it into a static utility method somewhere.
+ *
+ * @return
+ * @throws Exception
+ */
+ private Collection<ModificationInfo> createModificationInfo() throws Exception
+ {
+ ArrayList<ModificationInfo> modified = new ArrayList<ModificationInfo>();
+ Collection<ProfileDeployment> apps = getDeployments();
+ boolean trace = log.isTraceEnabled();
+ if (trace)
+ log.trace("Checking applications for modifications");
+ if (apps != null)
+ {
+ Iterator<ProfileDeployment> iter = apps.iterator();
+ int ignoreFlags = DeploymentContentFlags.LOCKED | DeploymentContentFlags.DISABLED;
+ while (iter.hasNext())
+ {
+ ProfileDeployment ctx = iter.next();
+ VirtualFile root = ctx.getRoot();
+ String pathName = root.getPathName();
+ // Ignore locked or disabled applications
+ if (this.hasDeploymentContentFlags(pathName, ignoreFlags))
+ {
+ if (trace)
+ log.trace("Ignoring locked application: " + root);
+ continue;
+ }
+ // Check for removal
+ if (root.exists() == false)
+ {
+ long rootLastModified = root.getLastModified();
+ ModificationInfo info = new ModificationInfo(ctx, rootLastModified, ModifyStatus.REMOVED);
+ modified.add(info);
+ iter.remove();
+ // Remove last modified cache
+ getChecker().removeStructureRoot(root);
+ if (trace)
+ log.trace(pathName + " was removed");
+ }
+ // Check for modification
+ else if (getChecker().hasStructureBeenModified(root) || hasDeploymentContentFlags(pathName, DeploymentContentFlags.MODIFIED))
+ {
+ long rootLastModified = root.getLastModified();
+ if (trace)
+ log.trace(pathName + " was modified: " + rootLastModified);
+ // Create the modification info
+ ModificationInfo info = new ModificationInfo(ctx, rootLastModified, ModifyStatus.MODIFIED);
+ modified.add(info);
+ }
+ }
+ // Now check for additions
+ for (URI applicationDir : getRepositoryURIs())
+ {
+ VirtualFile deployDir = getCachedVirtualFile(applicationDir);
+ ArrayList<VirtualFile> added = new ArrayList<VirtualFile>();
+ addedDeployments(added, deployDir);
+ for (VirtualFile vf : added)
+ {
+ if (this.hasDeploymentContentFlags(vf.getPathName(), ignoreFlags))
+ {
+ if (trace)
+ log.trace("Ignoring locked application: " + vf);
+ continue;
+ }
+ ProfileDeployment ctx = createDeployment(vf);
+ ModificationInfo info = new ModificationInfo(ctx, vf.getLastModified(), ModifyStatus.ADDED);
+ modified.add(info);
+ addDeployment(ctx.getName(), ctx);
+ getChecker().addStructureRoot(vf);
+ }
+ }
+ }
+
+ if (modified.size() > 0)
+ updateLastModfied();
+ return modified;
+ }
+
+}
Property changes on: trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/ClusteredDeploymentRepository.java
___________________________________________________________________
Name: svn:keywords
+
Added: trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/ClusteredDeploymentRepositoryFactory.java
===================================================================
--- trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/ClusteredDeploymentRepositoryFactory.java (rev 0)
+++ trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/ClusteredDeploymentRepositoryFactory.java 2009-03-31 22:10:16 UTC (rev 86553)
@@ -0,0 +1,238 @@
+/*
+ * 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;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.security.PrivilegedAction;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.jboss.deployers.vfs.spi.structure.modified.StructureModificationChecker;
+import org.jboss.profileservice.spi.DeploymentRepository;
+import org.jboss.profileservice.spi.DeploymentRepositoryFactory;
+import org.jboss.profileservice.spi.ProfileKey;
+import org.jboss.profileservice.spi.metadata.ProfileSourceMetaData;
+import org.jboss.system.server.profileservice.repository.clustered.local.LocalContentManagerFactory;
+import org.jboss.system.server.profileservice.repository.clustered.metadata.ClusteredProfileSourceMetaData;
+import org.jboss.system.server.profileservice.repository.clustered.metadata.HotDeploymentClusteredProfileSourceMetaData;
+import org.jboss.system.server.profileservice.repository.clustered.metadata.ImmutableClusteredProfileSourceMetaData;
+import org.jboss.virtual.VirtualFileFilter;
+
+/**
+ * Factory for clustered deployment repositories.
+ *
+ * @author Brian Stansberry
+ */
+public class ClusteredDeploymentRepositoryFactory implements DeploymentRepositoryFactory
+{
+ /** The mutable type. */
+ public final static String MUTABLE_TYPE = HotDeploymentClusteredProfileSourceMetaData.class.getName();
+
+ /** The immutable type. */
+ public final static String IMMUTABLE_TYPE = ImmutableClusteredProfileSourceMetaData.class.getName();
+
+ /** The repository types. */
+ private static final List<String> types = Arrays.asList(new String[]{ MUTABLE_TYPE, IMMUTABLE_TYPE });
+
+ private String defaultPartitionName;
+
+ /** The deployment filter. */
+ private VirtualFileFilter deploymentFilter;
+
+ /** The structure modification checker */
+ private StructureModificationChecker checker;
+
+ /** Factories for LocalContentManagers */
+ private final Set<LocalContentManagerFactory<?>> localContentManagerFactories = new HashSet<LocalContentManagerFactory<?>>();
+
+ private final Map<ProfileKey, RepositoryClusteringHandler> clusteringHandlers = new HashMap<ProfileKey, RepositoryClusteringHandler>();
+
+ // -------------------------------------------------------------- Properties
+
+ public String getDefaultPartitionName()
+ {
+ if (defaultPartitionName == null)
+ {
+ defaultPartitionName = new PrivilegedAction<String>()
+ {
+ public String run()
+ {
+ return System.getProperty("jboss.partition.name", "DefaultPartition");
+ }
+
+ }.run();
+ }
+ return defaultPartitionName;
+ }
+
+ public void setDefaultPartitionName(String defaultPartitionName)
+ {
+ this.defaultPartitionName = defaultPartitionName;
+ }
+
+ public VirtualFileFilter getDeploymentFilter()
+ {
+ return deploymentFilter;
+ }
+
+ public void setDeploymentFilter(VirtualFileFilter deploymentFilter)
+ {
+ this.deploymentFilter = deploymentFilter;
+ }
+
+ public StructureModificationChecker getChecker()
+ {
+ return checker;
+ }
+
+ public void setChecker(StructureModificationChecker checker)
+ {
+ this.checker = checker;
+ }
+
+
+ // -------------------------------------------- Install/Uninstall Callbacks
+
+
+ public void addRepositoryClusteringHandler(RepositoryClusteringHandler handler)
+ {
+ if (handler != null)
+ {
+ ProfileKey key = handler.getProfileKey();
+ synchronized (clusteringHandlers)
+ {
+ clusteringHandlers.put(key, handler);
+ }
+ }
+ }
+
+ public void removeRepositoryClusteringHandler(RepositoryClusteringHandler handler)
+ {
+ if (handler != null)
+ {
+ ProfileKey key = handler.getProfileKey();
+ synchronized (clusteringHandlers)
+ {
+ clusteringHandlers.remove(key);
+ }
+ }
+ }
+
+ public void addLocalContentManagerFactory(LocalContentManagerFactory<?> factory)
+ {
+ synchronized (localContentManagerFactories)
+ {
+ localContentManagerFactories.add(factory);
+ }
+ }
+
+ public void removeLocalContentManagerFactory(LocalContentManagerFactory<?> factory)
+ {
+ synchronized (localContentManagerFactories)
+ {
+ localContentManagerFactories.remove(factory);
+ }
+ }
+
+ // --------------------------------------------- DeploymentRepositoryFactory
+
+
+ public DeploymentRepository createDeploymentRepository(ProfileKey key, ProfileSourceMetaData metaData)
+ throws Exception
+ {
+ if(key == null)
+ throw new IllegalArgumentException("Null profile key.");
+ if(metaData == null)
+ throw new IllegalArgumentException("Null metaData");
+
+ if ((metaData instanceof ClusteredProfileSourceMetaData) == false)
+ {
+ throw new IllegalArgumentException("Incompatible metadata type " +
+ metaData.getClass().getName() + " -- a " +
+ ClusteredProfileSourceMetaData.class.getSimpleName() + " is required");
+ }
+
+ // Sanity check
+ String repositoryType = metaData.getType();
+ if(repositoryType == null)
+ throw new IllegalArgumentException("Null repository type.");
+ if(types.contains(repositoryType) == false)
+ throw new IllegalArgumentException("Cannot handle type: " + repositoryType);
+
+ ClusteredProfileSourceMetaData clusteredMD = (ClusteredProfileSourceMetaData) metaData;
+ if (clusteredMD.getPartitionName() == null)
+ {
+ clusteredMD.setPartitionName(getDefaultPartitionName());
+ }
+
+ URI[] uris = createUris(metaData);
+ Map<ProfileKey, RepositoryClusteringHandler> handlers = Collections.unmodifiableMap(this.clusteringHandlers);
+ Set<LocalContentManagerFactory<?>> persisters = Collections.unmodifiableSet(this.localContentManagerFactories);
+
+ boolean immutable = clusteredMD instanceof ImmutableClusteredProfileSourceMetaData;
+ if (immutable)
+ {
+ ImmutableClusteredDeploymentRepository repository = new ImmutableClusteredDeploymentRepository(key, uris, handlers, persisters);
+ // Manually inject beans :)
+ repository.setDeploymentFilter(deploymentFilter);
+ repository.setChecker(checker);
+
+ return repository;
+ }
+ else
+ {
+ ClusteredDeploymentRepository repository = new ClusteredDeploymentRepository(key, uris, handlers, persisters);
+ // Manually inject beans :)
+ repository.setDeploymentFilter(deploymentFilter);
+ repository.setChecker(checker);
+
+ return repository;
+ }
+ }
+
+ public String[] getTypes()
+ {
+ return types.toArray(new String[types.size()]);
+ }
+
+ // ----------------------------------------------------------------- Private
+
+ private static URI[] createUris(ProfileSourceMetaData metaData) throws URISyntaxException
+ {
+ List<URI> uris = new ArrayList<URI>();
+ for(String source : metaData.getSources())
+ {
+ URI uri = new URI(source);
+ uris.add(uri);
+ }
+ return uris.toArray(new URI[uris.size()]);
+ }
+
+}
Property changes on: trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/ClusteredDeploymentRepositoryFactory.java
___________________________________________________________________
Name: svn:keywords
+
Added: trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/ImmutableClusteredDeploymentRepository.java
===================================================================
--- trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/ImmutableClusteredDeploymentRepository.java (rev 0)
+++ trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/ImmutableClusteredDeploymentRepository.java 2009-03-31 22:10:16 UTC (rev 86553)
@@ -0,0 +1,87 @@
+/*
+ * 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;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+
+import org.jboss.profileservice.spi.ModificationInfo;
+import org.jboss.profileservice.spi.ProfileKey;
+import org.jboss.system.server.profileservice.repository.clustered.local.LocalContentManagerFactory;
+
+/**
+ * @author Brian Stansberry
+ *
+ */
+public class ImmutableClusteredDeploymentRepository extends ClusteredDeploymentRepository
+{
+
+ /**
+ * Create a new ImmutableClusteredDeploymentRepository.
+ *
+ * @param key
+ * @param uris
+ */
+ public ImmutableClusteredDeploymentRepository(ProfileKey key, URI[] uris,
+ Map<ProfileKey, RepositoryClusteringHandler> clusteringHandlers,
+ Set<LocalContentManagerFactory<?>> persisterFactories)
+ throws IOException
+ {
+ super(key, uris, clusteringHandlers, persisterFactories);
+ }
+
+ public Collection<ModificationInfo> getModifiedDeployments() throws Exception
+ {
+ return Collections.emptySet();
+ }
+
+ public String addDeploymentContent(String vfsPath, InputStream contentIS) throws IOException
+ {
+ throw new IllegalStateException("Cannot add content to an immutable repository.");
+ }
+
+ public void remove() throws Exception
+ {
+ throw new IllegalStateException("Cannot remove immutable repository.");
+ }
+
+ @Override
+ public synchronized boolean registerClusteringHandler(RepositoryClusteringHandler handler)
+ {
+ boolean update = super.registerClusteringHandler(handler);
+ if (update)
+ {
+ RepositoryClusteringHandler ours = getClusteringHandler();
+ ours.setImmutable(true);
+ }
+ return update;
+ }
+
+
+
+}
Property changes on: trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/ImmutableClusteredDeploymentRepository.java
___________________________________________________________________
Name: svn:keywords
+
Added: trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/RepositoryClusteringHandler.java
===================================================================
--- trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/RepositoryClusteringHandler.java (rev 0)
+++ trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/RepositoryClusteringHandler.java 2009-03-31 22:10:16 UTC (rev 86553)
@@ -0,0 +1,57 @@
+/*
+ * 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;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.jboss.profileservice.spi.ProfileKey;
+import org.jboss.system.server.profileservice.repository.clustered.local.LocalContentManager;
+import org.jboss.system.server.profileservice.repository.clustered.metadata.RepositoryContentMetadata;
+import org.jboss.system.server.profileservice.repository.clustered.sync.InconsistentRepositoryStructureException;
+import org.jboss.virtual.VirtualFile;
+
+/**
+ * @author Brian Stansberry
+ *
+ */
+public interface RepositoryClusteringHandler
+{
+ ProfileKey getProfileKey();
+ String getPartitionName();
+ String getLocalNodeName();
+
+ boolean isImmutable();
+ void setImmutable(boolean immutable);
+
+ void initialize(LocalContentManager<?> persister) throws Exception;
+ void shutdown() throws Exception;
+ boolean lockGlobally();
+ boolean lockLocally();
+ RepositoryContentMetadata synchronizeContent(boolean pullFromCluster)
+ throws InconsistentRepositoryStructureException, IOException;
+ VirtualFile addDeploymentContent(String vfsPath, InputStream contentIS) throws IOException;
+ void removeDeploymentContent(VirtualFile vf) throws Exception;
+ void unlockGlobally();
+ void unlockLocally();
+}
Property changes on: trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/RepositoryClusteringHandler.java
___________________________________________________________________
Name: svn:keywords
+
Added: trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/local/AbstractContentMetadataPersister.java
===================================================================
--- trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/local/AbstractContentMetadataPersister.java (rev 0)
+++ trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/local/AbstractContentMetadataPersister.java 2009-03-31 22:10:16 UTC (rev 86553)
@@ -0,0 +1,116 @@
+/*
+ * 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.local;
+
+import java.io.File;
+import java.io.NotSerializableException;
+
+import org.jboss.logging.Logger;
+import org.jboss.system.server.profileservice.repository.clustered.metadata.RepositoryContentMetadata;
+
+/**
+ * @author Brian Stansberry
+ *
+ */
+public abstract class AbstractContentMetadataPersister
+ implements ContentMetadataPersister
+{
+ private static final Logger log = Logger.getLogger(AbstractContentMetadataPersister.class);
+
+ private final File contentMetadataDir;
+
+ public AbstractContentMetadataPersister(File dir)
+ {
+ if(dir == null)
+ throw new IllegalArgumentException("Null store dir.");
+ this.contentMetadataDir = dir;
+ }
+
+ public RepositoryContentMetadata load(String baseName)
+ {
+ File attachmentsStore = getMetadataPath(baseName);
+ if( attachmentsStore.exists() == false )
+ {
+ return null;
+ }
+
+ try
+ {
+ return loadMetadata(attachmentsStore);
+ }
+ catch (RuntimeException e)
+ {
+ throw e;
+ }
+ catch (Exception e)
+ {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public void store(String baseName, RepositoryContentMetadata metadata)
+ {
+ File attachmentsStore = getMetadataPath(baseName);
+ File attachmentsParent = attachmentsStore.getParentFile();
+ if( attachmentsParent.exists() == false )
+ {
+ if( attachmentsParent.mkdirs() == false )
+ throw new RuntimeException("Failed to create attachmentsParent: "+attachmentsParent.getAbsolutePath());
+ }
+
+ if( metadata != null )
+ {
+ try
+ {
+ saveMetadata(attachmentsStore, metadata);
+ }
+ catch (RuntimeException e)
+ {
+ throw e;
+ }
+ catch(NotSerializableException e)
+ {
+ // Log what is in the attachments
+ StringBuilder tmp = new StringBuilder("Save failed with NSE, attachments contents: ");
+ tmp.append(metadata).append(" to: ").append(attachmentsStore);
+ log.error(tmp.toString());
+ throw new RuntimeException(e);
+ }
+ catch (Exception e)
+ {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ public abstract File getMetadataPath(String baseName);
+
+ protected abstract RepositoryContentMetadata loadMetadata(File attachmentsStore) throws Exception;
+
+ protected abstract void saveMetadata(File metadataStore, RepositoryContentMetadata metadata) throws Exception;
+
+ protected File getContentMetadataDir()
+ {
+ return contentMetadataDir;
+ }
+}
Property changes on: trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/local/AbstractContentMetadataPersister.java
___________________________________________________________________
Name: svn:keywords
+
Added: trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/local/AbstractLocalContentManager.java
===================================================================
--- trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/local/AbstractLocalContentManager.java (rev 0)
+++ trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/local/AbstractLocalContentManager.java 2009-03-31 22:10:16 UTC (rev 86553)
@@ -0,0 +1,656 @@
+/*
+ * 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.local;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.jboss.logging.Logger;
+import org.jboss.profileservice.spi.ProfileKey;
+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;
+import org.jboss.system.server.profileservice.repository.clustered.sync.ContentModification;
+import org.jboss.system.server.profileservice.repository.clustered.sync.NoOpSynchronizationAction;
+import org.jboss.system.server.profileservice.repository.clustered.sync.RemovalMetadataInsertionAction;
+import org.jboss.system.server.profileservice.repository.clustered.sync.StreamReadAction;
+import org.jboss.system.server.profileservice.repository.clustered.sync.SynchronizationAction;
+import org.jboss.system.server.profileservice.repository.clustered.sync.SynchronizationActionContext;
+import org.jboss.system.server.profileservice.repository.clustered.sync.SynchronizationId;
+import org.jboss.system.server.profileservice.repository.clustered.sync.TwoPhaseCommitAction;
+import org.jboss.virtual.VFS;
+import org.jboss.virtual.VirtualFile;
+
+/**
+ *
+ *
+ * @author Brian Stansberry
+ *
+ * @version $Revision: $
+ */
+public abstract class AbstractLocalContentManager<T extends SynchronizationActionContext> implements LocalContentManager<T>
+{
+ private final Logger log = Logger.getLogger(getClass());
+
+ private RepositoryContentMetadata officialContentMetadata;
+ private RepositoryContentMetadata currentContentMetadata;
+ private final Map<String, URI> namedURIMap;
+ private final Map<String, VirtualFile> vfCache = new ConcurrentHashMap<String, VirtualFile>();
+ private final ProfileKey profileKey;
+ private final String storeName;
+ private final String localNodeName;
+ private final ContentMetadataPersister contentMetadataPersister;
+ private List<TwoPhaseCommitAction<T>> currentSynchronizationActions;
+ private T currentSynchronizationActionContext;
+ private Map<RepositoryItemMetadata, InputStream> pendingStreams = new ConcurrentHashMap<RepositoryItemMetadata, InputStream>();
+
+
+ protected static String createStoreName(ProfileKey key)
+ {
+ // Normal case
+ if (ProfileKey.DEFAULT.equals(key.getDomain())
+ && ProfileKey.DEFAULT.equals(key.getServer()))
+ {
+ return key.getName();
+ }
+
+ StringBuilder sb = new StringBuilder();
+ if (ProfileKey.DEFAULT.equals(key.getDomain()) == false)
+ {
+ sb.append(key.getDomain());
+ sb.append('-');
+ }
+ if (ProfileKey.DEFAULT.equals(key.getServer()) == false)
+ {
+ sb.append(key.getServer());
+ sb.append('-');
+ }
+ sb.append(key.getName());
+ return sb.toString();
+ }
+
+ protected AbstractLocalContentManager(Map<String, URI> namedURIMap,
+ ProfileKey profileKey, String localNodeName,
+ ContentMetadataPersister contentMetadataPersister)
+ {
+ if (namedURIMap == null)
+ {
+ throw new IllegalArgumentException("Null namedURIMap");
+ }
+ if (profileKey == null)
+ {
+ throw new IllegalArgumentException("Null profileKey");
+ }
+ if (localNodeName == null)
+ {
+ throw new IllegalArgumentException("Null localNodeName");
+ }
+ if (contentMetadataPersister == null)
+ {
+ throw new IllegalArgumentException("Null contentMetadataPersister");
+ }
+
+ this.namedURIMap = namedURIMap;
+ this.profileKey = profileKey;
+ this.storeName = createStoreName(profileKey);
+ this.localNodeName = localNodeName;
+ this.contentMetadataPersister = contentMetadataPersister;
+ this.officialContentMetadata = this.contentMetadataPersister.load(this.storeName);
+ }
+
+ // ------------------------------------------------------------- Properties
+
+ public String getLocalNodeName()
+ {
+ return localNodeName;
+ }
+
+ public String getStoreName()
+ {
+ return storeName;
+ }
+
+ // ---------------------------------------------------- LocalContentManager
+
+ public RepositoryContentMetadata getOfficialContentMetadata()
+ {
+ return officialContentMetadata;
+ }
+
+ public RepositoryContentMetadata createEmptyContentMetadata()
+ {
+ RepositoryContentMetadata md = new RepositoryContentMetadata(profileKey);
+ List<RepositoryRootMetadata> roots = new ArrayList<RepositoryRootMetadata>();
+ for (Map.Entry<String, URI> entry : namedURIMap.entrySet())
+ {
+ RepositoryRootMetadata rmd = new RepositoryRootMetadata();
+ rmd.setName(entry.getKey());
+ roots.add(rmd);
+ }
+ md.setRepositories(roots);
+ return md;
+ }
+
+ public RepositoryContentMetadata getCurrentContentMetadata() throws IOException
+ {
+ synchronized (this)
+ {
+ if (this.currentContentMetadata == null)
+ {
+ RepositoryContentMetadata md = new RepositoryContentMetadata(profileKey);
+ List<RepositoryRootMetadata> roots = new ArrayList<RepositoryRootMetadata>();
+ RepositoryContentMetadata official = getOfficialContentMetadata();
+ for (Map.Entry<String, URI> entry : namedURIMap.entrySet())
+ {
+ RepositoryRootMetadata rmd = new RepositoryRootMetadata();
+ rmd.setName(entry.getKey());
+ RepositoryRootMetadata existingRmd = official == null ? null : official.getRepositoryRootMetadata(entry.getKey());
+
+ VirtualFile root = getCachedVirtualFile(entry.getValue());
+ if (isDirectory(root))
+ {
+ for(VirtualFile child: root.getChildren())
+ {
+ createItemMetadataFromScan(rmd, existingRmd, child, root);
+ }
+ }
+ else
+ {
+ // The root is itself an item. Treat it as a "child" of
+ // itself with no relative path
+ createItemMetadataFromScan(rmd, existingRmd, root, root);
+ }
+
+ roots.add(rmd);
+ }
+ md.setRepositories(roots);
+
+ // Retain any existing "removed item" metadata -- but only if
+ // it wasn't re-added!!
+ RepositoryContentMetadata existing = getOfficialContentMetadata();
+ if (existing != null)
+ {
+ for (RepositoryRootMetadata existingRoot : existing.getRepositories())
+ {
+ RepositoryRootMetadata rmd = md.getRepositoryRootMetadata(existingRoot.getName());
+ if (rmd != null)
+ {
+ for (RepositoryItemMetadata existingItem : existingRoot.getContent())
+ {
+ if (existingItem.isRemoved() // but check for re-add
+ && rmd.getItemMetadata(existingItem.getRelativePathElements()) == null)
+ {
+ rmd.addItemMetadata(new RepositoryItemMetadata(existingItem));
+ }
+ }
+ }
+ }
+ }
+
+ this.currentContentMetadata = md;
+ }
+ return this.currentContentMetadata;
+ }
+ }
+
+ public List<? extends SynchronizationAction<T>> initiateSynchronization(SynchronizationId<?> id,
+ List<ContentModification> modifications, RepositoryContentMetadata toInstall, boolean localLed)
+ {
+ if (id == null)
+ {
+ throw new IllegalArgumentException("Null id");
+ }
+ if (modifications == null)
+ {
+ throw new IllegalArgumentException("Null modifications");
+ }
+ if (toInstall == null)
+ {
+ throw new IllegalArgumentException("Null toInstall");
+ }
+
+ synchronized (this)
+ {
+ if (currentSynchronizationActionContext != null)
+ {
+ throw new IllegalStateException("Synchronization " + currentSynchronizationActionContext.getId() +
+ " is already in progress");
+ }
+
+ this.currentSynchronizationActionContext = createSynchronizationActionContext(id, toInstall);
+ }
+
+ List<TwoPhaseCommitAction<T>> actions = new ArrayList<TwoPhaseCommitAction<T>>();
+ for (ContentModification mod : modifications)
+ {
+ TwoPhaseCommitAction<T> action = null;
+ switch (mod.getType())
+ {
+ case PULL_FROM_CLUSTER:
+ action = createPullFromClusterAction(mod, localLed);
+ break;
+ case PUSH_TO_CLUSTER:
+ InputStream stream = pendingStreams.remove(mod.getItem());
+ if (stream == null)
+ {
+ action = createPushToClusterAction(mod, localLed);
+ }
+ else
+ {
+ action = createPushStreamToClusterAction(mod, stream);
+ }
+ break;
+ case REMOVE_FROM_CLUSTER:
+ action = createRemoveFromClusterAction(mod, localLed);
+ break;
+ case REMOVE_TO_CLUSTER:
+ action = createRemoveToClusterAction(mod, localLed);
+ break;
+ case PREPARE_RMDIR_FROM_CLUSTER:
+ action = createPrepareRmdirFromClusterAction(mod, localLed);
+ break;
+ case PREPARE_RMDIR_TO_CLUSTER:
+ action = createPrepareRmdirToClusterAction(mod, localLed);
+ break;
+ case DIR_TIMESTAMP_MISMATCH:
+ action = createDirectoryTimestampMismatchAction(mod, localLed);
+ break;
+ case MKDIR_FROM_CLUSTER:
+ action = createMkdirFromClusterAction(mod, localLed);
+ break;
+ case MKDIR_TO_CLUSTER:
+ action = createMkdirToClusterAction(mod, localLed);
+ break;
+ case REMOVAL_METADATA_FROM_CLUSTER:
+ action = createRemovalMetadataAction(mod, localLed);
+ break;
+ default:
+ throw new IllegalStateException("Unknown " + ContentModification.Type.class.getSimpleName() + " " + mod.getType());
+ }
+ actions.add(action);
+ }
+
+ this.currentSynchronizationActions = actions;
+ return Collections.unmodifiableList(actions);
+ }
+
+ public boolean prepareSynchronization(SynchronizationId<?> id)
+ {
+ validateSynchronization(id);
+ for (TwoPhaseCommitAction<T> action : this.currentSynchronizationActions)
+ {
+ if (action.prepare() == false)
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public void commitSynchronization(SynchronizationId<?> id)
+ {
+ validateSynchronization(id);
+ for (TwoPhaseCommitAction<T> action : this.currentSynchronizationActions)
+ {
+ action.commit();
+ }
+ updateContentMetadata(this.currentSynchronizationActionContext.getInProgressMetadata());
+ synchronized (this)
+ {
+ this.currentSynchronizationActions = null;
+ this.currentSynchronizationActionContext = null;
+ }
+ }
+
+ public void rollbackSynchronization(SynchronizationId<?> id)
+ {
+ validateSynchronization(id);
+
+ for (TwoPhaseCommitAction<T> action : this.currentSynchronizationActions)
+ {
+ action.rollback();
+ }
+ synchronized (this)
+ {
+ this.currentSynchronizationActionContext = null;
+ this.currentSynchronizationActions = null;
+ }
+ }
+
+ public void installCurrentContentMetadata()
+ {
+ synchronized (this)
+ {
+ if (this.currentContentMetadata == null)
+ {
+ throw new IllegalStateException("No currentContentMetadata");
+ }
+ if (this.currentSynchronizationActionContext != null)
+ {
+ throw new IllegalStateException("Cannot install currentContentMetadata; " +
+ "cluster synchronization " + this.currentSynchronizationActionContext.getId() +
+ " is in progress");
+ }
+
+ updateContentMetadata(this.currentContentMetadata);
+ }
+ }
+
+ public RepositoryItemMetadata getItemForAddition(String vfsPath) throws IOException
+ {
+ RepositoryItemMetadata item = new RepositoryItemMetadata();
+ item.setRelativePath(vfsPath);
+ List<String> pathElements = item.getRelativePathElements();
+ String rootName = null;
+ for (RepositoryRootMetadata rmd : getOfficialContentMetadata().getRepositories())
+ {
+ if (rmd.getItemMetadata(pathElements) != null)
+ {
+ // Exact match to existing item -- done
+ rootName = rmd.getName();
+ break;
+ }
+ else if (rootName == null)
+ {
+ // Use the first root that can accept children
+ URI rootURI = namedURIMap.get(rootName);
+ VirtualFile vf = getCachedVirtualFile(rootURI);
+ if (isDirectory(vf))
+ {
+ rootName = rmd.getName();
+ break;
+ }
+ }
+ }
+
+ if (rootName == null)
+ {
+ throw new IllegalStateException("No roots can accept children");
+ }
+
+ item.setRootName(rootName);
+ return item;
+ }
+
+ public RepositoryContentMetadata getContentMetadataForAdd(RepositoryItemMetadata toAdd, InputStream contentIS) throws IOException
+ {
+ RepositoryContentMetadata result = new RepositoryContentMetadata(getOfficialContentMetadata());
+ RepositoryRootMetadata rmd = result.getRepositoryRootMetadata(toAdd.getRootName());
+ if (rmd == null)
+ {
+ throw new IllegalArgumentException("Unknown root name " + toAdd.getRootName());
+ }
+ RepositoryItemMetadata remove = rmd.getItemMetadata(toAdd.getRelativePathElements());
+ if (remove.isDirectory())
+ {
+ for (RepositoryItemMetadata rim : rmd.getContent())
+ {
+ if (rim.isChildOf(remove))
+ {
+ rmd.removeItemMetadata(rim.getRelativePathElements());
+ }
+ }
+ }
+ rmd.addItemMetadata(toAdd);
+ pendingStreams.put(toAdd, contentIS);
+ return result;
+ }
+
+ public VirtualFile getVirtualFileForItem(RepositoryItemMetadata item) throws IOException
+ {
+ URI uri = namedURIMap.get(item.getRootName());
+ VirtualFile vf = getCachedVirtualFile(uri);
+ List<String> path = item.getRelativePathElements();
+ for (String element : path)
+ {
+ vf = vf.getChild(element);
+ if (vf == null)
+ {
+ throw new IllegalStateException("No child " + element + " under " + vf);
+ }
+ }
+ return vf;
+ }
+
+ public RepositoryContentMetadata getContentMetadataForRemove(VirtualFile vf) throws IOException
+ {
+ List<String> path = null;
+ RepositoryRootMetadata root = null;
+ RepositoryContentMetadata cmd = new RepositoryContentMetadata(getOfficialContentMetadata());
+ for (RepositoryRootMetadata rmd : cmd.getRepositories())
+ {
+ URI uri = namedURIMap.get(rmd.getName());
+ VirtualFile vfRoot = getCachedVirtualFile(uri);
+ try
+ {
+ path = getRelativePath(vf, vfRoot);
+ root = rmd;
+ break;
+ }
+ catch (IllegalStateException ise)
+ {
+ // vf wasn't a child; ignore and move on to next root
+ }
+ }
+
+ if (root == null)
+ {
+ throw new IllegalArgumentException(vf + " is not a child of any known roots");
+ }
+
+ RepositoryItemMetadata remove = root.getItemMetadata(path);
+ if (isDirectory(vf))
+ {
+ for (RepositoryItemMetadata rim : root.getContent())
+ {
+ if (rim.isChildOf(remove))
+ {
+ root.removeItemMetadata(rim.getRelativePathElements());
+ }
+ }
+
+ }
+ root.removeItemMetadata(path);
+ return cmd;
+ }
+
+
+ // -------------------------------------------------------------- Protected
+
+
+ protected abstract T createSynchronizationActionContext(SynchronizationId<?> id, RepositoryContentMetadata toUpdate);
+
+ protected abstract TwoPhaseCommitAction<T> createPullFromClusterAction(ContentModification mod, boolean localLed);
+
+ protected abstract TwoPhaseCommitAction<T> createPushToClusterAction(ContentModification mod, boolean localLed);
+
+ protected abstract TwoPhaseCommitAction<T> createRemoveFromClusterAction(ContentModification mod, boolean localLed);
+
+ protected abstract TwoPhaseCommitAction<T> createRemoveToClusterAction(ContentModification mod, boolean localLed);
+
+ protected abstract TwoPhaseCommitAction<T> createPrepareRmdirToClusterAction(ContentModification mod, boolean localLed);
+
+
+ protected abstract TwoPhaseCommitAction<T> createMkdirToClusterAction(ContentModification mod,
+ boolean localLed);
+
+ protected abstract TwoPhaseCommitAction<T> createMkdirFromClusterAction(
+ ContentModification mod, boolean localLed);
+
+ protected abstract TwoPhaseCommitAction<T> createDirectoryTimestampMismatchAction(
+ ContentModification mod, boolean localLed);
+
+ protected abstract TwoPhaseCommitAction<T> createPrepareRmdirFromClusterAction(ContentModification mod, boolean localLed);
+
+ protected T getSynchronizationActionContext()
+ {
+ return currentSynchronizationActionContext;
+ }
+
+ protected VirtualFile getCachedVirtualFile(URI uri) throws IOException
+ {
+ VirtualFile vf = this.vfCache.get(uri.toString());
+ if(vf == null)
+ {
+ vf = VFS.getRoot(uri);
+ this.vfCache.put(uri.toString(), vf);
+ }
+ return vf;
+ }
+
+ protected URI getRootURIForModification(ContentModification mod)
+ {
+ return namedURIMap.get(mod.getRootName());
+ }
+
+ public void clearCurrentContentMetadata()
+ {
+ this.currentContentMetadata = null;
+ }
+
+ // -------------------------------------------------------------- Private
+
+ private TwoPhaseCommitAction<T> createRemovalMetadataAction(ContentModification mod,
+ boolean localLed)
+ {
+ if (localLed)
+ {
+ return new RemovalMetadataInsertionAction<T>(getSynchronizationActionContext(), mod);
+ }
+ else
+ {
+ return new NoOpSynchronizationAction<T>(getSynchronizationActionContext(), mod);
+ }
+ }
+
+ private void updateContentMetadata(RepositoryContentMetadata newOfficial)
+ {
+ if (newOfficial.equals(this.officialContentMetadata) == false)
+ {
+ try
+ {
+ this.contentMetadataPersister.store(this.storeName, newOfficial);
+ }
+ catch (Exception e)
+ {
+ log.error("Caught exception persisting " + RepositoryContentMetadata.class.getSimpleName(), e);
+ }
+ this.officialContentMetadata = newOfficial;
+ }
+ this.currentContentMetadata = null;
+ }
+
+ private void createItemMetadataFromScan(RepositoryRootMetadata rmd,
+ RepositoryRootMetadata existingRMD,
+ VirtualFile file, VirtualFile root)
+ throws IOException
+ {
+ boolean directory = isDirectory(file);
+ long timestamp = file.getLastModified();
+
+ List<String> pathElements = getRelativePath(file, root);
+ RepositoryItemMetadata existing = existingRMD == null ? null : existingRMD.getItemMetadata(pathElements);
+
+ // If there's an existing item, assume for now it's unchanged and keep existing originator
+ String originator = existing == null ? this.localNodeName : existing.getOriginatingNode();
+ RepositoryItemMetadata md = new RepositoryItemMetadata(pathElements, timestamp, originator, directory, false);
+ if (md.equals(existing) == false)
+ {
+ // above if test failing means this is a new item or
+ // timestamp, removed or directory status has changed
+ // In any case, this node is now the originator
+ md.setOriginatingNode(this.localNodeName);
+ }
+
+ rmd.addItemMetadata(md);
+
+ if (directory)
+ {
+ for(VirtualFile child: file.getChildren())
+ {
+ createItemMetadataFromScan(rmd, existingRMD, child, root);
+ }
+ }
+ }
+
+ private TwoPhaseCommitAction<T> createPushStreamToClusterAction(ContentModification mod, InputStream stream)
+ {
+ return new StreamReadAction<T>(stream, getSynchronizationActionContext(), mod);
+ }
+
+ private void validateSynchronization(SynchronizationId<?> id)
+ {
+ if (id == null)
+ {
+ throw new IllegalArgumentException("Null id");
+ }
+
+ if (this.currentSynchronizationActionContext == null)
+ {
+ throw new IllegalStateException("No active synchronization");
+ }
+
+ SynchronizationId<?> ours = this.currentSynchronizationActionContext.getId();
+ if (id.equals(ours) == false)
+ {
+ throw new IllegalStateException(id + " does not match the current synchronization " + ours);
+ }
+ }
+
+ private static boolean isDirectory(VirtualFile file) throws IOException
+ {
+ return (!file.isLeaf() && !file.isArchive());
+ }
+
+ private static List<String> getRelativePath(VirtualFile file, VirtualFile root)
+ throws IOException
+ {
+ List<String> reversed = new ArrayList<String>();
+ VirtualFile now = file;
+ while(now != null && now.equals(root) == false)
+ {
+ reversed.add(now.getName());
+ now = now.getParent();
+ }
+
+ if (now == null)
+ {
+ throw new IllegalArgumentException(file + " is not a child of " + root);
+ }
+
+ List<String> forward = new ArrayList<String>(reversed.size());
+ for (int i = reversed.size() - 1; i > -1; i--)
+ {
+ forward.add(reversed.get(i));
+ }
+
+ return forward;
+ }
+
+}
Property changes on: trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/local/AbstractLocalContentManager.java
___________________________________________________________________
Name: svn:keywords
+
Added: trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/local/ContentMetadataPersister.java
===================================================================
--- trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/local/ContentMetadataPersister.java (rev 0)
+++ trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/local/ContentMetadataPersister.java 2009-03-31 22:10:16 UTC (rev 86553)
@@ -0,0 +1,40 @@
+/*
+ * 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.local;
+
+import org.jboss.system.server.profileservice.repository.clustered.metadata.RepositoryContentMetadata;
+
+/**
+ * Object responsible for maintaining a persistent copy of a node's
+ * {@link RepositoryContentMetadata}.
+ *
+ * @author Brian Stansberry
+ *
+ * @version $Revision: $
+ */
+public interface ContentMetadataPersister
+{
+ RepositoryContentMetadata load(String storeName);
+
+ void store(String storeName, RepositoryContentMetadata metadata);
+
+}
\ No newline at end of file
Property changes on: trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/local/ContentMetadataPersister.java
___________________________________________________________________
Name: svn:keywords
+
Added: trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/local/JAXBRepositoryContentMetadataPersister.java
===================================================================
--- trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/local/JAXBRepositoryContentMetadataPersister.java (rev 0)
+++ trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/local/JAXBRepositoryContentMetadataPersister.java 2009-03-31 22:10:16 UTC (rev 86553)
@@ -0,0 +1,103 @@
+/*
+ * 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.local;
+
+import java.io.File;
+import java.net.URI;
+
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.Marshaller;
+
+import org.jboss.bootstrap.spi.Server;
+import org.jboss.logging.Logger;
+import org.jboss.system.server.profileservice.repository.clustered.metadata.RepositoryContentMetadata;
+import org.jboss.xb.binding.Unmarshaller;
+import org.jboss.xb.binding.UnmarshallerFactory;
+import org.jboss.xb.binding.sunday.unmarshalling.DefaultSchemaResolver;
+
+/**
+ * RepositoryContentPersister that uses JAXB to store the content metadata as XML.
+ *
+ * @author Brian Stansberry
+ */
+public class JAXBRepositoryContentMetadataPersister extends AbstractContentMetadataPersister
+{
+ /** The logger */
+ private static final Logger log = Logger.getLogger(JAXBRepositoryContentMetadataPersister.class);
+
+ /** The attachment suffix. */
+ private static final String METADATA_SUFFIX = "-repository-contents.xml";
+
+ /** The default schema resolver. */
+ private static final DefaultSchemaResolver resolver = new DefaultSchemaResolver();
+
+ static
+ {
+ resolver.addClassBindingForLocation("repository-content", RepositoryContentMetadata.class);
+ }
+
+ public JAXBRepositoryContentMetadataPersister(Server server)
+ {
+ this(server.getConfig().getServerDataDir());
+ }
+
+ public JAXBRepositoryContentMetadataPersister(URI uri)
+ {
+ this(new File(uri));
+ }
+
+ public JAXBRepositoryContentMetadataPersister(File dir)
+ {
+ super(dir);
+ }
+
+ @Override
+ public File getMetadataPath(String baseName)
+ {
+ final String vfsPath = baseName + METADATA_SUFFIX;
+ return new File(getContentMetadataDir(), vfsPath);
+ }
+
+ @Override
+ protected RepositoryContentMetadata loadMetadata(File metadataStore) throws Exception
+ {
+ Unmarshaller unmarshaller = UnmarshallerFactory.newInstance().newUnmarshaller();
+ return (RepositoryContentMetadata) unmarshaller.unmarshal(metadataStore.toURL().openStream(), resolver);
+ }
+
+ @Override
+ protected void saveMetadata(File metadataStore, RepositoryContentMetadata metadata) throws Exception
+ {
+ if (log.isTraceEnabled())
+ {
+ log.trace("saveMetadata, metadataStore="+metadataStore+ ", metadata="+metadata);
+ }
+ JAXBContext ctx = JAXBContext.newInstance(metadata.getClass());
+ Marshaller marshaller = ctx.createMarshaller();
+ marshaller.setProperty("jaxb.formatted.output", Boolean.TRUE);
+ marshaller.marshal(metadata, metadataStore);
+ }
+
+
+
+}
Property changes on: trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/local/JAXBRepositoryContentMetadataPersister.java
___________________________________________________________________
Name: svn:keywords
+
Added: trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/local/LocalContentManager.java
===================================================================
--- trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/local/LocalContentManager.java (rev 0)
+++ trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/local/LocalContentManager.java 2009-03-31 22:10:16 UTC (rev 86553)
@@ -0,0 +1,171 @@
+/*
+ * 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.local;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.List;
+
+import org.jboss.system.server.profileservice.repository.clustered.ClusteredDeploymentRepository;
+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;
+import org.jboss.system.server.profileservice.repository.clustered.sync.AbstractContentModificationGenerator;
+import org.jboss.system.server.profileservice.repository.clustered.sync.ContentModification;
+import org.jboss.system.server.profileservice.repository.clustered.sync.SynchronizationAction;
+import org.jboss.system.server.profileservice.repository.clustered.sync.SynchronizationActionContext;
+import org.jboss.system.server.profileservice.repository.clustered.sync.SynchronizationId;
+import org.jboss.virtual.VirtualFile;
+
+/**
+ * Object responsible for the local persistence operations associated with a
+ * {@link ClusteredDeploymentRepository}.
+ *
+ * @author Brian Stansberry
+ *
+ * @version $Revision: $
+ */
+public interface LocalContentManager<T extends SynchronizationActionContext>
+{
+
+ /**
+ * Gets the "official" record of the contents of the persistent store.
+ * This is the version persisted to disk following synchronization
+ * of changes with the cluster.
+ *
+ * @return
+ */
+ RepositoryContentMetadata getOfficialContentMetadata();
+
+ /**
+ * Scan the persistent store for the current content metadata. This
+ * is not the "official" metadata that has been persisted, as any
+ * changes between it and the {@link #getOfficialContentMetadata() official version}
+ * have not been synchronized with the cluster.
+ */
+ RepositoryContentMetadata getCurrentContentMetadata() throws IOException;
+
+ /**
+ * Initiate a process of synchronizing this node's persistent store with
+ * the rest of the cluster
+ *
+ * @param id a unique id for this cluster synchronization process
+ * @param modifications the overall list of modifications that will occur during
+ * this process
+ * @param toInstall TODO
+ * @param localLed <code>true</code> if this node is driving the synchronization,
+ * <code>false</code> if another node is
+ * @return list of {@link SynchronizationAction} each of which
+ * can be executed by the caller to fulfill a portion of this node's role in
+ * the overall cluster synchronization
+ *
+ * @throws IllegalStateException if another synchronization process has
+ * been initiated and not yet completed
+ */
+ List<? extends SynchronizationAction<T>> initiateSynchronization(SynchronizationId<?> id,
+ List<ContentModification> modifications, RepositoryContentMetadata toInstall, boolean localLed);
+
+ /**
+ * Execute the prepare phase of the two phase commit process for the cluster
+ * synchronization that has been
+ * {@link #initiateSynchronization(SynchronizationId, List, RepsitoryContentMetadata, boolean) initialized}.
+ *
+ * @param id id of the synchronization. Cannot be <code>null</code>
+ *
+ * @throws IllegalStateException if <code>id</code> is not equal to the
+ * id of an uncompleted synchronization started via
+ * {@link #initiateSynchronization(SynchronizationId, List, RepsitoryContentMetadata, boolean)}
+ */
+ boolean prepareSynchronization(SynchronizationId<?> id);
+
+ /**
+ * Complete the two-phase commit process for the cluster synchronization that has been
+ * {@link #prepareSynchronization(SynchronizationId) prepared}.
+ *
+ * @param id id of the synchronization. Cannot be <code>null</code>
+ *
+ * @throws IllegalStateException if <code>id</code> is not equal to the
+ * id of an uncompleted synchronization started via
+ * {@link #initiateSynchronization(SynchronizationId, List, RepsitoryContentMetadata, boolean)}
+ */
+ void commitSynchronization(SynchronizationId<?> id);
+
+ /**
+ * Roll back the cluster synchronization.
+ *
+ * @param id id of the synchronization. Cannot be <code>null</code>
+ *
+ * @throws IllegalStateException if <code>id</code> is not equal to the
+ * id of an uncompleted synchronization started via
+ * {@link #initiateSynchronization(SynchronizationId, List, RepsitoryContentMetadata, boolean)}
+ */
+ void rollbackSynchronization(SynchronizationId<?> id);
+
+ /**
+ * Creates a new {@link RepositoryContentMetadata} with a child
+ * {@link RepositoryRootMetadata} for each of this persister's URIs,
+ * but no {@link RepositoryItemMetadata}s under those roots. When a node that
+ * is starting for the first time does not have a persisted set of
+ * content metadata, this method should be used to create an object that can
+ * be used as a base to
+ * {@link AbstractContentModificationGenerator#getModificationList(RepositoryContentMetadata, RepositoryContentMetadata) generate a set of modifications}
+ * needed to synchronize the node with the cluster.
+ *
+ * @return a {@link RepositoryContentMetadata} with no grandchildren.
+ */
+ RepositoryContentMetadata createEmptyContentMetadata();
+
+ /**
+ * Install the result from the latest call to {@link #getCurrentContentMetadata()}
+ * as the "official" content metadata. Intended for use during node startup
+ * when the node discovers it is the only member of the cluster, and thus
+ * that it's "current" content metadata is "official".
+ *
+ * @throws IllegalStateException if {@link #initiateSynchronization(SynchronizationId, List, RepsitoryContentMetadata, boolean)
+ * a cluster synchronization has been initiated} since the call to {@link #getCurrentContentMetadata()}
+ * but hasn't been committed or rolled back.
+ *
+ * @throws IllegalStateException if no "current" content metadata is available, either
+ * because {@link #getCurrentContentMetadata()}
+ * hasn't been called, or because a cluster
+ * synchronization has been executed to completion
+ * since that call.
+ *
+ */
+ void installCurrentContentMetadata();
+
+ RepositoryItemMetadata getItemForAddition(String vfsPath) throws IOException;
+ /**
+ * Generate content metadata that would reflect what the metadata would
+ * look like if an item with path vfsPath were added.
+ *
+ * @param vfsPath
+ * @param contentIS
+ * @return
+ */
+ RepositoryContentMetadata getContentMetadataForAdd(RepositoryItemMetadata toAdd, InputStream contentIS) throws IOException;
+
+ VirtualFile getVirtualFileForItem(RepositoryItemMetadata item) throws IOException;
+
+ RepositoryContentMetadata getContentMetadataForRemove(VirtualFile vf) throws IOException;
+}
Property changes on: trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/local/LocalContentManager.java
___________________________________________________________________
Name: svn:keywords
+
Added: trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/local/LocalContentManagerFactory.java
===================================================================
--- trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/local/LocalContentManagerFactory.java (rev 0)
+++ trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/local/LocalContentManagerFactory.java 2009-03-31 22:10:16 UTC (rev 86553)
@@ -0,0 +1,54 @@
+/*
+ * 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.local;
+
+import java.net.URI;
+import java.util.Collection;
+import java.util.Map;
+
+import org.jboss.profileservice.spi.ProfileKey;
+import org.jboss.system.server.profileservice.repository.clustered.sync.SynchronizationActionContext;
+
+/**
+ * Factory for a {@link LocalContentManager} that understands a
+ * particular type of URI.
+ *
+ * @author Brian Stansberry
+ *
+ * @version $Revision: $
+ */
+public interface LocalContentManagerFactory<T extends SynchronizationActionContext>
+{
+ /**
+ * Indicates whether this factory can create a {@link LocalContentManager}
+ * that works with the given collection of URIs.
+ *
+ * @param uris the URIs. Cannot be <code>null</code>
+ * @return <code>true</code> if a persister can be created, <code>false</code>
+ * otherwise
+ */
+ boolean accepts(Collection<URI> uris);
+
+ LocalContentManager<T> getRepositoryContentPersister(Map<String, URI> namedURIMap,
+ ProfileKey profileKey, String localNodeName);
+}
Property changes on: trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/local/LocalContentManagerFactory.java
___________________________________________________________________
Name: svn:keywords
+
Added: trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/local/file/AbstractLocalContentChangeAction.java
===================================================================
--- trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/local/file/AbstractLocalContentChangeAction.java (rev 0)
+++ trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/local/file/AbstractLocalContentChangeAction.java 2009-03-31 22:10:16 UTC (rev 86553)
@@ -0,0 +1,231 @@
+package org.jboss.system.server.profileservice.repository.clustered.local.file;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.jboss.logging.Logger;
+import org.jboss.system.server.profileservice.repository.clustered.sync.AbstractContentMetadataMutatorAction;
+import org.jboss.system.server.profileservice.repository.clustered.sync.ContentModification;
+
+public abstract class AbstractLocalContentChangeAction
+ extends AbstractContentMetadataMutatorAction<FileBasedSynchronizationActionContext>
+{
+ private final File targetFile;
+ private final boolean targetWasDir;
+ private final boolean targetExists;
+ private final long targetTimestamp;
+ private File tempRollback;
+
+ /**
+ * Create a new AbstractLocalContentChangeAction.
+ *
+ * @param targetFile the file whose content is to be changed
+ * @param context the overall context of the modification
+ * @param modification the modification
+ */
+ protected AbstractLocalContentChangeAction(File targetFile, FileBasedSynchronizationActionContext context,
+ ContentModification modification)
+ {
+ super(context, modification);
+
+ if (targetFile == null)
+ {
+ throw new IllegalArgumentException("Null targetFile");
+ }
+
+ this.targetFile = targetFile;
+ this.targetWasDir = targetFile.isDirectory();
+ this.targetExists = targetFile.exists();
+ this.targetTimestamp = targetFile.lastModified();
+ }
+
+ protected abstract Logger getLogger();
+
+ protected abstract boolean modifyTarget() throws IOException;
+
+ protected File getTargetFile()
+ {
+ return targetFile;
+ }
+
+ @Override
+ protected void doCancel()
+ {
+ safeCleanup();
+ }
+
+ @Override
+ protected void doComplete() throws Exception
+ {
+ // no-op
+ }
+
+ @Override
+ protected boolean doPrepare()
+ {
+ File backup = null;
+ try
+ {
+ if (targetExists)
+ {
+ if (!targetWasDir)
+ {
+ // Make a backup copy of target in case of rollback
+ backup = createTempFile();
+ FileUtil.localMove(targetFile, backup, targetTimestamp);
+ // assign after creation so the ref to the file
+ // indicates a successful write -- useful in rollback
+ this.tempRollback = backup;
+ }
+ else
+ {
+ // No backup copy needed; we can just recreate
+ targetFile.delete();
+ }
+ }
+
+ return modifyTarget();
+ }
+ catch (Exception e)
+ {
+ getLogger().error("Caught exception in doPrepare() ", e);
+ if (backup != null && tempRollback == null)
+ {
+ // We failed during backup creation.
+ // Discard unneeded backup copy
+ backup.delete();
+ }
+ }
+ return false;
+ }
+
+ @Override
+ protected void doRollbackFromCancelled()
+ {
+ // no-op
+ }
+
+ @Override
+ protected void doRollbackFromComplete()
+ {
+ safeCleanup();
+ }
+
+ @Override
+ protected void doRollbackFromOpen()
+ {
+ safeCleanup();
+ }
+
+ @Override
+ protected void doRollbackFromPrepared()
+ {
+ boolean cleanRollback = true;
+
+ rollbackContentMetadata();
+
+ if (targetWasDir)
+ {
+ targetFile.delete();
+ targetFile.mkdirs();
+ targetFile.setLastModified(targetTimestamp);
+ }
+ else if (targetExists)
+ {
+ // Restore it
+ try
+ {
+ FileUtil.localMove(tempRollback, targetFile, this.targetTimestamp);
+ }
+ catch (IOException e)
+ {
+ getLogger().error("Failed restoring " + targetFile + " during rollback. " +
+ "Backup copy is stored in " + tempRollback, e);
+ // Don't discard the rollback file
+ cleanRollback = false;
+ }
+ }
+ else
+ {
+ targetFile.delete();
+ }
+
+ safeCleanup(cleanRollback);
+ }
+
+ @Override
+ protected void doRollbackFromRollbackOnly()
+ {
+ boolean cleanRollback = true;
+ // We get here either from complete or from prepare. We
+ // can tell which by whether tempRollback exists
+ if (tempRollback != null && tempRollback.exists())
+ {
+ // Restore it
+ try
+ {
+ FileUtil.localMove(tempRollback, targetFile, targetTimestamp);
+ }
+ catch (IOException e)
+ {
+ getLogger().error("Failed restoring " + targetFile + " during rollback. " +
+ "Backup copy is stored in " + tempRollback, e);
+ // Don't discard the rollback file
+ cleanRollback = false;
+ }
+ }
+ else if (targetWasDir)
+ {
+ if (targetFile.exists())
+ {
+ if (targetFile.isDirectory() == false)
+ {
+ targetFile.delete();
+ targetFile.mkdirs();
+ targetFile.setLastModified(targetTimestamp);
+ }
+ }
+ else
+ {
+ targetFile.mkdirs();
+ targetFile.setLastModified(targetTimestamp);
+ }
+ }
+ else if (targetExists == false)
+ {
+ // if we created one, get rid of it
+ targetFile.delete();
+ }
+
+ safeCleanup(cleanRollback);
+ }
+
+ @Override
+ protected void doCommit()
+ {
+ updateContentMetadata();
+ safeCleanup();
+ }
+
+ protected File createTempFile() throws IOException
+ {
+ FileBasedSynchronizationActionContext ctx = getContext();
+ File f = FileUtil.createTempFile(ctx.getTempDir(), ctx.getStoreName());
+ f.deleteOnExit();
+ return f;
+ }
+
+ protected synchronized void safeCleanup(boolean cleanRollback)
+ {
+ if (cleanRollback && tempRollback != null)
+ {
+ tempRollback.delete();
+ }
+ }
+
+ protected void safeCleanup()
+ {
+ safeCleanup(true);
+ }
+
+}
\ No newline at end of file
Property changes on: trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/local/file/AbstractLocalContentChangeAction.java
___________________________________________________________________
Name: svn:keywords
+
Added: trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/local/file/DirectoryTimestampUpdateAction.java
===================================================================
--- trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/local/file/DirectoryTimestampUpdateAction.java (rev 0)
+++ trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/local/file/DirectoryTimestampUpdateAction.java 2009-03-31 22:10:16 UTC (rev 86553)
@@ -0,0 +1,126 @@
+/*
+ * 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.local.file;
+
+import java.io.File;
+
+import org.jboss.system.server.profileservice.repository.clustered.sync.AbstractContentMetadataMutatorAction;
+import org.jboss.system.server.profileservice.repository.clustered.sync.ContentModification;
+
+/**
+ * RepositorySynchronizationAction that updates a directory lastModified time.
+ *
+ * @author Brian Stansberry
+ *
+ * @version $Revision: $
+ */
+public class DirectoryTimestampUpdateAction extends AbstractContentMetadataMutatorAction<FileBasedSynchronizationActionContext>
+{
+ private final File targetDir;
+ private final long rollbackTimestamp;
+
+ /**
+ * Create a new DirectoryTimestampUpdateAction.
+ *
+ * @param targetFile the directory whose timestamp is to be changed
+ * @param context the overall context of the modification
+ * @param modification the modification
+ */
+ public DirectoryTimestampUpdateAction(File targetDir,
+ FileBasedSynchronizationActionContext context,
+ ContentModification modification)
+ {
+ super(context, modification);
+
+ if (targetDir == null)
+ {
+ throw new IllegalArgumentException("Null targetDir");
+ }
+ if (targetDir.exists() == false)
+ {
+ throw new IllegalArgumentException(targetDir + " does not exist");
+ }
+ if (targetDir.isDirectory() == false)
+ {
+ throw new IllegalArgumentException(targetDir + " is not a directory");
+ }
+ this.targetDir = targetDir;
+ this.rollbackTimestamp = targetDir.lastModified();
+ }
+
+ @Override
+ protected void doCancel()
+ {
+ // no-op
+ }
+
+ @Override
+ protected void doCommit()
+ {
+ updateContentMetadata();
+ }
+
+ @Override
+ protected void doComplete() throws Exception
+ {
+ // no-op
+ }
+
+ @Override
+ protected boolean doPrepare()
+ {
+ targetDir.setLastModified(getRepositoryContentModification().getItem().getTimestamp());
+ return true;
+ }
+
+ @Override
+ protected void doRollbackFromCancelled()
+ {
+ // no-op
+ }
+
+ @Override
+ protected void doRollbackFromComplete()
+ {
+ // no-op
+ }
+
+ @Override
+ protected void doRollbackFromOpen()
+ {
+ // no-op
+ }
+
+ @Override
+ protected void doRollbackFromPrepared()
+ {
+ targetDir.setLastModified(rollbackTimestamp);
+ }
+
+ @Override
+ protected void doRollbackFromRollbackOnly()
+ {
+ targetDir.setLastModified(rollbackTimestamp);
+ }
+
+}
Property changes on: trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/local/file/DirectoryTimestampUpdateAction.java
___________________________________________________________________
Name: svn:keywords
+
Added: trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/local/file/FileBasedSynchronizationActionContext.java
===================================================================
--- trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/local/file/FileBasedSynchronizationActionContext.java (rev 0)
+++ trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/local/file/FileBasedSynchronizationActionContext.java 2009-03-31 22:10:16 UTC (rev 86553)
@@ -0,0 +1,67 @@
+/*
+ * 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.local.file;
+
+import java.io.File;
+
+import org.jboss.system.server.profileservice.repository.clustered.metadata.RepositoryContentMetadata;
+import org.jboss.system.server.profileservice.repository.clustered.sync.SynchronizationActionContext;
+import org.jboss.system.server.profileservice.repository.clustered.sync.SynchronizationId;
+
+/**
+ * {@link SynchronizationActionContext} subclass that provides additional
+ * contextual information useful to filesystem based actions.
+ *
+ * @author Brian Stansberry
+ *
+ * @version $Revision: $
+ */
+public class FileBasedSynchronizationActionContext extends SynchronizationActionContext
+{
+ private final File tempDir;
+ private final String storeName;
+
+ public FileBasedSynchronizationActionContext(SynchronizationId<?> id,
+ RepositoryContentMetadata inProgressMetadata, File tempDir, String storeName)
+ {
+ super(id, inProgressMetadata);
+ if (storeName == null)
+ {
+ throw new IllegalArgumentException("Null storeName");
+ }
+ this.storeName = storeName;
+ this.tempDir = tempDir;
+ }
+
+ public File getTempDir()
+ {
+ return tempDir;
+ }
+
+ public String getStoreName()
+ {
+ return storeName;
+ }
+
+
+}
Property changes on: trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/local/file/FileBasedSynchronizationActionContext.java
___________________________________________________________________
Name: svn:keywords
+
Added: trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/local/file/FileReadAction.java
===================================================================
--- trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/local/file/FileReadAction.java (rev 0)
+++ trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/local/file/FileReadAction.java 2009-03-31 22:10:16 UTC (rev 86553)
@@ -0,0 +1,189 @@
+/*
+ * 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.local.file;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.jboss.logging.Logger;
+import org.jboss.system.server.profileservice.repository.clustered.sync.AbstractSynchronizationAction;
+import org.jboss.system.server.profileservice.repository.clustered.sync.ByteChunk;
+import org.jboss.system.server.profileservice.repository.clustered.sync.ContentModification;
+import org.jboss.system.server.profileservice.repository.clustered.sync.SynchronizationReadAction;
+
+/**
+ * {@link SynchronizationReadAction} that reads from a {@link File}.
+ *
+ * @author Brian Stansberry
+ *
+ * @version $Revision: $
+ */
+public class FileReadAction extends AbstractSynchronizationAction<FileBasedSynchronizationActionContext>
+ implements SynchronizationReadAction<FileBasedSynchronizationActionContext>
+{
+ private static final Logger log = Logger.getLogger(FileReadAction.class);
+
+ /**
+ * Max file transfer buffer size that we read at a time.
+ * This influences the number of times that we will invoke disk read/write file
+ * operations versus how much memory we will consume for a file transfer.
+ */
+ public static final int MAX_CHUNK_BUFFER_SIZE = 512 * 1024;
+
+ private final File file;
+ private InputStream stream;
+
+ /**
+ * Create a new FileReadAction.
+ *
+ * @param file the file to read
+ * @param context the overall context of the modification
+ * @param modification the modification
+ */
+ public FileReadAction(File file, FileBasedSynchronizationActionContext context,
+ ContentModification modification)
+ {
+ super(context, modification);
+ if (file == null)
+ {
+ throw new IllegalArgumentException("Null file");
+ }
+ this.file = file;
+ }
+
+ // ------------------------------------ RepositorySynchronizationReadAction
+
+ public ByteChunk getNextBytes() throws IOException
+ {
+ InputStream is = getInputStream();
+ byte[] b = null;
+ int read = -1;
+ synchronized (is)
+ {
+ b = new byte[MAX_CHUNK_BUFFER_SIZE];
+ read = is.read(b);
+ }
+ return new ByteChunk(b, read);
+ }
+
+ // -------------------------------------------------------------- Protected
+
+ @Override
+ protected void doCancel()
+ {
+ safeCloseStream();
+ }
+
+ @Override
+ protected void doCommit()
+ {
+ safeCloseStream();
+ }
+
+ @Override
+ protected void doComplete() throws Exception
+ {
+ safeCloseStream();
+ }
+
+ @Override
+ protected boolean doPrepare()
+ {
+ safeCloseStream();
+ return true;
+ }
+
+ @Override
+ protected void doRollbackFromCancelled()
+ {
+ safeCloseStream();
+ }
+
+ @Override
+ protected void doRollbackFromComplete()
+ {
+ safeCloseStream();
+ }
+
+ @Override
+ protected void doRollbackFromOpen()
+ {
+ safeCloseStream();
+ }
+
+ @Override
+ protected void doRollbackFromPrepared()
+ {
+ safeCloseStream();
+ }
+
+ @Override
+ protected void doRollbackFromRollbackOnly()
+ {
+ safeCloseStream();
+ }
+
+ private synchronized InputStream getInputStream() throws IOException
+ {
+ State s = getState();
+ if (s != State.OPEN && s != State.CANCELLED)
+ {
+ throw new IllegalStateException("Cannot read when state is " + s);
+ }
+
+ if (stream == null)
+ {
+ FileInputStream fis = new FileInputStream(file);
+ stream = new BufferedInputStream(fis);
+ }
+ return stream;
+ }
+
+ private synchronized void safeCloseStream()
+ {
+ if (stream != null)
+ {
+ synchronized (stream)
+ {
+ try
+ {
+ stream.close();
+ }
+ catch (IOException e)
+ {
+ ContentModification mod = getRepositoryContentModification();
+ log.debug("Caught exception closing stream for " + mod.getRootName() +
+ " " + mod.getItem().getRelativePath(), e);
+ }
+ finally
+ {
+ stream = null;
+ }
+ }
+ }
+ }
+
+}
Property changes on: trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/local/file/FileReadAction.java
___________________________________________________________________
Name: svn:keywords
+
Added: trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/local/file/FileUtil.java
===================================================================
--- trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/local/file/FileUtil.java (rev 0)
+++ trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/local/file/FileUtil.java 2009-03-31 22:10:16 UTC (rev 86553)
@@ -0,0 +1,136 @@
+/*
+ * 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.local.file;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URI;
+
+import org.jboss.logging.Logger;
+import org.jboss.system.server.profileservice.repository.clustered.metadata.RepositoryItemMetadata;
+
+/**
+ * Utility methods related to filesystem operations.
+ *
+ * @author Brian Stansberry
+ *
+ * @version $Revision: $
+ */
+public class FileUtil
+{
+ private static final Logger log = Logger.getLogger(FileUtil.class);
+
+ public static void localMove(File source, File destination, long modifiedTime) throws IOException
+ {
+ // if we can simply rename the file, all the better
+ if(!source.renameTo(destination))
+ {
+ // otherwise, copy source to destination
+ OutputStream out = new FileOutputStream(destination);
+ InputStream in = new FileInputStream(source);
+ byte buffer[] = new byte[32*1024];
+ int bytesRead = 0;
+ while(bytesRead > -1) // until we hit end of source file
+ {
+ bytesRead = in.read(buffer);
+ if(bytesRead > 0)
+ {
+ out.write(buffer,0, bytesRead);
+ }
+ }
+ in.close();
+ out.close();
+ }
+
+ destination.setLastModified(modifiedTime);
+ }
+
+ public static File getFileForItem(URI rootURI, RepositoryItemMetadata item)
+ {
+ File f = new File(rootURI);
+ for (String element : item.getRelativePathElements())
+ {
+ f = new File(f, element);
+ }
+ return f;
+ }
+
+ public static File createTempFile(String tmpDirName, String partitionName) throws IOException
+ {
+ if (tmpDirName == null)
+ {
+ return File.createTempFile(partitionName, "tmp");
+ }
+ else
+ {
+ return createTempFile(createTempDir(tmpDirName), partitionName);
+ }
+ }
+
+ public static File createTempFile(File tmpDir, String partitionName) throws IOException
+ {
+ if (tmpDir.exists() == false)
+ {
+ tmpDir.mkdirs();
+ }
+ return File.createTempFile(partitionName, "tmp", tmpDir);
+ }
+
+ public static File createTempDir(String tmpDirName) throws IOException
+ {
+ File dir = new File(tmpDirName);
+ if (! dir.exists())
+ {
+ dir.mkdirs();
+ }
+ else if (! dir.isDirectory())
+ {
+ throw new IllegalStateException(dir + " already exists and is not a directory");
+ }
+ return dir;
+ }
+
+ public static void safeCloseStream(OutputStream os, Object id)
+ {
+ try
+ {
+ os.close();
+ }
+ catch (IOException e)
+ {
+ log.trace("Failed to close temporary output stream for " + id, e);
+ }
+ }
+
+ /**
+ * Prevent instantiation.
+ */
+ private FileUtil()
+ {
+ }
+
+}
Property changes on: trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/local/file/FileUtil.java
___________________________________________________________________
Name: svn:keywords
+
Added: trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/local/file/FileWriteAction.java
===================================================================
--- trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/local/file/FileWriteAction.java (rev 0)
+++ trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/local/file/FileWriteAction.java 2009-03-31 22:10:16 UTC (rev 86553)
@@ -0,0 +1,167 @@
+/*
+ * 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.local.file;
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.jboss.logging.Logger;
+import org.jboss.system.server.profileservice.repository.clustered.sync.ByteChunk;
+import org.jboss.system.server.profileservice.repository.clustered.sync.ContentModification;
+import org.jboss.system.server.profileservice.repository.clustered.sync.SynchronizationReadAction;
+import org.jboss.system.server.profileservice.repository.clustered.sync.SynchronizationWriteAction;
+
+/**
+ * {@link SynchronizationReadAction} that writes to a {@link File}.
+ *
+ * @author Brian Stansberry
+ *
+ * @version $Revision: $
+ */
+public class FileWriteAction extends AbstractLocalContentChangeAction
+ implements SynchronizationWriteAction<FileBasedSynchronizationActionContext>
+{
+ private static final Logger log = Logger.getLogger(FileWriteAction.class);
+
+ private File tempFile;
+ private OutputStream stream;
+
+ /**
+ * Create a new FileWriteAction.
+ *
+ * @param targetFile the file to write to
+ * @param context the overall context of the modification
+ * @param modification the modification
+ */
+ public FileWriteAction(File targetFile, FileBasedSynchronizationActionContext context,
+ ContentModification modification)
+ {
+ super(targetFile, context, modification);
+ }
+
+ // ----------------------------------- RepositorySynchronizationWriteAction
+
+ public void writeBytes(ByteChunk bytes) throws IOException
+ {
+ if (bytes == null)
+ {
+ throw new IllegalArgumentException("Null bytes");
+ }
+ if (bytes.getByteCount() < 0)
+ {
+ throw new IllegalArgumentException("Illegal byte count " + bytes.getByteCount());
+ }
+ OutputStream os = getOutputStream();
+ os.write(bytes.getBytes(), 0, bytes.getByteCount());
+ }
+
+ // -------------------------------------------------------------- Protected
+
+
+ @Override
+ protected void doComplete() throws Exception
+ {
+ // Done writing
+ safeCloseStream();
+ super.doComplete();
+ }
+
+ @Override
+ protected boolean modifyTarget() throws IOException
+ {
+ // Our temp file replaces targetFile
+ FileUtil.localMove(tempFile, getTargetFile(), getRepositoryContentModification().getItem().getTimestamp());
+ return true;
+ }
+
+ @Override
+ protected Logger getLogger()
+ {
+ return log;
+ }
+
+ protected synchronized void safeCleanup(boolean cleanRollback)
+ {
+ super.safeCleanup(cleanRollback);
+ safeCloseStream();
+ if (tempFile != null)
+ {
+ tempFile.delete();
+ }
+ }
+
+ // ---------------------------------------------------------------- Private
+
+ private synchronized OutputStream getOutputStream() throws IOException
+ {
+ State s = getState();
+ if (s != State.OPEN && s != State.CANCELLED)
+ {
+ throw new IllegalStateException("Cannot write when state is " + s);
+ }
+
+ if (stream == null)
+ {
+ FileOutputStream fos = new FileOutputStream(getTempFile());
+ stream = new BufferedOutputStream(fos);
+ }
+ return stream;
+ }
+
+ private File getTempFile() throws IOException
+ {
+ if (tempFile == null)
+ {
+ tempFile = createTempFile();
+ }
+ return tempFile;
+ }
+
+ private synchronized void safeCloseStream()
+ {
+ if (stream != null)
+ {
+ synchronized (stream)
+ {
+ try
+ {
+ stream.close();
+ }
+ catch (IOException e)
+ {
+ ContentModification mod = getRepositoryContentModification();
+ log.debug("Caught exception closing stream for " + mod.getRootName() +
+ " " + mod.getItem().getRelativePath(), e);
+ }
+ finally
+ {
+ stream = null;
+ }
+ }
+ }
+ }
+
+}
Property changes on: trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/local/file/FileWriteAction.java
___________________________________________________________________
Name: svn:keywords
+
Added: trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/local/file/FilesystemLocalContentManager.java
===================================================================
--- trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/local/file/FilesystemLocalContentManager.java (rev 0)
+++ trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/local/file/FilesystemLocalContentManager.java 2009-03-31 22:10:16 UTC (rev 86553)
@@ -0,0 +1,229 @@
+/*
+ * 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.local.file;
+
+import java.io.File;
+import java.net.URI;
+import java.util.Map;
+
+import org.jboss.profileservice.spi.ProfileKey;
+import org.jboss.system.server.profileservice.repository.clustered.local.AbstractLocalContentManager;
+import org.jboss.system.server.profileservice.repository.clustered.local.ContentMetadataPersister;
+import org.jboss.system.server.profileservice.repository.clustered.local.LocalContentManager;
+import org.jboss.system.server.profileservice.repository.clustered.metadata.RepositoryContentMetadata;
+import org.jboss.system.server.profileservice.repository.clustered.sync.ContentModification;
+import org.jboss.system.server.profileservice.repository.clustered.sync.NoOpSynchronizationAction;
+import org.jboss.system.server.profileservice.repository.clustered.sync.RemoteRemovalAction;
+import org.jboss.system.server.profileservice.repository.clustered.sync.SimpleSynchronizationRemoteAction;
+import org.jboss.system.server.profileservice.repository.clustered.sync.SynchronizationId;
+import org.jboss.system.server.profileservice.repository.clustered.sync.TwoPhaseCommitAction;
+
+/**
+ * {@link LocalContentManager} that persists to the local filesystem.
+ *
+ * @author Brian Stansberry
+ *
+ * @version $Revision: $
+ */
+public class FilesystemLocalContentManager extends AbstractLocalContentManager<FileBasedSynchronizationActionContext>
+{
+ private final File tmpDir;
+
+ /**
+ * Create a new FilesystemRepositoryContentPersister.
+ *
+ * @param namedURIMap
+ * @param storeName
+ * @param localNodeName
+ * @param contentMetadataPersister
+ */
+ protected FilesystemLocalContentManager(Map<String, URI> namedURIMap, ProfileKey profileKey, String localNodeName,
+ ContentMetadataPersister contentMetadataPersister, URI tempDirURI)
+ {
+ super(namedURIMap, profileKey, localNodeName, contentMetadataPersister);
+
+ if (tempDirURI != null)
+ {
+ this.tmpDir = new File(tempDirURI);
+ }
+ else
+ {
+ this.tmpDir = null;
+ }
+ }
+
+ // -------------------------------------------------------------- Protected
+
+ @Override
+ protected FileBasedSynchronizationActionContext createSynchronizationActionContext(
+ SynchronizationId<?> id, RepositoryContentMetadata toUpdate)
+ {
+ return new FileBasedSynchronizationActionContext(id, toUpdate, tmpDir, getStoreName());
+ }
+
+ @Override
+ protected TwoPhaseCommitAction<FileBasedSynchronizationActionContext> createPullFromClusterAction(
+ ContentModification mod, boolean localLed)
+ {
+ File targetFile = FileUtil.getFileForItem(getRootURIForModification(mod), mod.getItem());
+ if (localLed)
+ {
+ return new FileWriteAction(targetFile, getSynchronizationActionContext(), mod);
+ }
+ else
+ {
+ return new FileReadAction(targetFile, getSynchronizationActionContext(), mod);
+ }
+ }
+
+ @Override
+ protected TwoPhaseCommitAction<FileBasedSynchronizationActionContext> createPushToClusterAction(ContentModification mod,
+ boolean localLed)
+ {
+ File targetFile = FileUtil.getFileForItem(getRootURIForModification(mod), mod.getItem());
+ if (localLed)
+ {
+ return new FileReadAction(targetFile, getSynchronizationActionContext(), mod);
+ }
+ else
+ {
+ return new FileWriteAction(targetFile, getSynchronizationActionContext(),
+ mod);
+ }
+ }
+
+ @Override
+ protected TwoPhaseCommitAction<FileBasedSynchronizationActionContext> createRemoveFromClusterAction(
+ ContentModification mod, boolean localLed)
+ {
+ if (localLed)
+ {
+ File targetFile = FileUtil.getFileForItem(getRootURIForModification(mod), mod.getItem());
+ return new RemoveFileAction(targetFile, getSynchronizationActionContext(),
+ mod);
+ }
+ else
+ {
+ // nothing to do on a remote node
+ return new NoOpSynchronizationAction<FileBasedSynchronizationActionContext>(getSynchronizationActionContext(), mod);
+ }
+ }
+
+ @Override
+ protected TwoPhaseCommitAction<FileBasedSynchronizationActionContext> createRemoveToClusterAction(
+ ContentModification mod, boolean localLed)
+ {
+ if (localLed)
+ {
+ return new RemoteRemovalAction<FileBasedSynchronizationActionContext>(getSynchronizationActionContext(), mod);
+ }
+ else
+ {
+ File targetFile = FileUtil.getFileForItem(getRootURIForModification(mod), mod.getItem());
+ return new RemoveFileAction(targetFile, getSynchronizationActionContext(),
+ mod);
+ }
+ }
+
+ @Override
+ protected TwoPhaseCommitAction<FileBasedSynchronizationActionContext> createDirectoryTimestampMismatchAction(ContentModification mod,
+ boolean localLed)
+ {
+ if (localLed)
+ {
+ File targetFile = FileUtil.getFileForItem(getRootURIForModification(mod), mod.getItem());
+ return new DirectoryTimestampUpdateAction(targetFile, getSynchronizationActionContext(),
+ mod);
+ }
+ else
+ {
+ // nothing to do on a remote node
+ return new NoOpSynchronizationAction<FileBasedSynchronizationActionContext>(getSynchronizationActionContext(), mod);
+ }
+ }
+
+ @Override
+ protected TwoPhaseCommitAction<FileBasedSynchronizationActionContext> createMkdirFromClusterAction(ContentModification mod, boolean localLed)
+ {
+ if (localLed)
+ {
+ File targetFile = FileUtil.getFileForItem(getRootURIForModification(mod), mod.getItem());
+ return new MkDirAction(targetFile, getSynchronizationActionContext(),
+ mod);
+ }
+ else
+ {
+ // nothing to do on a remote node
+ return new NoOpSynchronizationAction<FileBasedSynchronizationActionContext>(getSynchronizationActionContext(), mod);
+ }
+ }
+
+ @Override
+ protected TwoPhaseCommitAction<FileBasedSynchronizationActionContext> createMkdirToClusterAction(ContentModification mod, boolean localLed)
+ {
+ if (localLed)
+ {
+ return new SimpleSynchronizationRemoteAction<FileBasedSynchronizationActionContext>(getSynchronizationActionContext(), mod);
+ }
+ else
+ {
+ File targetFile = FileUtil.getFileForItem(getRootURIForModification(mod), mod.getItem());
+ return new MkDirAction(targetFile, getSynchronizationActionContext(),
+ mod);
+ }
+ }
+
+ @Override
+ protected TwoPhaseCommitAction<FileBasedSynchronizationActionContext> createPrepareRmdirFromClusterAction(ContentModification mod, boolean localLed)
+ {
+ if (localLed)
+ {
+ File targetFile = FileUtil.getFileForItem(getRootURIForModification(mod), mod.getItem());
+ return new InitiateRmdirAction(targetFile, getSynchronizationActionContext(),
+ mod);
+ }
+ else
+ {
+ // nothing to do on a remote node
+ return new NoOpSynchronizationAction<FileBasedSynchronizationActionContext>(getSynchronizationActionContext(), mod);
+ }
+ }
+
+ @Override
+ protected TwoPhaseCommitAction<FileBasedSynchronizationActionContext> createPrepareRmdirToClusterAction(ContentModification mod, boolean localLed)
+ {
+ if (localLed)
+ {
+ boolean initiation = true;
+ return new SimpleSynchronizationRemoteAction<FileBasedSynchronizationActionContext>(getSynchronizationActionContext(), mod, initiation);
+ }
+ else
+ {
+ File targetFile = FileUtil.getFileForItem(getRootURIForModification(mod), mod.getItem());
+ return new InitiateRmdirAction(targetFile, getSynchronizationActionContext(),
+ mod);
+ }
+ }
+
+
+}
Property changes on: trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/local/file/FilesystemLocalContentManager.java
___________________________________________________________________
Name: svn:keywords
+
Added: trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/local/file/FilesystemLocalContentManagerFactory.java
===================================================================
--- trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/local/file/FilesystemLocalContentManagerFactory.java (rev 0)
+++ trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/local/file/FilesystemLocalContentManagerFactory.java 2009-03-31 22:10:16 UTC (rev 86553)
@@ -0,0 +1,124 @@
+/*
+ * 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.local.file;
+
+import java.io.File;
+import java.net.URI;
+import java.util.Collection;
+import java.util.Map;
+
+import org.jboss.profileservice.spi.ProfileKey;
+import org.jboss.system.server.profileservice.repository.clustered.local.ContentMetadataPersister;
+import org.jboss.system.server.profileservice.repository.clustered.local.LocalContentManager;
+import org.jboss.system.server.profileservice.repository.clustered.local.LocalContentManagerFactory;
+
+/**
+ * {@link LocalContentManagerFactory} that creates a
+ * {@link FilesystemLocalContentManager}.
+ *
+ * @author Brian Stansberry
+ *
+ * @version $Revision: $
+ */
+public class FilesystemLocalContentManagerFactory implements LocalContentManagerFactory<FileBasedSynchronizationActionContext>
+{
+ private ContentMetadataPersister metadataPersister;
+ private URI tempDirURI;
+
+ // ------------------------------------------------------------- Properties
+
+ public ContentMetadataPersister getMetadataPersister()
+ {
+ return metadataPersister;
+ }
+
+ public void setMetadataPersister(ContentMetadataPersister metadataPersister)
+ {
+ this.metadataPersister = metadataPersister;
+ }
+
+
+
+ // -------------------------------------- RepositoryContentPersisterFactory
+
+ public URI getTempDirURI()
+ {
+ return tempDirURI;
+ }
+
+ public void setTempDirURI(URI tempDirURI)
+ {
+ this.tempDirURI = tempDirURI;
+ }
+
+ public boolean accepts(Collection<URI> uris)
+ {
+ try
+ {
+ testURIs(uris);
+ return true;
+ }
+ catch (Exception e)
+ {
+ return false;
+ }
+ }
+
+ public LocalContentManager<FileBasedSynchronizationActionContext> getRepositoryContentPersister(Map<String, URI> namedURIMap, ProfileKey profileKey,
+ String localNodeName)
+ {
+ if (this.metadataPersister == null)
+ {
+ throw new IllegalStateException("Null metadataPersister; must configure a " +
+ ContentMetadataPersister.class.getSimpleName());
+ }
+ // Don't trust they called accept()
+ testURIs(namedURIMap.values());
+ // OK, looks good
+ return new FilesystemLocalContentManager(namedURIMap, profileKey, localNodeName, metadataPersister, tempDirURI);
+ }
+
+ // ---------------------------------------------------------------- Private
+
+ /**
+ * Confirms whether each element of <code>uris</code> can be passed to
+ * {@link File}'s constructor, resulting in a <code>File</code> that
+ * {@link File#exists() exists}.
+ *
+ * @param uris the collection of uris
+ *
+ * @throws IllegalArgumentException if any URI fails the above test.
+ */
+ private static void testURIs(Collection<URI> uris)
+ {
+ for (URI uri : uris)
+ {
+ File f = new File(uri);
+ if (!f.exists())
+ {
+ throw new IllegalArgumentException("No file found for URI " + uri);
+ }
+ }
+ }
+
+}
Property changes on: trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/local/file/FilesystemLocalContentManagerFactory.java
___________________________________________________________________
Name: svn:keywords
+
Added: trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/local/file/InitiateRmdirAction.java
===================================================================
--- trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/local/file/InitiateRmdirAction.java (rev 0)
+++ trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/local/file/InitiateRmdirAction.java 2009-03-31 22:10:16 UTC (rev 86553)
@@ -0,0 +1,80 @@
+/*
+ * 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.local.file;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.jboss.logging.Logger;
+import org.jboss.system.server.profileservice.repository.clustered.sync.ContentModification;
+import org.jboss.system.server.profileservice.repository.clustered.sync.SynchronizationAction;
+import org.jboss.system.server.profileservice.repository.clustered.sync.SynchronizationInitiationAction;
+
+/**
+ * {@link SynchronizationAction} that does nothing normally, but restores
+ * a removed directory during the rollback phase.
+ * <p>
+ * The intent is this action would execute at the start of a processing of
+ * removing a directory tree, followed by other actions to remove the contents
+ * of the tree, followed by a {@link RemoveFileAction} to remove the directory.
+ * This action does nothing during that sequence. But, during a rollback
+ * of the overall synchronization, it restores the removed directory, ensuring
+ * the directory is in place when the child removals roll back.
+ *
+ * @author Brian Stansberry
+ *
+ * @version $Revision: $
+ */
+public class InitiateRmdirAction
+ extends AbstractLocalContentChangeAction
+ implements SynchronizationInitiationAction<FileBasedSynchronizationActionContext>
+{
+ private static final Logger log = Logger.getLogger(InitiateRmdirAction.class);
+
+ /**
+ * Create a new PrepareRmdirAction.
+ */
+ public InitiateRmdirAction(File targetFile, FileBasedSynchronizationActionContext context,
+ ContentModification modification)
+ {
+ super(targetFile, context, modification);
+ }
+
+
+ // -------------------------------------------------------------- Protected
+
+
+ @Override
+ protected boolean modifyTarget() throws IOException
+ {
+ return true;
+ }
+
+ @Override
+ protected Logger getLogger()
+ {
+ return log;
+ }
+
+
+}
Property changes on: trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/local/file/InitiateRmdirAction.java
___________________________________________________________________
Name: svn:keywords
+
Added: trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/local/file/MkDirAction.java
===================================================================
--- trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/local/file/MkDirAction.java (rev 0)
+++ trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/local/file/MkDirAction.java 2009-03-31 22:10:16 UTC (rev 86553)
@@ -0,0 +1,83 @@
+/*
+ * 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.local.file;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.jboss.logging.Logger;
+import org.jboss.system.server.profileservice.repository.clustered.sync.ContentModification;
+import org.jboss.system.server.profileservice.repository.clustered.sync.SynchronizationAction;
+
+/**
+ * {@link SynchronizationAction} that makes a directory.
+ *
+ * @author Brian Stansberry
+ *
+ * @version $Revision: $
+ */
+public class MkDirAction extends AbstractLocalContentChangeAction
+{
+ private static final Logger log = Logger.getLogger(MkDirAction.class);
+
+ /**
+ * Create a new MkDirAction.
+ *
+ * @param targetFile the directory to create
+ * @param context the overall context of the modification
+ * @param modification the modification
+ */
+ public MkDirAction(File targetFile, FileBasedSynchronizationActionContext context,
+ ContentModification modification)
+ {
+ super(targetFile, context, modification);
+ }
+
+
+ // -------------------------------------------------------------- Protected
+
+
+ @Override
+ protected boolean modifyTarget() throws IOException
+ {
+ File target = getTargetFile();
+
+ boolean ok = (target.exists() == false || target.delete());
+ if (ok)
+ {
+ ok = target.mkdirs();
+ if (ok)
+ {
+ target.setLastModified(getRepositoryContentModification().getItem().getTimestamp());
+ }
+ }
+ return ok;
+ }
+
+ @Override
+ protected Logger getLogger()
+ {
+ return log;
+ }
+
+}
Property changes on: trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/local/file/MkDirAction.java
___________________________________________________________________
Name: svn:keywords
+
Added: trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/local/file/RemoveFileAction.java
===================================================================
--- trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/local/file/RemoveFileAction.java (rev 0)
+++ trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/local/file/RemoveFileAction.java 2009-03-31 22:10:16 UTC (rev 86553)
@@ -0,0 +1,128 @@
+/*
+ * 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.local.file;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.jboss.logging.Logger;
+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;
+import org.jboss.system.server.profileservice.repository.clustered.sync.ContentModification;
+import org.jboss.system.server.profileservice.repository.clustered.sync.SynchronizationAction;
+import org.jboss.system.server.profileservice.repository.clustered.sync.ContentModification.Type;
+
+/**
+ * {@link SynchronizationAction} that removes a {@link File}.
+ *
+ * @author Brian Stansberry
+ *
+ * @version $Revision: $
+ */
+public class RemoveFileAction extends AbstractLocalContentChangeAction
+{
+ private static final Logger log = Logger.getLogger(RemoveFileAction.class);
+
+ /**
+ * Create a new RemoveFileAction.
+ */
+ public RemoveFileAction(File targetFile, FileBasedSynchronizationActionContext context,
+ ContentModification modification)
+ {
+ super(targetFile, context, modification);
+ }
+
+
+ // -------------------------------------------------------------- Protected
+
+
+ @Override
+ protected boolean modifyTarget() throws IOException
+ {
+ File target = getTargetFile();
+ return target.exists() == false || target.delete();
+ }
+
+ @Override
+ protected Logger getLogger()
+ {
+ return log;
+ }
+
+
+ @Override
+ protected void doRollbackFromPrepared()
+ {
+ if (getRepositoryContentModification().getItem().isDirectory() == false)
+ {
+ super.doRollbackFromPrepared();
+ }
+ // else we assume there was a PrepareRmdir action that handled
+ // the rollback of the directory removal
+ }
+
+
+ @Override
+ protected void doRollbackFromRollbackOnly()
+ {
+ if (getRepositoryContentModification().getItem().isDirectory() == false)
+ {
+ super.doRollbackFromRollbackOnly();
+ }
+ // else we assume there was a PrepareRmdir action that handled
+ // the rollback of the directory removal
+ }
+
+
+ @Override
+ protected void updateContentMetadata()
+ {
+ ContentModification mod = getRepositoryContentModification();
+ RepositoryItemMetadata modItem = mod.getItem();
+ if (modItem.isRemoved())
+ {
+ // Just record it
+ super.updateContentMetadata();
+ }
+ else if (mod.getType() == Type.REMOVE_FROM_CLUSTER)
+ {
+ // An addition has been rejected. We don't record the item as
+ // removed in the metadata, we just remove it.
+ RepositoryContentMetadata contentMetadata = getContext().getInProgressMetadata();
+ RepositoryRootMetadata rmd = contentMetadata.getRepositoryRootMetadata(mod.getRootName());
+ rmd.removeItemMetadata(modItem.getRelativePathElements());
+ }
+ else
+ {
+ // Add a record of the item, marked as removed
+ RepositoryItemMetadata markedRemoved = getMarkedRemovedItem(mod);
+ RepositoryContentMetadata contentMetadata = getContext().getInProgressMetadata();
+ RepositoryRootMetadata rmd = contentMetadata.getRepositoryRootMetadata(mod.getRootName());
+ rmd.addItemMetadata(markedRemoved);
+ }
+ }
+
+
+
+}
Property changes on: trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/local/file/RemoveFileAction.java
___________________________________________________________________
Name: svn:keywords
+
Added: trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/metadata/AbstractSortedMetadataContainer.java
===================================================================
--- trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/metadata/AbstractSortedMetadataContainer.java (rev 0)
+++ trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/metadata/AbstractSortedMetadataContainer.java 2009-03-31 22:10:16 UTC (rev 86553)
@@ -0,0 +1,224 @@
+/*
+ * 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.metadata;
+
+import java.io.IOException;
+import java.io.Serializable;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeSet;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * Base class for objects that maintain a sorted collection of child
+ * {@link Identifiable} metadata and also provide efficient lookup capability
+ * based on a child item's {@link Identifiable#getId() id}.
+ *
+ * @author Brian Stansberry
+ *
+ * @version $Revision: $
+ */
+public abstract class AbstractSortedMetadataContainer<K, T extends Identifiable<K>>
+ implements Serializable
+{
+ /** The serialVersionUID */
+ private static final long serialVersionUID = 7130572488073615772L;
+
+ protected SortedSet<T> sortedItems = new TreeSet<T>();
+ private transient Map<K, T> itemMap = new ConcurrentHashMap<K, T>();
+ private transient Collection<T> exposedCollection = new MetadataCollection();
+
+ // --------------------------------------------------------------- Protected
+
+ /**
+ * Gets a collection that can be exposed to external callers. Modifications
+ * to the collection affect the internal state of this object. The
+ * iterator exposed by this collection will provide items ordered by
+ * the natural ordering of <code>T</code>. The returned collection is not
+ * thread safe.
+ */
+ protected Collection<T> getExposedCollection()
+ {
+ return exposedCollection;
+ }
+
+ /**
+ * Gets the metadata object identified by <code>key</code>.
+ *
+ * @param key the key
+ * @return the metadata, or <code>null</code> if <code>key</code> is unknown.
+ */
+ protected T getContainedMetadata(K key)
+ {
+ return itemMap.get(key);
+ }
+
+ /**
+ * Gets an unmodifiable view of the {@link Identifiable#getId() ids} of the
+ * metadata stored in this container.
+ *
+ * @return the ids. Will not be <code>null</code>.
+ */
+ protected Set<K> getContainedMetadataIds()
+ {
+ return Collections.unmodifiableSet(itemMap.keySet());
+ }
+
+ // ----------------------------------------------------------------- Private
+
+ private void readObject(java.io.ObjectInputStream in)
+ throws IOException, ClassNotFoundException
+ {
+ in.defaultReadObject();
+ exposedCollection = new MetadataCollection();
+ itemMap = new ConcurrentHashMap<K, T>();
+ for (T item : sortedItems)
+ {
+ itemMap.put(item.getId(), item);
+ }
+ }
+
+ private class MetadataCollection implements Collection<T>
+ {
+ public boolean add(T toAdd)
+ {
+ K id = toAdd.getId();
+ T existing = itemMap.put(id, toAdd);
+ boolean change = (toAdd.equals(existing) == false);
+ if (change)
+ {
+ if (existing != null)
+ {
+ sortedItems.remove(existing);
+ }
+ sortedItems.add(toAdd);
+ }
+ return change;
+ }
+
+ public boolean addAll(Collection<? extends T> c)
+ {
+ boolean mod = false;
+ for (T t : c)
+ {
+ if (add(t))
+ {
+ mod = true;
+ }
+ }
+ return mod;
+ }
+
+ public void clear()
+ {
+ itemMap.clear();
+ sortedItems.clear();
+ }
+
+ public boolean contains(Object o)
+ {
+ return sortedItems.contains(o);
+ }
+
+ public boolean containsAll(Collection<?> c)
+ {
+ return sortedItems.containsAll(c);
+ }
+
+ public boolean isEmpty()
+ {
+ return sortedItems.isEmpty();
+ }
+
+ public Iterator<T> iterator()
+ {
+ return sortedItems.iterator();
+ }
+
+ public boolean remove(Object toRemove)
+ {
+ boolean result = sortedItems.remove(toRemove);
+ if (result)
+ {
+ @SuppressWarnings("unchecked")
+ T item = (T) toRemove;
+ itemMap.remove(item.getId());
+ }
+ return result;
+ }
+
+ public boolean removeAll(Collection<?> c)
+ {
+ boolean mod = false;
+ for (Object o : c)
+ {
+ if (remove(o))
+ {
+ mod = true;
+ }
+ }
+ return mod;
+ }
+
+ public boolean retainAll(Collection<?> c)
+ {
+ throw new UnsupportedOperationException("retainAll is not supported");
+ }
+
+ public int size()
+ {
+ return sortedItems.size();
+ }
+
+ public Object[] toArray()
+ {
+ return sortedItems.toArray();
+ }
+
+ public <E> E[] toArray(E[] a)
+ {
+ return sortedItems.toArray(a);
+ }
+
+
+ public boolean equals(Object other)
+ {
+ if (other == this)
+ {
+ return true;
+ }
+
+ return sortedItems.equals(other);
+ }
+
+ public int hashCode()
+ {
+ return sortedItems.hashCode();
+ }
+ }
+
+}
Property changes on: trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/metadata/AbstractSortedMetadataContainer.java
___________________________________________________________________
Name: svn:keywords
+
Added: trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/metadata/ClusteredProfileSourceMetaData.java
===================================================================
--- trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/metadata/ClusteredProfileSourceMetaData.java (rev 0)
+++ trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/metadata/ClusteredProfileSourceMetaData.java 2009-03-31 22:10:16 UTC (rev 86553)
@@ -0,0 +1,48 @@
+/*
+ * 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.metadata;
+
+import javax.xml.bind.annotation.XmlAttribute;
+
+import org.jboss.system.server.profile.repository.metadata.AbstractProfileSourceMetaData;
+
+/**
+ * @author Brian Stansberry
+ *
+ */
+public abstract class ClusteredProfileSourceMetaData extends AbstractProfileSourceMetaData
+{
+ private String partitionName;
+
+ @XmlAttribute(name = "partition")
+ public String getPartitionName()
+ {
+ return partitionName;
+ }
+
+ public void setPartitionName(String partitionName)
+ {
+ this.partitionName = partitionName;
+ }
+
+}
Property changes on: trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/metadata/ClusteredProfileSourceMetaData.java
___________________________________________________________________
Name: svn:keywords
+
Added: trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/metadata/HotDeploymentClusteredProfileSourceMetaData.java
===================================================================
--- trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/metadata/HotDeploymentClusteredProfileSourceMetaData.java (rev 0)
+++ trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/metadata/HotDeploymentClusteredProfileSourceMetaData.java 2009-03-31 22:10:16 UTC (rev 86553)
@@ -0,0 +1,35 @@
+/*
+ * 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.metadata;
+
+
+/**
+ * @author Brian Stansberry
+ *
+ */
+public class HotDeploymentClusteredProfileSourceMetaData extends ClusteredProfileSourceMetaData
+{
+
+
+
+}
Property changes on: trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/metadata/HotDeploymentClusteredProfileSourceMetaData.java
___________________________________________________________________
Name: svn:keywords
+
Added: trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/metadata/Identifiable.java
===================================================================
--- trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/metadata/Identifiable.java (rev 0)
+++ trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/metadata/Identifiable.java 2009-03-31 22:10:16 UTC (rev 86553)
@@ -0,0 +1,35 @@
+/*
+ * 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.metadata;
+
+/**
+ *
+ *
+ * @author Brian Stansberry
+ *
+ * @version $Revision: $
+ */
+public interface Identifiable<T>
+{
+ T getId();
+}
Property changes on: trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/metadata/Identifiable.java
___________________________________________________________________
Name: svn:keywords
+
Added: trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/metadata/ImmutableClusteredProfileSourceMetaData.java
===================================================================
--- trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/metadata/ImmutableClusteredProfileSourceMetaData.java (rev 0)
+++ trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/metadata/ImmutableClusteredProfileSourceMetaData.java 2009-03-31 22:10:16 UTC (rev 86553)
@@ -0,0 +1,32 @@
+/*
+ * 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.metadata;
+
+/**
+ * @author Brian Stansberry
+ *
+ */
+public class ImmutableClusteredProfileSourceMetaData extends ClusteredProfileSourceMetaData
+{
+
+}
Property changes on: trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/metadata/ImmutableClusteredProfileSourceMetaData.java
___________________________________________________________________
Name: svn:keywords
+
Added: trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/metadata/RepositoryContentMetadata.java
===================================================================
--- trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/metadata/RepositoryContentMetadata.java (rev 0)
+++ trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/metadata/RepositoryContentMetadata.java 2009-03-31 22:10:16 UTC (rev 86553)
@@ -0,0 +1,247 @@
+/*
+ * 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.metadata;
+
+import java.io.Serializable;
+import java.util.Collection;
+import java.util.Set;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlAttribute;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlNsForm;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlType;
+
+import org.jboss.profileservice.spi.ProfileKey;
+import org.jboss.xb.annotations.JBossXmlSchema;
+
+/**
+ * Concise description of the contents of a ClusteredDeploymentRepository.
+ *
+ * @author Brian Stansberry
+ */
+ at XmlRootElement(name="repository-content", namespace= "")
+ at JBossXmlSchema(ignoreUnresolvedFieldOrClass=false,
+ namespace="",
+ elementFormDefault=XmlNsForm.UNSET,
+ normalizeSpace=true)
+ at XmlType(name="repositoryContentType", propOrder={"repositories", "name", "server", "domain"})
+ at XmlAccessorType(XmlAccessType.NONE)
+public class RepositoryContentMetadata
+ extends AbstractSortedMetadataContainer<String, RepositoryRootMetadata>
+ implements Serializable
+{
+ /** The serialVersionUID */
+ private static final long serialVersionUID = -557008659849613674L;
+
+ private String domain;
+ private String server;
+ private String name;
+
+ /**
+ * Default constructor.
+ */
+ public RepositoryContentMetadata()
+ {
+
+ }
+
+ public RepositoryContentMetadata(ProfileKey key)
+ {
+ this();
+
+ if (key == null)
+ {
+ throw new IllegalArgumentException("Null key");
+ }
+ setDomain(key.getDomain());
+ setServer(key.getServer());
+ setName(key.getName());
+ }
+
+ /**
+ * Copy constructor.
+ *
+ * @param toCopy the item to copy
+ *
+ * @throws IllegalArgumentException if <code>toCopy</code> is <code>null</code>
+ */
+ public RepositoryContentMetadata(RepositoryContentMetadata toCopy)
+ {
+ this();
+
+ if (toCopy == null)
+ {
+ throw new IllegalArgumentException("Null toCopy");
+ }
+
+ setDomain(toCopy.getDomain());
+ setServer(toCopy.getServer());
+ setName(toCopy.getName());
+
+ Collection<RepositoryRootMetadata> exposed = getExposedCollection();
+ for (RepositoryRootMetadata rmd : toCopy.getRepositories())
+ {
+ exposed.add(new RepositoryRootMetadata(rmd));
+ }
+ }
+
+ @XmlElement(name = "repository-root", type = RepositoryRootMetadata.class, required=true)
+ public Collection<RepositoryRootMetadata> getRepositories()
+ {
+ return getExposedCollection();
+ }
+
+ public void setRepositories(Collection<RepositoryRootMetadata> repositories)
+ {
+ Collection<RepositoryRootMetadata> internal = getExposedCollection();
+ internal.clear();
+
+ if (repositories != null)
+ {
+ internal.addAll(repositories);
+ }
+ }
+
+ public RepositoryRootMetadata getRepositoryRootMetadata(String repositoryRoot)
+ {
+ return getContainedMetadata(repositoryRoot);
+ }
+
+ public Set<String> getRootNames()
+ {
+ return getContainedMetadataIds();
+ }
+
+ @XmlAttribute(name = "name", required=true)
+ public String getName()
+ {
+ return name;
+ }
+
+ public void setName(String name)
+ {
+ if (name == null)
+ {
+ throw new IllegalArgumentException("Null name");
+ }
+ this.name = name;
+ }
+
+ @XmlAttribute(name = "server", required=true)
+ public String getServer()
+ {
+ return server;
+ }
+
+ public void setServer(String server)
+ {
+ if (server == null)
+ {
+ throw new IllegalArgumentException("Null server");
+ }
+ this.server = server;
+ }
+
+ @XmlAttribute(name = "domain", required=true)
+ public String getDomain()
+ {
+ return domain;
+ }
+
+ public void setDomain(String domain)
+ {
+ if (domain == null)
+ {
+ throw new IllegalArgumentException("Null domain");
+ }
+ this.domain = domain;
+ }
+
+ // -------------------------------------------------------------- Overrides
+
+ @Override
+ public boolean equals(Object obj)
+ {
+ if (this == obj)
+ {
+ return true;
+ }
+
+ if (obj instanceof RepositoryContentMetadata)
+ {
+ RepositoryContentMetadata other = (RepositoryContentMetadata) obj;
+ return (getExposedCollection().equals(other.getExposedCollection())
+ && safeEquals(this.name, other.name)
+ && safeEquals(this.server, other.server)
+ && safeEquals(this.domain, other.domain));
+ }
+
+ return false;
+ }
+
+ @Override
+ public int hashCode()
+ {
+
+ int result = 17;
+ result = 31 * result + (name == null ? 0 : name.hashCode());
+ result = 31 * result + (server == null ? 0 : server.hashCode());
+ result = 31 * result + (domain == null ? 0 : domain.hashCode());
+ result = 31 * result + getExposedCollection().hashCode();
+ return result;
+ }
+
+ @Override
+ public String toString()
+ {
+ StringBuilder sb = new StringBuilder(getClass().getName())
+ .append("[domain=").append(domain)
+ .append(",server=").append(server)
+ .append(",name=").append(name)
+ .append(",roots={");
+ boolean first = true;
+ for (String root : getRootNames())
+ {
+ if (!first)
+ {
+ sb.append(',');
+ }
+ else
+ {
+ first = false;
+ }
+ sb.append(root);
+ }
+ sb.append("}]");
+ return sb.toString();
+ }
+
+ private static boolean safeEquals(Object a, Object b)
+ {
+ return (a == b || (a != null && a.equals(b)));
+ }
+
+}
Property changes on: trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/metadata/RepositoryContentMetadata.java
___________________________________________________________________
Name: svn:keywords
+
Added: trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/metadata/RepositoryItemMetadata.java
===================================================================
--- trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/metadata/RepositoryItemMetadata.java (rev 0)
+++ trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/metadata/RepositoryItemMetadata.java 2009-03-31 22:10:16 UTC (rev 86553)
@@ -0,0 +1,378 @@
+/*
+ * 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.metadata;
+
+import java.io.Serializable;
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+
+import javax.xml.bind.annotation.XmlAttribute;
+import javax.xml.bind.annotation.XmlTransient;
+import javax.xml.bind.annotation.XmlType;
+
+/**
+ * Description of an individual item (i.e. file) in a clustered repository.
+ *
+ * @author Brian Stansberry
+ */
+ at XmlType(name="repositoryItemType", propOrder={"timestampAsString", "originatingNode", "removed", "directory", "relativePath"})
+public class RepositoryItemMetadata
+ implements Identifiable<List<String>>, Serializable, Comparable<RepositoryItemMetadata>
+{
+ /** The serialVersionUID */
+ private static final long serialVersionUID = 7712110893517082031L;
+
+ private static final DateFormat dateFormat;
+
+ static
+ {
+ dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
+ }
+
+ /**
+ * Marker value to pass to {@link #setTimestampAsString(String)} that will
+ * generate a timestamp equal to System.currentTimeMillis(). Useful for
+ * testing.
+ */
+ public static String NOW = "NOW";
+
+ private volatile String relativePath;
+ private volatile long timestamp;
+ private volatile boolean directory;
+ /** The parsed elements of relativePath. Lazy initialized */
+ private volatile transient List<String> pathElements;
+
+ private volatile String originatingNode;
+ private volatile boolean removed;
+ private String rootName;
+
+
+ public static List<String> getPathElements(String path)
+ {
+ String[] elements = path.split("/");
+ return Collections.unmodifiableList(Arrays.asList(elements));
+ }
+
+ /**
+ * Constructor for XML parser.
+ */
+ public RepositoryItemMetadata()
+ {
+
+ }
+
+ public RepositoryItemMetadata(List<String> pathElements, long timestamp, String originatingNode, boolean directory, boolean removed)
+ {
+ setDirectory(directory);
+ setRelativePathElements(pathElements);
+ setTimestamp(timestamp);
+ setOriginatingNode(originatingNode);
+ setRemoved(removed);
+ }
+
+ /**
+ * Copy constructor. Performs a deep copy of the path element list.
+ *
+ * @param toCopy the item to copy
+ *
+ * @throws NullPointerException if <code>toCopy</code> is <code>null</code>
+ */
+ public RepositoryItemMetadata(RepositoryItemMetadata toCopy)
+ {
+ this(toCopy.getRelativePathElements(), toCopy.getTimestamp(),
+ toCopy.getOriginatingNode(), toCopy.isDirectory(), toCopy.isRemoved());
+ }
+
+ public List<String> getId()
+ {
+ return getRelativePathElements();
+ }
+
+ @XmlTransient
+ public String getRootName()
+ {
+ return rootName;
+ }
+
+ public void setRootName(String rootName)
+ {
+ this.rootName = rootName;
+ }
+
+ @XmlAttribute(name = "relative-path", required=true)
+ public String getRelativePath()
+ {
+ return relativePath;
+ }
+
+ public void setRelativePath(String path)
+ {
+ if (path != null && path.length() > 0 && '/' == path.charAt(0))
+ {
+ path = path.length() == 0 ? "" : path.substring(1);
+ }
+ this.relativePath = path;
+ this.pathElements = null;
+ }
+
+ @XmlAttribute(name = "directory")
+ public boolean isDirectory()
+ {
+ return directory;
+ }
+
+ public void setDirectory(boolean directory)
+ {
+ this.directory = directory;
+ if (relativePath != null)
+ {
+ if (!directory && relativePath.endsWith("/"))
+ {
+ relativePath = relativePath.substring(0, relativePath.length() - 1);
+ }
+ else if (directory && relativePath.endsWith("/") == false)
+ {
+ relativePath += "/";
+ }
+ }
+ }
+
+ @XmlTransient
+ public long getTimestamp()
+ {
+ return timestamp;
+ }
+
+ public void setTimestamp(long timestamp)
+ {
+ this.timestamp = timestamp;
+ }
+
+ @XmlAttribute(name = "timestamp", required=true)
+ public String getTimestampAsString()
+ {
+ Date d = new Date(timestamp);
+ synchronized (dateFormat)
+ {
+ return dateFormat.format(d);
+ }
+ }
+
+ public void setTimestampAsString(String timestamp)
+ {
+ if (NOW.equals(timestamp))
+ {
+ setTimestamp(System.currentTimeMillis());
+ }
+ else
+ {
+ try
+ {
+ synchronized (dateFormat)
+ {
+ Date d = dateFormat.parse(timestamp);
+ setTimestamp(d.getTime());
+ }
+ }
+ catch (ParseException e)
+ {
+ throw new RuntimeException("Failed to parse " + timestamp, e);
+ }
+ }
+ }
+
+ @XmlTransient
+ public List<String> getRelativePathElements()
+ {
+ if (pathElements == null && relativePath != null)
+ {
+ String[] elements = relativePath.split("/");
+ setRelativePathElements(Arrays.asList(elements));
+ }
+ return pathElements;
+ }
+
+ public void setRelativePathElements(List<String> pathElements)
+ {
+ if (pathElements == null)
+ {
+ this.pathElements = null;
+ this.relativePath = null;
+ }
+ else
+ {
+ this.pathElements = Collections.unmodifiableList(new ArrayList<String>(pathElements));
+ boolean first = true;
+ StringBuilder sb = new StringBuilder();
+ for (String element : pathElements)
+ {
+ if (!first)
+ {
+ sb.append('/');
+ }
+ else
+ {
+ first = false;
+ }
+ sb.append(element);
+ }
+ if (directory)
+ {
+ sb.append('/');
+ }
+ this.relativePath = sb.toString();
+ }
+ }
+
+ /**
+ * The name of the cluster node that propagated this version of
+ * the item to the cluster.
+ *
+ * @return
+ */
+ @XmlAttribute(name = "originator", required=true)
+ public String getOriginatingNode()
+ {
+ return originatingNode;
+ }
+
+ public void setOriginatingNode(String originatingNode)
+ {
+ this.originatingNode = originatingNode;
+ }
+
+ @XmlAttribute(name = "removed")
+ public boolean isRemoved()
+ {
+ return removed;
+ }
+
+ public void setRemoved(boolean removed)
+ {
+ this.removed = removed;
+ }
+
+ /**
+ * Gets whether this item is a child of another item.
+ *
+ * @param other the other item. Can be <code>null</code> in which case
+ * this method will return <code>false</code>
+ *
+ * @return <code>true</code> if other is not <code>null</code>, is a
+ * {@link #isDirectory() directory} and this items path starts
+ * with <code>other</code>'s path.
+ */
+ public boolean isChildOf(RepositoryItemMetadata other)
+ {
+ return other != null && other.isDirectory() && getRelativePath().startsWith(other.getRelativePath());
+ }
+
+ // -------------------------------------------------------------- Comparable
+
+ public int compareTo(RepositoryItemMetadata o)
+ {
+ int result = 0;
+ if (this != o)
+ {
+ if (this.relativePath != o.relativePath)
+ {
+ if (this.relativePath != null)
+ {
+ result = o.relativePath == null ? 1 : this.relativePath.compareTo(o.relativePath);
+ }
+ else
+ {
+ result = -1;
+ }
+ }
+
+ if (result == 0)
+ {
+ result = (int) (this.timestamp - o.timestamp);
+ }
+ }
+ return result;
+ }
+
+ // -------------------------------------------------------------- Overrides
+
+ @Override
+ public boolean equals(Object obj)
+ {
+ boolean result = (this == obj);
+
+ if (!result && obj instanceof RepositoryItemMetadata)
+ {
+ RepositoryItemMetadata other = (RepositoryItemMetadata) obj;
+ result = (this.timestamp == other.timestamp)
+ && this.removed == other.removed
+ && this.directory == other.directory
+ && safeEquals(this.getRelativePathElements(), other.getRelativePathElements())
+ && safeEquals(this.originatingNode, other.originatingNode);
+ }
+ return result;
+ }
+
+ @Override
+ public int hashCode()
+ {
+ int result = 17;
+ result = 31 * result + ((int) (timestamp ^ (timestamp >>>32)));
+ result = 31 * result + (removed ? 0 : 1);
+ result = 31 * result + (directory ? 0 : 1);
+ List<String> elements = getRelativePathElements();
+ result = 31 * result + (elements == null ? 0 : elements.hashCode());
+ result = 31 * result + (originatingNode == null ? 0 : originatingNode.hashCode());
+ return result;
+ }
+
+ @Override
+ public String toString()
+ {
+ return new StringBuilder(getClass().getName())
+ .append("[path='")
+ .append(relativePath)
+ .append(",timestamp=")
+ .append(timestamp)
+ .append(",originatingNode=")
+ .append(originatingNode)
+ .append(",removed=")
+ .append(removed)
+ .append(']').toString();
+ }
+
+ // -------------------------------------------------------------- Private
+
+ private static boolean safeEquals(Object a, Object b)
+ {
+ return (a == b || (a != null && a.equals(b)));
+ }
+
+
+}
Property changes on: trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/metadata/RepositoryItemMetadata.java
___________________________________________________________________
Name: svn:keywords
+
Added: trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/metadata/RepositoryRootMetadata.java
===================================================================
--- trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/metadata/RepositoryRootMetadata.java (rev 0)
+++ trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/metadata/RepositoryRootMetadata.java 2009-03-31 22:10:16 UTC (rev 86553)
@@ -0,0 +1,224 @@
+/*
+ * 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.metadata;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import javax.xml.bind.annotation.XmlAttribute;
+import javax.xml.bind.annotation.XmlElement;
+
+/**
+ * @author Brian Stansberry
+ *
+ */
+public class RepositoryRootMetadata
+ extends AbstractSortedMetadataContainer<List<String>, RepositoryItemMetadata>
+ implements Identifiable<String>, Serializable, Comparable<RepositoryRootMetadata>
+{
+ /** The serialVersionUID */
+ private static final long serialVersionUID = -4102001386900647551L;
+
+ private String name;
+
+ /**
+ * Constructor for XML parser.
+ */
+ public RepositoryRootMetadata()
+ {
+ }
+
+ /**
+ * Create a new RepositoryRootMetadata with the given name.
+ *
+ * @param name the name. Cannot be <code>null</code>
+ *
+ * @throws IllegalArgumentException if name is <code>null</code>
+ */
+ public RepositoryRootMetadata(String name)
+ {
+ if (name == null)
+ {
+ throw new IllegalArgumentException("Null name");
+ }
+ setName(name);
+ }
+
+ /**
+ * Copy constructor.
+ *
+ * @param toCopy the item to copy
+ *
+ * @throws NullPointerException if <code>toCopy</code> is <code>null</code>
+ */
+ public RepositoryRootMetadata(RepositoryRootMetadata toCopy)
+ {
+ this(toCopy.getName());
+ Collection<RepositoryItemMetadata> content = toCopy.getContent();
+ Collection<RepositoryItemMetadata> internal = getExposedCollection();
+ for(RepositoryItemMetadata item : content)
+ {
+ internal.add(new RepositoryItemMetadata(item));
+ }
+ }
+
+ public String getId()
+ {
+ return name;
+ }
+
+ @XmlAttribute(name = "name")
+ public String getName()
+ {
+ return name;
+ }
+
+ public void setName(String name)
+ {
+ this.name = name;
+ }
+
+ @XmlElement(name = "content")
+ public Collection<RepositoryItemMetadata> getContent()
+ {
+ return getExposedCollection();
+ }
+
+ public void setContent(Collection<RepositoryItemMetadata> content)
+ {
+ Collection<RepositoryItemMetadata> internal = getExposedCollection();
+ internal.clear();
+ if (content != null)
+ {
+ for (RepositoryItemMetadata md : content)
+ {
+ internal.add(md);
+ }
+ }
+ }
+
+ public RepositoryItemMetadata getItemMetadata(List<String> path)
+ {
+ return getContainedMetadata(path);
+ }
+
+ public void addItemMetadata(RepositoryItemMetadata md)
+ {
+ getExposedCollection().add(md);
+ }
+
+ public boolean removeItemMetadata(List<String> path)
+ {
+ RepositoryItemMetadata md = getItemMetadata(path);
+ if (md != null)
+ {
+ return getExposedCollection().remove(md);
+ }
+ return false;
+ }
+
+ // -------------------------------------------------------------- Comparable
+
+ public int compareTo(RepositoryRootMetadata other)
+ {
+ int result = 0;
+ // Null name comes later
+ if (this.name == null)
+ {
+ if (other.name != null)
+ {
+ result = 1;
+ }
+ }
+ else if (other.name == null)
+ {
+ result = -1;
+ }
+ else
+ {
+ result = this.name.compareTo(other.name);
+ }
+
+ if (result == 0)
+ {
+ List<RepositoryItemMetadata> us = new ArrayList<RepositoryItemMetadata>(this.getExposedCollection());
+ List<RepositoryItemMetadata> them = new ArrayList<RepositoryItemMetadata>(other.getExposedCollection());
+
+ result = them.size() - us.size();
+ if (result == 0)
+ {
+ for (int i = 0; i < us.size(); i++)
+ {
+ result = us.get(i).compareTo(them.get(i));
+ if (result != 0)
+ {
+ break;
+ }
+ }
+ }
+ }
+ return result;
+
+ }
+
+ // -------------------------------------------------------------- Overrides
+
+ @Override
+ public boolean equals(Object obj)
+ {
+ if (this == obj)
+ {
+ return true;
+ }
+
+ if (obj instanceof RepositoryRootMetadata)
+ {
+ RepositoryRootMetadata other = (RepositoryRootMetadata) obj;
+ return (this.getExposedCollection().equals(other.getExposedCollection())
+ && (this.name != null && this.name.equals(other.name)));
+ }
+
+ return false;
+ }
+
+ @Override
+ public int hashCode()
+ {
+ int result = 17;
+ result = 31 * result + (name == null ? 0 : name.hashCode());
+ result = 31 * result + getExposedCollection().hashCode();
+ return result;
+ }
+
+ @Override
+ public String toString()
+ {
+ return new StringBuilder(getClass().getName())
+ .append("[name='")
+ .append(name)
+ .append(']').toString();
+ }
+
+}
Property changes on: trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/metadata/RepositoryRootMetadata.java
___________________________________________________________________
Name: svn:keywords
+
Added: trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/AbstractContentMetadataMutatorAction.java
===================================================================
--- trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/AbstractContentMetadataMutatorAction.java (rev 0)
+++ trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/AbstractContentMetadataMutatorAction.java 2009-03-31 22:10:16 UTC (rev 86553)
@@ -0,0 +1,86 @@
+/*
+ * 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 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;
+
+/**
+ * Base class for {@link SynchronizationAction} implementations
+ * that mutate the {@link RepositoryContentMetadata} as part of their
+ * function.
+ *
+ * @author Brian Stansberry
+ *
+ * @version $Revision: $
+ */
+public abstract class AbstractContentMetadataMutatorAction<T extends SynchronizationActionContext>
+ extends AbstractSynchronizationAction<T>
+{
+ private final RepositoryItemMetadata rollbackMetadata;
+
+ /**
+ * Create a new AbstractContentMetadataMutatorAction.
+ *
+ * @param context the overall context of the modification
+ * @param modification the modification
+ */
+ protected AbstractContentMetadataMutatorAction(T context,
+ ContentModification modification)
+ {
+ super(context, modification);
+
+ RepositoryContentMetadata contentMetadata = context.getInProgressMetadata();
+ RepositoryRootMetadata rmd = contentMetadata.getRepositoryRootMetadata(modification.getRootName());
+ if (rmd == null)
+ {
+ throw new IllegalStateException("Root " + modification.getRootName() + " unknown to " + contentMetadata);
+ }
+ this.rollbackMetadata = rmd.getItemMetadata(modification.getItem().getRelativePathElements());
+ }
+
+ protected void updateContentMetadata()
+ {
+ ContentModification mod = getRepositoryContentModification();
+ RepositoryContentMetadata contentMetadata = getContext().getInProgressMetadata();
+ RepositoryRootMetadata rmd = contentMetadata.getRepositoryRootMetadata(mod.getRootName());
+ rmd.addItemMetadata(mod.getItem());
+ }
+
+ protected void rollbackContentMetadata()
+ {
+ ContentModification mod = getRepositoryContentModification();
+ RepositoryContentMetadata contentMetadata = getContext().getInProgressMetadata();
+ RepositoryRootMetadata rmd = contentMetadata.getRepositoryRootMetadata(mod.getRootName());
+ if (rollbackMetadata == null)
+ {
+ rmd.removeItemMetadata(mod.getItem().getRelativePathElements());
+ }
+ else
+ {
+ rmd.addItemMetadata(rollbackMetadata);
+ }
+ }
+
+}
Property changes on: trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/AbstractContentMetadataMutatorAction.java
___________________________________________________________________
Name: svn:keywords
+
Added: trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/AbstractContentModificationGenerator.java
===================================================================
--- trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/AbstractContentModificationGenerator.java (rev 0)
+++ trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/AbstractContentModificationGenerator.java 2009-03-31 22:10:16 UTC (rev 86553)
@@ -0,0 +1,492 @@
+/*
+ * 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.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Stack;
+import java.util.TreeSet;
+
+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 superclass of classes that generate a list of
+ * {@link ContentModification} from a pair
+ * of {@link RepositoryContentMetadata}.
+ *
+ * @author Brian Stansberry
+ *
+ * @version $Revision: $
+ */
+public abstract class AbstractContentModificationGenerator
+{
+ // ----------------------------------------------------------------- Public
+
+ public List<ContentModification> getModificationList(RepositoryContentMetadata base,
+ RepositoryContentMetadata modified)
+ throws InconsistentRepositoryStructureException
+ {
+ if (base == null)
+ {
+ throw new IllegalArgumentException("Null base");
+ }
+ if (modified == null)
+ {
+ throw new IllegalArgumentException("Null modified");
+ }
+
+ Collection<RepositoryRootMetadata> baseRoots = base.getRepositories();
+ Collection<RepositoryRootMetadata> modifiedRoots = modified.getRepositories();
+
+ // We validate consistent repository structure by 1) confirming
+ // same number of roots and 2) (below) confirming all base roots are in modified
+ if (baseRoots.size() != modifiedRoots.size())
+ {
+ throw new InconsistentRepositoryStructureException(base, modified);
+ }
+
+ List<ContentModification> mods = new ArrayList<ContentModification>();
+ for (RepositoryRootMetadata root : base.getRepositories())
+ {
+ RepositoryRootMetadata newRoot = modified.getRepositoryRootMetadata(root.getName());
+ if (newRoot != null)
+ {
+ mods.addAll(getModificationList(root, newRoot));
+ }
+ else
+ {
+ throw new InconsistentRepositoryStructureException(base, modified);
+ }
+ }
+
+ return mods;
+ }
+
+ // -------------------------------------------------------------- Protected
+
+ protected abstract void handleAddition(String rootName,
+ RepositoryItemMetadata item, GeneratedModifications mods);
+
+ protected abstract void handleMissing(String rootName,
+ RepositoryItemMetadata item, GeneratedModifications mods);
+
+
+
+ protected void handleMatch(String rootName,
+ RepositoryItemMetadata base,
+ RepositoryItemMetadata modified,
+ GeneratedModifications mods)
+ {
+ // Most common case of all is no change
+ if (base.equals(modified) == false)
+ {
+ // There was a change; how we handle depends on what changed
+
+ if (base.isRemoved() && !modified.isRemoved())
+ {
+ // Reincarnation
+ handleAddition(rootName, modified, base, mods);
+ }
+ else if (modified.isRemoved() && !base.isRemoved())
+ {
+ // Item was removed on the remote node
+ handleRemoval(rootName, base, mods);
+ }
+ else if (base.isDirectory() && !modified.isDirectory())
+ {
+ // An exploded archive replaced by a zipped one
+ handleChangeFromDirectory(rootName, base, modified, mods);
+ }
+ else if (modified.isDirectory() && !base.isDirectory())
+ {
+ // A zipped archive replaced by an exploded one
+ handleChangeToDirectory(rootName, base, modified, mods);
+ }
+ else if (modified.isDirectory())
+ {
+ // Directory timestamp modification
+ handleDirectoryTimestampModification(rootName, base, modified, mods);
+ }
+ else
+ {
+ // Simple file change
+ handleSimpleModification(rootName, base, modified, mods);
+ }
+ }
+ }
+
+ protected abstract void handleSimpleModification(String rootName, RepositoryItemMetadata base, RepositoryItemMetadata modified,
+ GeneratedModifications mods);
+
+ protected abstract void handleDirectoryTimestampModification(String rootName, RepositoryItemMetadata base,
+ RepositoryItemMetadata modified, GeneratedModifications mods);
+
+ protected abstract void handleChangeToDirectory(String rootName, RepositoryItemMetadata base, RepositoryItemMetadata modified,
+ GeneratedModifications mods);
+
+ protected abstract void handleChangeFromDirectory(String rootName, RepositoryItemMetadata base,
+ RepositoryItemMetadata modified, GeneratedModifications mods);
+
+ protected abstract void handleRemoval(String rootName, RepositoryItemMetadata base, GeneratedModifications mods);
+
+ protected abstract void handleAddition(String rootName, RepositoryItemMetadata modified, RepositoryItemMetadata base,
+ GeneratedModifications mods);
+
+ protected void drainPreapprovedRemovals(GeneratedModifications mods)
+ {
+ ContentModification preapprovedRemoval;
+ while ((preapprovedRemoval = mods.popPreapprovedRemoveParent()) != null)
+ {
+ mods.addModification(preapprovedRemoval);
+ }
+ }
+
+ protected static RepositoryItemMetadata getMarkedRemovedItem(RepositoryItemMetadata base)
+ {
+ RepositoryItemMetadata result = base;
+ if (result.isRemoved() == false)
+ {
+ result = new RepositoryItemMetadata(result);
+ result.setRemoved(true);
+ }
+
+ return result;
+ }
+
+ // ---------------------------------------------------------------- Private
+
+ private List<ContentModification> getModificationList(RepositoryRootMetadata base,
+ RepositoryRootMetadata modified)
+ {
+ List<ContentModification> mods = new ArrayList<ContentModification>();
+
+ OwnedItem[] items = getOwnedItems(base, modified);
+
+ // Track directories that we have added or removed so we can flag
+ // their children for automatic modification approval/rejection. We
+ // don't want to add/remove a directory and then later do something
+ // inconsistent with a child. For stuff involving a removal we use
+ // a Stack<RepositoryContentModification> so we can add the root removal
+ // to the overall list of mods *after* all the children. This is needed
+ // to allow the removal to be rolled back.
+ RepositoryItemMetadata preapprovedAddParent = null;
+ Stack<ContentModification> preapprovedRemoveParent = new Stack<ContentModification>();
+ Stack<ContentModification> prerejectedAddParent = new Stack<ContentModification>();
+ RepositoryItemMetadata prerejectedRemoveParent = null;
+
+ for(int first = 0; first < items.length; first++)
+ {
+ GeneratedModifications pairmod = new GeneratedModifications(preapprovedAddParent, preapprovedRemoveParent, prerejectedAddParent, prerejectedRemoveParent);
+
+ int next = first + 1;
+ if (next >= items.length)
+ {
+ // Last unmatched item
+ if (items[first].base)
+ {
+ // base w/o match == missing
+ handleMissing(base.getName(), items[first].item, pairmod);
+ }
+ else
+ {
+ // !base w/o match == addition
+ handleAddition(base.getName(), items[first].item, pairmod);
+ }
+ }
+ else if (items[first].itemPath.equals(items[next].itemPath))
+ {
+ handleMatch(base.getName(), items[first].item, items[next].item, pairmod);
+ first++; // we just consumed "next"
+ }
+ else if (items[first].base)
+ {
+ // base w/o match == removal
+ handleMissing(base.getName(), items[first].item, pairmod);
+ }
+ else
+ {
+ // !base w/o match == addition
+ handleAddition(base.getName(), items[first].item, pairmod);
+ }
+
+ mods.addAll(pairmod.getModifications());
+ preapprovedAddParent = pairmod.getPreapprovedAddParent();
+ prerejectedRemoveParent = pairmod.getPrerejectedRemoveParent();
+ }
+
+ // Any remaining mods on our stacks need to be flushed to the list
+ for (ContentModification mod : preapprovedRemoveParent)
+ {
+ mods.add(mod);
+ }
+ for (ContentModification mod : prerejectedAddParent)
+ {
+ mods.add(mod);
+ }
+ return mods;
+ }
+
+ private OwnedItem[] getOwnedItems(RepositoryRootMetadata base, RepositoryRootMetadata modified)
+ {
+ TreeSet<OwnedItem> ownedItems = new TreeSet<OwnedItem>();
+ for (RepositoryItemMetadata item : base.getContent())
+ {
+ ownedItems.add(new OwnedItem(item, true));
+ }
+ for (RepositoryItemMetadata item : modified.getContent())
+ {
+ ownedItems.add(new OwnedItem(item, false));
+ }
+
+ OwnedItem[] ownedItemArray = ownedItems.toArray(new OwnedItem[ownedItems.size()]);
+ return ownedItemArray;
+ }
+
+ protected static class GeneratedModifications
+ {
+ private RepositoryItemMetadata preapprovedAddParent;
+ private final Stack<ContentModification> preapprovedRemoveParent;
+ private final Stack<ContentModification> prerejectedAddParent;
+ private RepositoryItemMetadata prerejectedRemoveParent;
+ private final List<ContentModification> modifications = new ArrayList<ContentModification>();
+
+ public GeneratedModifications(RepositoryItemMetadata preapprovedAddParent,
+ Stack<ContentModification> preapprovedRemoveParent,
+ Stack<ContentModification> prerejectedAddParent,
+ RepositoryItemMetadata prerejectedRemoveParent)
+ {
+ if (preapprovedRemoveParent == null)
+ {
+ throw new IllegalArgumentException("Null preapprovedRemoveParent");
+ }
+ if (prerejectedAddParent == null)
+ {
+ throw new IllegalArgumentException("Null prerejectedAddParent");
+ }
+ this.preapprovedAddParent = preapprovedAddParent;
+ this.preapprovedRemoveParent = preapprovedRemoveParent;
+ this.prerejectedAddParent = prerejectedAddParent;
+ this.prerejectedRemoveParent = prerejectedRemoveParent;
+ }
+
+ // --------------------------------------------------------------- Public
+
+ public void addModification(ContentModification mod)
+ {
+ modifications.add(mod);
+ }
+
+ public List<ContentModification> getModifications()
+ {
+ return modifications;
+ }
+
+ public RepositoryItemMetadata getPreapprovedAddParent()
+ {
+ return preapprovedAddParent;
+ }
+
+ public void setPreapprovedAddParent(RepositoryItemMetadata preapprovedAddParent)
+ {
+ if (preapprovedAddParent != null)
+ {
+ validateCanCallSet();
+ }
+ this.preapprovedAddParent = preapprovedAddParent;
+ }
+
+ public ContentModification peekPreapprovedRemoveParent()
+ {
+ return preapprovedRemoveParent.size() == 0 ? null : preapprovedRemoveParent.peek();
+ }
+
+ public ContentModification popPreapprovedRemoveParent()
+ {
+ return preapprovedRemoveParent.size() == 0 ? null : preapprovedRemoveParent.pop();
+ }
+
+ public void pushPreapprovedRemoveParent(ContentModification toPush)
+ {
+ if (toPush == null)
+ {
+ throw new IllegalArgumentException("Null prerejectedAddParent");
+ }
+ if (this.preapprovedAddParent != null)
+ {
+ throw new IllegalStateException("preapprovedAddParent already set");
+ }
+ else if (this.prerejectedAddParent.size() > 0)
+ {
+ throw new IllegalStateException("prerejectedAddParent already set");
+ }
+ else if (this.prerejectedRemoveParent != null)
+ {
+ throw new IllegalStateException("prerejectedRemoveParent already set");
+ }
+
+ ContentModification peeked = peekPreapprovedRemoveParent();
+ if (peeked != null && toPush.getItem().isChildOf(peeked.getItem()) == false)
+ {
+ throw new IllegalArgumentException(toPush.getItem() +
+ " is not a child of existing item " + peeked.getItem());
+ }
+
+ this.preapprovedRemoveParent.push(toPush);
+ }
+
+ public ContentModification peekPrerejectedAddParent()
+ {
+ return prerejectedAddParent.size() == 0 ? null : prerejectedAddParent.peek();
+ }
+
+ public ContentModification popPrerejectedAddParent()
+ {
+ return prerejectedAddParent.size() == 0 ? null : prerejectedAddParent.pop();
+ }
+
+ public void pushPrerejectedAddParent(ContentModification toPush)
+ {
+ if (toPush == null)
+ {
+ throw new IllegalArgumentException("Null prerejectedAddParent");
+ }
+ if (this.preapprovedAddParent != null)
+ {
+ throw new IllegalStateException("preapprovedAddParent already set");
+ }
+ else if (this.preapprovedRemoveParent.size() > 0)
+ {
+ throw new IllegalStateException("preapprovedRemoveParent already set");
+ }
+ else if (this.prerejectedRemoveParent != null)
+ {
+ throw new IllegalStateException("prerejectedRemoveParent already set");
+ }
+ ContentModification peeked = peekPrerejectedAddParent();
+ if (peeked != null && toPush.getItem().isChildOf(peeked.getItem()) == false)
+ {
+ throw new IllegalArgumentException(toPush.getItem() +
+ " is not a child of existing item " + peeked.getItem());
+ }
+
+ this.prerejectedAddParent.push(toPush);
+ }
+
+ public RepositoryItemMetadata getPrerejectedRemoveParent()
+ {
+ return prerejectedRemoveParent;
+ }
+
+ public void setPrerejectedRemoveParent(RepositoryItemMetadata prerejectedRemoveParent)
+ {
+ if (prerejectedRemoveParent != null)
+ {
+ validateCanCallSet();
+ }
+ this.prerejectedRemoveParent = prerejectedRemoveParent;
+ }
+
+ private void validateCanCallSet()
+ {
+ if (this.preapprovedAddParent != null)
+ {
+ throw new IllegalStateException("preapprovedAddParent already set");
+ }
+ else if (this.preapprovedRemoveParent.size() > 0)
+ {
+ throw new IllegalStateException("preapprovedRemoveParent already set");
+ }
+ else if (this.prerejectedAddParent.size() > 0)
+ {
+ throw new IllegalStateException("prerejectedAddParent already set");
+ }
+ else if (this.prerejectedRemoveParent != null)
+ {
+ throw new IllegalStateException("prerejectedRemoveParent already set");
+ }
+ }
+
+ }
+
+ private static class OwnedItem implements Comparable<OwnedItem>
+ {
+ public final boolean base;
+ public final RepositoryItemMetadata item;
+ public final List<String> itemPath;
+
+ private OwnedItem(RepositoryItemMetadata item, boolean base)
+ {
+ assert item != null : "item is null";
+ this.item = item;
+ this.itemPath = item.getRelativePathElements();
+ this.base = base;
+ }
+
+ public int compareTo(OwnedItem other)
+ {
+ List<String> ourPath = itemPath;
+ List<String> otherPath = other.itemPath;
+ int result = 0;
+ for (int i = 0; i < ourPath.size(); i++)
+ {
+ if (i >= otherPath.size())
+ {
+ // We've got extra levels they don't
+ result = 1;
+ break;
+ }
+
+ int comp = ourPath.get(i).compareTo(otherPath.get(i));
+ if (comp != 0)
+ {
+ result = (comp < 0) ? -1 : 1;
+ break;
+ }
+ }
+
+ if (result == 0 && otherPath.size() != ourPath.size())
+ {
+ // They've got extra levels we don't
+ result = -1;
+ }
+
+ if (result == 0)
+ {
+ // base comes before !base
+ if (base && !other.base)
+ {
+ result = -1;
+ }
+ else if (other.base && !base)
+ {
+ result = 1;
+ }
+ }
+ return result;
+ }
+ }
+}
Property changes on: trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/AbstractContentModificationGenerator.java
___________________________________________________________________
Name: svn:keywords
+
Added: trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/AbstractSynchronizationAction.java
===================================================================
--- trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/AbstractSynchronizationAction.java (rev 0)
+++ trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/AbstractSynchronizationAction.java 2009-03-31 22:10:16 UTC (rev 86553)
@@ -0,0 +1,235 @@
+/*
+ * 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 org.jboss.logging.Logger;
+import org.jboss.system.server.profileservice.repository.clustered.metadata.RepositoryItemMetadata;
+
+/**
+ * Abstract superclass of {@link SynchronizationAction}
+ * implementations.
+ *
+ * @author Brian Stansberry
+ *
+ * @version $Revision: $
+ */
+public abstract class AbstractSynchronizationAction<T extends SynchronizationActionContext>
+ implements TwoPhaseCommitAction<T>
+{
+ public enum State { OPEN, CANCELLED, CLOSED, PREPARED, COMMITTED, ROLLEDBACK, ROLLBACK_ONLY}
+
+ private Logger log = Logger.getLogger(getClass());
+
+ private final ContentModification modification;
+ private final T context;
+ private boolean cancelled = false;
+ private boolean complete = false;
+ private State state = State.OPEN;
+
+ /**
+ * Create a new AbstractSynchronizationAction.
+ *
+ * @param context the overall context of the modification
+ * @param modification the modification
+ */
+ protected AbstractSynchronizationAction(T context,
+ ContentModification modification)
+ {
+ if (context == null)
+ {
+ throw new IllegalArgumentException("Null context");
+ }
+ if (modification == null)
+ {
+ throw new IllegalArgumentException("Null modification");
+ }
+ this.context = context;
+ this.modification = modification;
+ }
+
+ public ContentModification getRepositoryContentModification()
+ {
+ return modification;
+ }
+
+ public T getContext()
+ {
+ return context;
+ }
+
+ public void cancel()
+ {
+ if (state == State.OPEN)
+ {
+ doCancel();
+ this.cancelled = true;
+ this.state = State.CANCELLED;
+ }
+ }
+
+ public void complete()
+ {
+ if (state == State.OPEN)
+ {
+ try
+ {
+ doComplete();
+ this.state = State.CLOSED;
+ }
+ catch (Exception e)
+ {
+ this.state = State.ROLLBACK_ONLY;
+ }
+ finally
+ {
+ this.complete = true;
+ }
+ }
+ }
+
+ public boolean prepare()
+ {
+ boolean result = false;
+ switch (state)
+ {
+ case OPEN:
+ // Not all actions get executed; e.g. reads on nodes that don't
+ // get called. So we'll clean up.
+ complete();
+ if (state != State.CLOSED)
+ {
+ // break and return false
+ break;
+ }
+ // else fall through
+ case CLOSED:
+ result = doPrepare();
+ if (result)
+ {
+ state = State.PREPARED;
+ result = true;
+ }
+ else
+ {
+ state = State.ROLLBACK_ONLY;
+ }
+ break;
+ case PREPARED:
+ case COMMITTED:
+ case ROLLEDBACK:
+ log.warn("Should not call prepare on an item with state " + state);
+ // fall through
+ case CANCELLED:
+ case ROLLBACK_ONLY:
+ // fall out and return false
+ break;
+ }
+ return result;
+ }
+
+
+ public void commit()
+ {
+ switch (state)
+ {
+ case PREPARED:
+ doCommit();
+ state = State.COMMITTED;
+ break;
+ case OPEN:
+ case CANCELLED:
+ case CLOSED:
+ case ROLLBACK_ONLY:
+ case COMMITTED:
+ case ROLLEDBACK:
+ log.warn("Should not call prepare on an item with state " + state);
+ break;
+ }
+ }
+
+ public void rollback()
+ {
+ switch (state)
+ {
+ case COMMITTED:
+ case ROLLEDBACK:
+ log.warn("Should not call prepare on an item with state " + state);
+ return;
+ case OPEN:
+ doRollbackFromOpen();
+ break;
+ case CANCELLED:
+ doRollbackFromCancelled();
+ break;
+ case ROLLBACK_ONLY:
+ doRollbackFromRollbackOnly();
+ break;
+ case CLOSED:
+ doRollbackFromComplete();
+ break;
+ case PREPARED:
+ doRollbackFromPrepared();
+ break;
+ }
+ state = State.ROLLEDBACK;
+ }
+
+ public boolean isCancelled()
+ {
+ return this.cancelled;
+ }
+
+ public boolean isComplete()
+ {
+ return this.complete;
+ }
+
+ public State getState()
+ {
+ return state;
+ }
+
+ // -------------------------------------------------------------- Protected
+
+ protected abstract void doCancel();
+ protected abstract void doComplete() throws Exception;
+ protected abstract boolean doPrepare();
+ protected abstract void doCommit();
+ protected abstract void doRollbackFromOpen();
+ protected abstract void doRollbackFromCancelled();
+ protected abstract void doRollbackFromRollbackOnly();
+ protected abstract void doRollbackFromComplete();
+ protected abstract void doRollbackFromPrepared();
+
+ protected static RepositoryItemMetadata getMarkedRemovedItem(ContentModification base)
+ {
+ RepositoryItemMetadata result = base.getItem();
+ if (result.isRemoved() == false)
+ {
+ result = new RepositoryItemMetadata(result);
+ result.setRemoved(true);
+ }
+
+ return result;
+ }
+
+}
\ No newline at end of file
Property changes on: trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/AbstractSynchronizationAction.java
___________________________________________________________________
Name: svn:keywords
+
Added: trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/AbstractSynchronizationPolicy.java
===================================================================
--- trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/AbstractSynchronizationPolicy.java (rev 0)
+++ trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/AbstractSynchronizationPolicy.java 2009-03-31 22:10:16 UTC (rev 86553)
@@ -0,0 +1,507 @@
+/*
+ * 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 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 of 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 (RepositoryItemMetadata rimd : rrmd.getContent())
+ {
+ if (rimd.isRemoved() && rimd.getTimestamp() < oldest)
+ {
+ rrmd.removeItemMetadata(rimd.getRelativePathElements());
+ 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");
+ }
+ }
+
+}
Property changes on: trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/AbstractSynchronizationPolicy.java
___________________________________________________________________
Name: svn:keywords
+
Added: trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/ByteChunk.java
===================================================================
--- trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/ByteChunk.java (rev 0)
+++ trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/ByteChunk.java 2009-03-31 22:10:16 UTC (rev 86553)
@@ -0,0 +1,106 @@
+/*
+ * 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.io.Serializable;
+
+/**
+ * Encapsulates the results of an IO read operation for transmission
+ * across the cluster.
+ *
+ *
+ * @author Brian Stansberry
+ *
+ * @version $Revision: $
+ */
+public class ByteChunk implements Serializable
+{
+ /** The serialVersionUID */
+ private static final long serialVersionUID = -1278778998152090786L;
+
+ private final int byteCount;
+ private final byte[] bytes;
+
+ /**
+ * Create a new ByteChunk.
+ *
+ * @param bytes source bytes. Note that this array will not necessarily
+ * be defensively copied, so callers should not alter it
+ * after passing it to this object.
+ * @param byteCount number of bytes in <code>bytes</code> that are valid;
+ * rest are filler. <code>-1</code> indicates end of stream.
+ */
+ public ByteChunk(byte[] bytes, int byteCount)
+ {
+ if (bytes == null)
+ {
+ throw new IllegalArgumentException("Null bytes");
+ }
+ if (byteCount < 0)
+ {
+ this.byteCount = -1;
+ this.bytes = null;
+ }
+ else
+ {
+ this.byteCount = byteCount;
+ int diff = bytes.length - byteCount;
+ // If we're wasting too much, discard unneeded bytes at the
+ // cost of a copy. TODO refine or discard this
+ if (diff > (10 * 1024) && diff > (bytes.length / 4))
+ {
+ this.bytes = new byte[byteCount];
+ System.arraycopy(bytes, 0, this.bytes, 0, byteCount);
+ }
+ else
+ {
+ this.bytes = bytes;
+ }
+ }
+ }
+
+ /**
+ * Gets the number of valid bytes.
+ *
+ * @return the number of valid bytes, or <code>-1</code> to indicate end of stream
+ */
+ public int getByteCount()
+ {
+ return byteCount;
+ }
+
+ /**
+ * The bytes contained by this chunk. The length of the array could be
+ * longer than {@link #getByteCount()} in which case the excess bytes
+ * are filler.
+ *
+ * @return the bytes, or <code>null</code> if {@link #getByteCount()} would
+ * return <code>-1</code>. Not that this may be a direct reference
+ * to this object's internal byte buffer, so callers should not
+ * alter the byte array.
+ */
+ public byte[] getBytes()
+ {
+ return this.bytes;
+ }
+}
\ No newline at end of file
Property changes on: trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/ByteChunk.java
___________________________________________________________________
Name: svn:keywords
+
Added: trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/ContentModification.java
===================================================================
--- trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/ContentModification.java (rev 0)
+++ trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/ContentModification.java 2009-03-31 22:10:16 UTC (rev 86553)
@@ -0,0 +1,133 @@
+/*
+ * 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.io.Serializable;
+
+import org.jboss.system.server.profileservice.repository.clustered.metadata.RepositoryItemMetadata;
+
+/**
+ * Describes a modification that a node needs to make to synchronize
+ * its repository with the cluster.
+ *
+ * @author Brian Stansberry
+ *
+ * @version $Revision:$
+ */
+public class ContentModification implements Serializable
+{
+ /** The serialVersionUID */
+ private static final long serialVersionUID = -9060367262266987206L;
+
+ public enum Type
+ {
+ /**
+ * This node needs to pull the item from the cluster and store it locally.
+ */
+ PULL_FROM_CLUSTER,
+ /** This node it needs to remove this item locally. */
+ REMOVE_FROM_CLUSTER,
+ /** This node needs to push the item to the cluster. */
+ PUSH_TO_CLUSTER,
+ /** This node needs to push the item to the cluster. */
+ PUSH_INPUT_STREAM_TO_CLUSTER,
+ /** This node needs to tell the cluster to remove the item. */
+ REMOVE_TO_CLUSTER,
+ /** This node needs to make a new directory */
+ MKDIR_FROM_CLUSTER,
+ /** This node needs to tell the cluster to make a new directory */
+ MKDIR_TO_CLUSTER,
+ /**
+ * This node needs to tell the cluster to prepare to remove a directory.
+ * The actual removal will come later, via a REMOVE_TO_CLUSTER, after
+ * all directory children are removed as well. The "prepare" modification
+ * allows the remote nodes to set up a {@link RepositorySynchronizationAction}
+ * to rollback the directory removal.
+ */
+ PREPARE_RMDIR_TO_CLUSTER,
+ /**
+ * This node needs to prepare to remove a directory.
+ * The actual removal will come later, via a REMOVE_FROM_CLUSTER, after
+ * all directory children are removed as well. The "prepare" modification
+ * allows the node to set up a {@link RepositorySynchronizationAction}
+ * to rollback the directory removal.
+ */
+ PREPARE_RMDIR_FROM_CLUSTER,
+ /**
+ * This node's directory with the same path as the related
+ * RepositoryItemMetadata has a different timestamp. What if anything
+ * the node should do about this is unspecified. The
+ * RepositoryItemMetadata included in this RepositoryContentModification
+ * will be the cluster version, as this node should know its own version.
+ */
+ DIR_TIMESTAMP_MISMATCH,
+ /**
+ * This node needs to add some metadata for an item that has been removed.
+ * Used when this node doesn't have the item either, but is lacking
+ * information on when it was removed. The
+ * RepositoryItemMetadata included in this RepositoryContentModification
+ * will return <code>true</code> to {@link RepositoryItemMetadata#isRemoved()}.
+ */
+ REMOVAL_METADATA_FROM_CLUSTER
+ }
+
+ private final Type type;
+ private final String rootName;
+ private final RepositoryItemMetadata item;
+
+ public ContentModification(Type type, String rootName, RepositoryItemMetadata item)
+ {
+ if (type == null)
+ {
+ throw new IllegalArgumentException("Null type");
+ }
+ if (rootName == null)
+ {
+ throw new IllegalArgumentException("Null rootName");
+ }
+ if (item == null)
+ {
+ throw new IllegalArgumentException("Null item");
+ }
+
+ this.type = type;
+ this.rootName = rootName;
+ this.item = item;
+ }
+
+ public Type getType()
+ {
+ return type;
+ }
+
+ public String getRootName()
+ {
+ return rootName;
+ }
+
+ public RepositoryItemMetadata getItem()
+ {
+ return item;
+ }
+
+}
Property changes on: trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/ContentModification.java
___________________________________________________________________
Name: svn:keywords
+
Added: trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/ImmutableSynchronizationPolicy.java
===================================================================
--- trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/ImmutableSynchronizationPolicy.java (rev 0)
+++ trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/ImmutableSynchronizationPolicy.java 2009-03-31 22:10:16 UTC (rev 86553)
@@ -0,0 +1,145 @@
+/*
+ * 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 org.jboss.system.server.profileservice.repository.clustered.metadata.RepositoryContentMetadata;
+import org.jboss.system.server.profileservice.repository.clustered.metadata.RepositoryItemMetadata;
+
+/**
+ * {@link SynchronizationPolicy} that does not accept any changes.
+ *
+ * @author Brian Stansberry
+ *
+ * @version $Revision: $
+ */
+public class ImmutableSynchronizationPolicy implements SynchronizationPolicy
+{
+ /**
+ * Always returns <code>false</code>
+ *
+ * {@inheritDoc}
+ *
+ * @return <code>false</code>
+ * */
+ public boolean acceptJoinAddition(RepositoryItemMetadata toAdd, RepositoryItemMetadata joinersPrevious)
+ {
+ return false;
+ }
+
+ /**
+ * Always returns <code>false</code>
+ *
+ * {@inheritDoc}
+ *
+ * @return <code>false</code>
+ * */
+ public boolean acceptJoinReincarnation(RepositoryItemMetadata reincarnation, RepositoryItemMetadata current)
+ {
+ return false;
+ }
+
+ /**
+ * Always returns <code>false</code>
+ *
+ * {@inheritDoc}
+ *
+ * @return <code>false</code>
+ * */
+ public boolean acceptJoinRemoval(RepositoryItemMetadata current, RepositoryItemMetadata joinersItem)
+ {
+ return false;
+ }
+
+ /**
+ * Always returns <code>false</code>
+ *
+ * {@inheritDoc}
+ *
+ * @return <code>false</code>
+ * */
+ public boolean acceptJoinUpdate(RepositoryItemMetadata update, RepositoryItemMetadata current)
+ {
+ return false;
+ }
+
+ /**
+ * Always returns <code>false</code>
+ *
+ * {@inheritDoc}
+ *
+ * @return <code>false</code>
+ * */
+ public boolean acceptMergeAddition(RepositoryItemMetadata toAdd)
+ {
+ return false;
+ }
+
+ /**
+ * Always returns <code>false</code>
+ *
+ * {@inheritDoc}
+ *
+ * @return <code>false</code>
+ * */
+ public boolean acceptMergeReincarnation(RepositoryItemMetadata reincarnation, RepositoryItemMetadata current)
+ {
+ return false;
+ }
+
+ /**
+ * Always returns <code>false</code>
+ *
+ * {@inheritDoc}
+ *
+ * @return <code>false</code>
+ * */
+ public boolean acceptMergeRemoval(RepositoryItemMetadata current, RepositoryItemMetadata mergersView)
+ {
+ return false;
+ }
+
+ /**
+ * Always returns <code>false</code>
+ *
+ * {@inheritDoc}
+ *
+ * @return <code>false</code>
+ * */
+ public boolean acceptMergeUpdate(RepositoryItemMetadata update, RepositoryItemMetadata current)
+ {
+ return false;
+ }
+
+ /**
+ * Always returns <code>false</code>
+ *
+ * {@inheritDoc}
+ *
+ * @return <code>false</code>
+ * */
+ public boolean purgeRemovedItems(RepositoryContentMetadata content)
+ {
+ return false;
+ }
+
+}
Property changes on: trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/ImmutableSynchronizationPolicy.java
___________________________________________________________________
Name: svn:keywords
+
Added: trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/InconsistentRepositoryStructureException.java
===================================================================
--- trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/InconsistentRepositoryStructureException.java (rev 0)
+++ trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/InconsistentRepositoryStructureException.java 2009-03-31 22:10:16 UTC (rev 86553)
@@ -0,0 +1,114 @@
+/*
+ * 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.ArrayList;
+import java.util.List;
+
+import org.jboss.system.server.profileservice.repository.clustered.metadata.RepositoryContentMetadata;
+import org.jboss.system.server.profileservice.repository.clustered.metadata.RepositoryRootMetadata;
+
+
+/**
+ * Exception indicating that
+ * {@link RepositoryContentMetadata#getRepositories() the list of repository roots}
+ * is inconsistent between two <code>RepositoryContentMetadata</code> objects.
+ * The expected cause of this would be two repositories set up with incompatible
+ * sets of backing URIs.
+ *
+ * @author Brian Stansberry
+ *
+ * @version $Revision: $
+ */
+public class InconsistentRepositoryStructureException extends Exception
+{
+ /** The serialVersionUID */
+ private static final long serialVersionUID = 8104994059142991240L;
+
+ public InconsistentRepositoryStructureException(RepositoryContentMetadata base,
+ RepositoryContentMetadata other)
+ {
+ super(buildExceptionMessage(base, other));
+ }
+
+ private static String buildExceptionMessage(RepositoryContentMetadata base, RepositoryContentMetadata other)
+ {
+ List<String> missing = new ArrayList<String>();
+ for (RepositoryRootMetadata root : base.getRepositories())
+ {
+ RepositoryRootMetadata otherRoot = other.getRepositoryRootMetadata(root.getName());
+ if (otherRoot == null)
+ {
+ missing.add(root.getName());
+ }
+ }
+ List<String> extra = new ArrayList<String>();
+ for (RepositoryRootMetadata root : other.getRepositories())
+ {
+ RepositoryRootMetadata baseRoot = base.getRepositoryRootMetadata(root.getName());
+ if (baseRoot == null)
+ {
+ extra.add(root.getName());
+ }
+ }
+
+ StringBuilder sb = new StringBuilder("Inconsistent structure between repositories");
+ if (missing.size() > 0)
+ {
+ sb.append("; Other repository is missing roots ");
+ boolean first = true;
+ for (String name : missing)
+ {
+ if (!first)
+ {
+ sb.append(',');
+ }
+ else
+ {
+ first = false;
+ }
+ sb.append(name);
+ }
+ }
+ if (extra.size() > 0)
+ {
+ sb.append("; Other repository has extra roots ");
+ boolean first = true;
+ for (String name : extra)
+ {
+ if (!first)
+ {
+ sb.append(',');
+ }
+ else
+ {
+ first = false;
+ }
+ sb.append(name);
+ }
+
+ }
+
+ return sb.toString();
+ }
+}
Property changes on: trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/InconsistentRepositoryStructureException.java
___________________________________________________________________
Name: svn:keywords
+
Added: trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/LocalContentModificationGenerator.java
===================================================================
--- trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/LocalContentModificationGenerator.java (rev 0)
+++ trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/LocalContentModificationGenerator.java 2009-03-31 22:10:16 UTC (rev 86553)
@@ -0,0 +1,172 @@
+/*
+ * 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 org.jboss.system.server.profileservice.repository.clustered.metadata.RepositoryItemMetadata;
+import org.jboss.system.server.profileservice.repository.clustered.sync.ContentModification.Type;
+
+
+/**
+ * Generates {@link ContentModification} object from a comparison
+ * of a current snapshot of local repository content to a official snapshot.
+ * This generator will only generate modifications that push content to the
+ * cluster or that tell the cluster to remove content; no modifications
+ * pulling content from the cluster or removing local content will be
+ * generated.
+ * <p>This generator should only be used when the node has been fully
+ * synchronized with the cluster; it assumes any changes are acceptable
+ * to the cluster.</p>
+ *
+ * @author Brian Stansberry
+ *
+ * @version $Revision: $
+ */
+public class LocalContentModificationGenerator extends AbstractContentModificationGenerator
+{
+
+ @Override
+ protected void handleAddition(String rootName,
+ RepositoryItemMetadata item, GeneratedModifications mods)
+ {
+ // If this is an addition, it can't be the child of an earlier removal.
+ // So, drain any remaining preapproved removals
+ drainPreapprovedRemovals(mods);
+ Type type = item.isDirectory() ? Type.MKDIR_TO_CLUSTER : Type.PUSH_TO_CLUSTER;
+ mods.addModification(new ContentModification(type, rootName, item));
+ }
+
+// @Override
+// protected void handleMatch(String rootName,
+// RepositoryItemMetadata base, RepositoryItemMetadata modified, GeneratedModifications mods)
+// {
+// drainPreapprovedRemovals(mods);
+// if (base.equals(modified) == false)
+// {
+// if (base.isDirectory() != modified.isDirectory())
+// {
+// // Swapped exploded for zipped or vice versa
+// if (!base.isRemoved())
+// {
+// mods.addModification(new ContentModification(Type.REMOVE_TO_CLUSTER, rootName, base));
+// }
+// }
+// mods.addModification(new ContentModification(Type.PUSH_TO_CLUSTER, rootName, modified));
+// }
+// }
+
+ @Override
+ protected void handleMissing(String rootName,
+ RepositoryItemMetadata item, GeneratedModifications mods)
+ {
+ if (!item.isRemoved())
+ {
+ handleRemoval(rootName, item, mods);
+ }
+ // else it's the brain-dead case where we have metadata recording the
+ // removal but no file -- 'cause it's removed! In which case there's no
+ // need to do anything.
+ }
+
+ @Override
+ protected void handleChangeToDirectory(String rootName, RepositoryItemMetadata base, RepositoryItemMetadata modified,
+ GeneratedModifications mods)
+ {
+ // This is a modification, so it can't be the child of an earlier removal.
+ // So, drain any remaining preapproved removals
+ drainPreapprovedRemovals(mods);
+
+ mods.addModification(new ContentModification(Type.MKDIR_TO_CLUSTER, rootName, modified));
+ }
+
+ @Override
+ protected void handleAddition(String rootName, RepositoryItemMetadata modified, RepositoryItemMetadata base,
+ GeneratedModifications mods)
+ {
+ handleAddition(rootName, modified, mods);
+ }
+
+ @Override
+ protected void handleChangeFromDirectory(String rootName, RepositoryItemMetadata base,
+ RepositoryItemMetadata modified, GeneratedModifications mods)
+ {
+ // This is a modification, so it can't be the child of an earlier removal.
+ // So, drain any remaining preapproved removals
+ drainPreapprovedRemovals(mods);
+
+ // We're going to need to remove all the content that was
+ // under "base" from all the nodes in the cluster before we
+ // can push the replacement file. So, we put the PUSH_TO_CLUSTER
+ // on the stack to be inserted once all the child removals
+ // are done.
+ ContentModification mod = new ContentModification(Type.PUSH_TO_CLUSTER, rootName, modified);
+ mods.pushPreapprovedRemoveParent(mod);
+
+ }
+
+ @Override
+ protected void handleDirectoryTimestampModification(String rootName, RepositoryItemMetadata base,
+ RepositoryItemMetadata modified, GeneratedModifications mods)
+ {
+ // If this is a modification, it can't be the child of an earlier removal.
+ // So, drain any remaining preapproved removals
+ drainPreapprovedRemovals(mods);
+
+ mods.addModification(new ContentModification(Type.DIR_TIMESTAMP_MISMATCH, rootName, base));
+ }
+
+ @Override
+ protected void handleSimpleModification(String rootName, RepositoryItemMetadata base, RepositoryItemMetadata modified,
+ GeneratedModifications mods)
+ {
+ // If this is a modification, it can't be the child of an earlier removal.
+ // So, drain any remaining preapproved removals
+ drainPreapprovedRemovals(mods);
+
+ mods.addModification(new ContentModification(Type.PUSH_TO_CLUSTER, rootName, modified));
+ }
+
+ @Override
+ protected void handleRemoval(String rootName, RepositoryItemMetadata item, GeneratedModifications mods)
+ {
+ ContentModification removedParent = mods.peekPreapprovedRemoveParent();
+ while (removedParent != null && item.isChildOf(removedParent.getItem()) == false)
+ {
+ mods.addModification(mods.popPreapprovedRemoveParent());
+ removedParent = mods.peekPreapprovedRemoveParent();
+ }
+ ContentModification removal = new ContentModification(Type.REMOVE_TO_CLUSTER, rootName, item);
+ if (item.isDirectory())
+ {
+ // Tell cluster to prepare for the removal
+ mods.addModification(new ContentModification(Type.PREPARE_RMDIR_TO_CLUSTER, rootName, item));
+ // Push the actual removal on the stack to execute when
+ // children are done
+ mods.pushPreapprovedRemoveParent(removal);
+ }
+ else
+ {
+ mods.addModification(removal);
+ }
+ }
+
+}
Property changes on: trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/LocalContentModificationGenerator.java
___________________________________________________________________
Name: svn:keywords
+
Added: trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/NoOpRepositorySynchronizationWriteAction.java
===================================================================
--- trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/NoOpRepositorySynchronizationWriteAction.java (rev 0)
+++ trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/NoOpRepositorySynchronizationWriteAction.java 2009-03-31 22:10:16 UTC (rev 86553)
@@ -0,0 +1,57 @@
+/*
+ * 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;
+
+
+
+/**
+ * A {@link SynchronizationWriteAction} that throws the bytes on the
+ * floor.
+ *
+ * @author Brian Stansberry
+ *
+ * @version $Revision: $
+ */
+public class NoOpRepositorySynchronizationWriteAction<T extends SynchronizationActionContext>
+ extends NoOpSynchronizationAction<T>
+ implements SynchronizationWriteAction<T>
+{
+
+ /**
+ * Create a new NoOpRepositorySynchronizationWriteAction.
+ *
+ * @param synchronizationId
+ * @param modification
+ */
+ public NoOpRepositorySynchronizationWriteAction(T context,
+ ContentModification modification)
+ {
+ super(context, modification);
+ }
+
+ public void writeBytes(ByteChunk bytes)
+ {
+ // no-op
+ }
+
+}
Property changes on: trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/NoOpRepositorySynchronizationWriteAction.java
___________________________________________________________________
Name: svn:keywords
+
Added: trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/NoOpSynchronizationAction.java
===================================================================
--- trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/NoOpSynchronizationAction.java (rev 0)
+++ trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/NoOpSynchronizationAction.java 2009-03-31 22:10:16 UTC (rev 86553)
@@ -0,0 +1,102 @@
+/*
+ * 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;
+
+
+/**
+ * {@link SynchronizationAction} that does nothing. Intended
+ * for use in cases where a node is already in sync with the cluster with
+ * respect to a particular item.
+ *
+ * @author Brian Stansberry
+ *
+ * @version $Revision: $
+ */
+public class NoOpSynchronizationAction<T extends SynchronizationActionContext>
+ extends AbstractSynchronizationAction<T>
+{
+ public NoOpSynchronizationAction(T context,
+ ContentModification modification)
+ {
+ super(context, modification);
+ }
+
+ // -------------------------------------------------------------- Protected
+
+ @Override
+ protected void doCancel()
+ {
+ // no-op
+ }
+
+ @Override
+ protected void doComplete()
+ {
+ // no-op
+ }
+
+ @Override
+ protected boolean doPrepare()
+ {
+ return true;
+ }
+
+ @Override
+ protected void doCommit()
+ {
+ // no-op
+ }
+
+ @Override
+ protected void doRollbackFromCancelled()
+ {
+ // no-op
+ }
+
+ @Override
+ protected void doRollbackFromComplete()
+ {
+ // no-op
+ }
+
+ @Override
+ protected void doRollbackFromOpen()
+ {
+ // no-op
+ }
+
+ @Override
+ protected void doRollbackFromPrepared()
+ {
+ // no-op
+ }
+
+ @Override
+ protected void doRollbackFromRollbackOnly()
+ {
+ // no-op
+ }
+
+
+
+}
Property changes on: trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/NoOpSynchronizationAction.java
___________________________________________________________________
Name: svn:keywords
+
Added: trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/RemoteContentModificationGenerator.java
===================================================================
--- trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/RemoteContentModificationGenerator.java (rev 0)
+++ trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/RemoteContentModificationGenerator.java 2009-03-31 22:10:16 UTC (rev 86553)
@@ -0,0 +1,526 @@
+/*
+ * 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 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;
+import org.jboss.system.server.profileservice.repository.clustered.sync.ContentModification.Type;
+
+/**
+ * Generates {@link ContentModification} objects from a comparison
+ * of a current snapshot of a remote node's repository content to the official
+ * snapshot available on this node. Intended for use when a new node joins the
+ * cluster or a split of the cluster heals.
+ *
+ * @author Brian Stansberry
+ *
+ * @version $Revision: $
+ */
+public class RemoteContentModificationGenerator extends AbstractContentModificationGenerator
+{
+ private final SynchronizationPolicy policy;
+ private final RepositoryContentMetadata baseRemoteContent;
+ private final boolean merge;
+
+ /**
+ * Create a new RemoteRepositoryContentModificationGenerator to handle
+ * a cluster merge.
+ *
+ * @param policy policy that decides whether to accept or reject changes
+ * from the remote repository
+ */
+ public RemoteContentModificationGenerator(SynchronizationPolicy policy)
+ {
+ if (policy == null)
+ {
+ throw new IllegalArgumentException("Null policy");
+ }
+
+ this.policy = policy;
+ this.baseRemoteContent = null;
+ this.merge = true;
+ }
+
+ /**
+ * Create a new RemoteRepositoryContentModificationGenerator to handle a
+ * cluster node join.
+ *
+ * @param policy policy that decides whether to accept or reject changes
+ * from the remote repository
+ * @param baseRemoteContent remote node's view of its content as of the
+ * last time it was part of the cluster, or
+ * <code>null</code> if the node was never part
+ * of the cluster.
+ */
+ public RemoteContentModificationGenerator(SynchronizationPolicy policy,
+ RepositoryContentMetadata baseRemoteContent)
+ {
+ if (policy == null)
+ {
+ throw new IllegalArgumentException("Null policy");
+ }
+ if (baseRemoteContent == null)
+ {
+ throw new IllegalArgumentException("Null baseRemoteContent");
+ }
+
+ this.policy = policy;
+ this.baseRemoteContent = baseRemoteContent;
+ this.merge = false;
+ }
+
+ // ----------------------------------------------------- Protected Overrides
+
+ @Override
+ protected void handleAddition(String rootName,
+ RepositoryItemMetadata item,
+ GeneratedModifications mods)
+ {
+ handleAddition(rootName, item, null, mods);
+ }
+
+ @Override
+ protected void handleMissing(String rootName, RepositoryItemMetadata item,
+ GeneratedModifications mods)
+ {
+ if (item.isRemoved())
+ {
+ // Remote node doesn't have file, just needs to add a missing RepositoryItemMetadata
+ handleRemovalMetadata(rootName, item, mods);
+ }
+ else
+ {
+ handleRemoval(rootName, item, mods);
+ }
+ }
+
+ @Override
+ protected void handleAddition(String rootName,
+ RepositoryItemMetadata item, RepositoryItemMetadata removedVersion,
+ GeneratedModifications mods)
+ {
+ // This is an add, so it can't be the child of an earlier removal.
+ // So, drain any remaining preapproved removals
+ drainPreapprovedRemovals(mods);
+ mods.setPrerejectedRemoveParent(null);
+
+ Boolean allow = null;
+
+ // See if this addition is preapproved as part of adding a new tree
+ RepositoryItemMetadata preapprovedAdd = mods.getPreapprovedAddParent();
+ if (preapprovedAdd != null)
+ {
+ if (item.isChildOf(preapprovedAdd))
+ {
+ // just keep the same one
+ allow = Boolean.TRUE;
+ }
+ else
+ {
+ // Clean up
+ mods.setPreapprovedAddParent(null);
+ }
+ }
+ else
+ {
+ // Addition wasn't preapproved. See if it was prerejected as part
+ // of rejecting adding a new tree
+
+ ContentModification prerejectedParentMod = mods.peekPrerejectedAddParent();
+ while (prerejectedParentMod != null)
+ {
+ if (item.isChildOf(prerejectedParentMod.getItem()))
+ {
+ // rejected
+ allow = Boolean.FALSE;
+ break;
+ }
+ else
+ {
+ // We're done with children of prerejected parent so add the cached
+ // modification to the overall list. This will cause it to
+ // get executed *after* its children
+ // (i.e. remove parent after removing children)
+ mods.addModification(mods.popPrerejectedAddParent());
+ // Start checking grandparent
+ prerejectedParentMod = mods.peekPrerejectedAddParent();
+ }
+ }
+ }
+
+ if (allow == null)
+ {
+ // Check with our policy
+ allow = Boolean.valueOf(isAdditionApproved(rootName, item, removedVersion));
+ }
+
+ if (allow.booleanValue())
+ {
+ Type type = item.isDirectory() ? Type.MKDIR_TO_CLUSTER : Type.PUSH_TO_CLUSTER;
+ mods.addModification(new ContentModification(type, rootName, item));
+
+ if (mods.getPreapprovedAddParent() == null && item.isDirectory())
+ {
+ mods.setPreapprovedAddParent(item);
+ }
+ }
+ else
+ {
+ // Addition not allowed; remote node must discard
+
+ // If available use the removedVersion in ContentModifications to
+ // help keep it around in metadata
+ RepositoryItemMetadata modItem = removedVersion == null ? item : removedVersion;
+ ContentModification mod = new ContentModification(Type.REMOVE_FROM_CLUSTER, rootName, modItem);
+ if (item.isDirectory())
+ {
+ // Tell node to prepare for the removal
+ mods.addModification(new ContentModification(Type.PREPARE_RMDIR_FROM_CLUSTER, rootName, modItem));
+ // Push it on the stack so we execute it after dealing with
+ // all the children we will now reject as well
+ mods.pushPrerejectedAddParent(mod);
+ }
+ else
+ {
+ mods.addModification(mod);
+ }
+ // housekeeping
+ mods.setPreapprovedAddParent(null);
+ }
+ }
+
+ @Override
+ protected void handleChangeFromDirectory(String rootName,
+ RepositoryItemMetadata base,
+ RepositoryItemMetadata modified,
+ GeneratedModifications mods)
+ {
+ // This is a modification, so it can't be the child of an earlier removal.
+ // So, drain any remaining preapproved removals
+ drainPreapprovedRemovals(mods);
+ mods.setPrerejectedRemoveParent(null);
+ // Also can't be child of an earlier add, so drain any remaining prerejected adds
+ drainPrerejectedAdds(mods);
+ mods.setPreapprovedAddParent(null);
+
+ boolean allow = checkAllowUpdate(base, modified);
+ if (allow)
+ {
+ // We're going to need to remove all the content that was
+ // under "base" from all the nodes in the cluster before we
+ // can push the replacement file. So, we put the PUSH_TO_CLUSTER
+ // on the stack to be inserted once all the child removals
+ // are done.
+ ContentModification mod = new ContentModification(Type.PUSH_TO_CLUSTER, rootName, modified);
+ mods.pushPreapprovedRemoveParent(mod);
+ }
+ else
+ {
+ // We're going to need to also reject the removal of all the content
+ // that is under "base".
+ mods.setPrerejectedRemoveParent(base);
+ mods.addModification(new ContentModification(Type.MKDIR_FROM_CLUSTER, rootName, base));
+ }
+ }
+
+ @Override
+ protected void handleChangeToDirectory(String rootName,
+ RepositoryItemMetadata base,
+ RepositoryItemMetadata modified,
+ GeneratedModifications mods)
+ {
+ // This is a modification, so it can't be the child of an earlier removal.
+ // So, drain any remaining preapproved removals
+ drainPreapprovedRemovals(mods);
+ mods.setPrerejectedRemoveParent(null);
+ // Also can't be child of an earlier add, so drain any remaining prerejected adds
+ drainPrerejectedAdds(mods);
+ mods.setPreapprovedAddParent(null);
+
+ boolean allow = checkAllowUpdate(base, modified);
+ if (allow)
+ {
+ // We're going to need to add all the content that is
+ // under "modified".
+ mods.setPreapprovedAddParent(modified);
+ mods.addModification(new ContentModification(Type.MKDIR_TO_CLUSTER, rootName, modified));
+ }
+ else
+ {
+ // We're going to need to also reject the addition of all the content
+ // that is under "modified".
+ ContentModification mod = new ContentModification(Type.PULL_FROM_CLUSTER, rootName, base);
+ mods.pushPrerejectedAddParent(mod);
+ }
+ }
+
+ private void handleRemovalMetadata(String rootName,
+ RepositoryItemMetadata item, GeneratedModifications mods)
+ {
+ // This is a removal, so it can't be a child of an earlier attempted add.
+ // So, drain any remaining prerejected adds
+ drainPrerejectedAdds(mods);
+ mods.setPreapprovedAddParent(null);
+
+ // Don't lose track of pre-approval stack if there is one
+ ContentModification preapprovedRemove = mods.peekPreapprovedRemoveParent();
+ while (preapprovedRemove != null)
+ {
+ if (item.isChildOf(preapprovedRemove.getItem()))
+ {
+ // we're at the right level
+ break;
+ }
+ else
+ {
+ // We're done with children of preapproved parent so add the cached
+ // modification to the overall list. This will cause it to
+ // get executed *after* its children
+ // (i.e. remove parent after removing children)
+ mods.addModification(mods.popPreapprovedRemoveParent());
+ // Start checking grandparent
+ preapprovedRemove = mods.peekPreapprovedRemoveParent();
+ }
+ }
+
+ ContentModification mod = new ContentModification(Type.REMOVAL_METADATA_FROM_CLUSTER, rootName, item);
+ if (item.isDirectory())
+ {
+ mods.pushPreapprovedRemoveParent(mod);
+ }
+ else
+ {
+ mods.addModification(mod);
+ }
+ }
+
+ @Override
+ protected void handleRemoval(String rootName, RepositoryItemMetadata item, GeneratedModifications mods)
+ {
+ // This is a removal, so it can't be a child of an earlier attempted add.
+ // So, drain any remaining prerejected adds
+ drainPrerejectedAdds(mods);
+ mods.setPreapprovedAddParent(null);
+
+ Boolean allow = null;
+
+ // See if this removal is prerejected as part of removing a higher level tree
+ RepositoryItemMetadata prerejected = mods.getPrerejectedRemoveParent();
+ if (prerejected != null)
+ {
+ if (item.isChildOf(prerejected))
+ {
+ allow = Boolean.FALSE;
+ }
+ else
+ {
+ // Clean up
+ mods.setPrerejectedRemoveParent(null);
+ }
+ }
+ else
+ {
+ // Removal wasn't prerejected. See if it was preapproved as part
+ // of approving removing a higher level tree
+ ContentModification preapprovedRemove = mods.peekPreapprovedRemoveParent();
+ while (preapprovedRemove != null)
+ {
+ if (item.isChildOf(preapprovedRemove.getItem()))
+ {
+ // approved
+ allow = Boolean.TRUE;
+ break;
+ }
+ else
+ {
+ // We're done with children of preapproved parent so add the cached
+ // modification to the overall list. This will cause it to
+ // get executed *after* its children
+ // (i.e. remove parent after removing children)
+ mods.addModification(mods.popPreapprovedRemoveParent());
+ // Start checking grandparent
+ preapprovedRemove = mods.peekPreapprovedRemoveParent();
+ }
+ }
+ }
+
+ if (allow == null)
+ {
+ // Check with our policy
+
+ if (merge)
+ {
+ allow = Boolean.valueOf(policy.acceptMergeRemoval(item, null));
+ }
+ else
+ {
+ // See if the base version of the remote node was aware of the
+ // item being removed
+ RepositoryItemMetadata baseRemoteItem = getBaseRemoteItem(rootName, item);
+ allow = Boolean.valueOf(policy.acceptJoinRemoval(item, baseRemoteItem));
+ }
+ }
+
+ if (allow)
+ {
+ ContentModification mod = new ContentModification(Type.REMOVE_TO_CLUSTER, rootName, item);
+
+ if (item.isDirectory())
+ {
+ // Tell cluster to prepare for the removal
+ mods.addModification(new ContentModification(Type.PREPARE_RMDIR_TO_CLUSTER, rootName, item));
+ // Push the actual removal on the stack to execute when
+ // children are done
+ mods.pushPreapprovedRemoveParent(mod);
+ }
+ else
+ {
+ mods.addModification(mod);
+ }
+ }
+ else
+ {
+ // Removal is rejected; tell remote node to pull ours
+ Type type = item.isDirectory() ? Type.MKDIR_FROM_CLUSTER : Type.PULL_FROM_CLUSTER;
+ mods.addModification(new ContentModification(type, rootName, item));
+
+ if (mods.getPrerejectedRemoveParent() == null && item.isDirectory())
+ {
+ mods.setPrerejectedRemoveParent(item);
+ }
+ }
+
+ }
+
+ @Override
+ protected void handleSimpleModification(String rootName, RepositoryItemMetadata base,
+ RepositoryItemMetadata modified, GeneratedModifications mods)
+ {
+ // If this is a modification, it can't be the child of an earlier removal.
+ // So, drain any remaining preapproved removals
+ drainPreapprovedRemovals(mods);
+ mods.setPrerejectedRemoveParent(null);
+ // Also can't be child of an earlier add, so drain any remaining prerejected adds
+ drainPrerejectedAdds(mods);
+ mods.setPreapprovedAddParent(null);
+
+ if (checkAllowUpdate(base, modified))
+ {
+ mods.addModification(new ContentModification(Type.PUSH_TO_CLUSTER, rootName, modified));
+ }
+ else
+ {
+ mods.addModification(new ContentModification(Type.PULL_FROM_CLUSTER, rootName, base));
+ }
+ }
+
+ @Override
+ protected void handleDirectoryTimestampModification(String rootName, RepositoryItemMetadata base,
+ RepositoryItemMetadata modified, GeneratedModifications mods)
+ {
+ // If this is a modification, it can't be the child of an earlier removal.
+ // So, drain any remaining preapproved removals
+ drainPreapprovedRemovals(mods);
+ mods.setPrerejectedRemoveParent(null);
+ // Also can't be child of an earlier add, so drain any remaining prerejected adds
+ drainPrerejectedAdds(mods);
+ mods.setPreapprovedAddParent(null);
+
+ mods.addModification(new ContentModification(Type.DIR_TIMESTAMP_MISMATCH, rootName, base));
+ }
+
+ // ---------------------------------------------------------------- Private
+
+ private boolean checkAllowUpdate(RepositoryItemMetadata current, RepositoryItemMetadata update)
+ {
+ boolean allow = false;
+ if (merge)
+ {
+ allow = policy.acceptMergeUpdate(update, current);
+ }
+ else
+ {
+ allow = policy.acceptJoinUpdate(update, current);
+ }
+ return allow;
+ }
+
+ private RepositoryItemMetadata getBaseRemoteItem(String rootName, RepositoryItemMetadata item)
+ {
+ RepositoryItemMetadata existingItem = null;
+ if (baseRemoteContent != null)
+ {
+ RepositoryRootMetadata existingRoot = baseRemoteContent.getRepositoryRootMetadata(rootName);
+ if (existingRoot != null)
+ {
+ existingItem = existingRoot.getItemMetadata(item.getRelativePathElements());
+ }
+ }
+ return existingItem;
+ }
+
+ private boolean isAdditionApproved(String rootName, RepositoryItemMetadata item,
+ RepositoryItemMetadata removedVersion)
+ {
+ boolean allow;
+
+ if (removedVersion == null)
+ {
+ if (merge)
+ {
+ allow = policy.acceptMergeAddition(item);
+ }
+ else
+ {
+ // See if the base version of the remote node was aware of the
+ // item being added
+ RepositoryItemMetadata baseRemoteItem = getBaseRemoteItem(rootName, item);
+ allow = Boolean.valueOf(policy.acceptJoinAddition(item, baseRemoteItem));
+ }
+ }
+ else
+ {
+ if (merge)
+ {
+ allow = policy.acceptMergeReincarnation(item, removedVersion);
+ }
+ else
+ {
+ allow = policy.acceptJoinReincarnation(item, removedVersion);
+ }
+ }
+
+ return allow;
+ }
+
+ private void drainPrerejectedAdds(GeneratedModifications mods)
+ {
+ ContentModification prerejectedAdd;
+ while ((prerejectedAdd = mods.popPrerejectedAddParent()) != null)
+ {
+ mods.addModification(prerejectedAdd);
+ }
+ }
+
+}
Property changes on: trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/RemoteContentModificationGenerator.java
___________________________________________________________________
Name: svn:keywords
+
Added: trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/RemoteRemovalAction.java
===================================================================
--- trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/RemoteRemovalAction.java (rev 0)
+++ trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/RemoteRemovalAction.java 2009-03-31 22:10:16 UTC (rev 86553)
@@ -0,0 +1,54 @@
+/*
+ * 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 org.jboss.system.server.profileservice.repository.clustered.metadata.RepositoryContentMetadata;
+import org.jboss.system.server.profileservice.repository.clustered.metadata.RepositoryRootMetadata;
+
+/**
+ * {@link SynchronizationRemoteAction} that updates the local metadata
+ * recording the removal.
+ *
+ * @author Brian Stansberry
+ *
+ * @version $Revision: $
+ */
+public class RemoteRemovalAction<T extends SynchronizationActionContext>
+ extends SimpleSynchronizationRemoteAction<T>
+{
+
+ public RemoteRemovalAction(T context, ContentModification modification)
+ {
+ super(context, modification, false);
+ }
+
+ @Override
+ protected void doCommit()
+ {
+ ContentModification mod = getRepositoryContentModification();
+ RepositoryContentMetadata contentMetadata = getContext().getInProgressMetadata();
+ RepositoryRootMetadata rmd = contentMetadata.getRepositoryRootMetadata(mod.getRootName());
+ rmd.addItemMetadata(getMarkedRemovedItem(mod));
+ }
+
+}
Property changes on: trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/RemoteRemovalAction.java
___________________________________________________________________
Name: svn:keywords
+
Added: trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/RemovalMetadataInsertionAction.java
===================================================================
--- trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/RemovalMetadataInsertionAction.java (rev 0)
+++ trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/RemovalMetadataInsertionAction.java 2009-03-31 22:10:16 UTC (rev 86553)
@@ -0,0 +1,107 @@
+/*
+ * 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 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;
+
+
+/**
+ * {@link SynchronizationAction} that modifies the node's metadata to insert
+ * a missing {@link RepositoryItemMetadata} that tracks a removed item.
+ *
+ * @author Brian Stansberry
+ *
+ * @version $Revision: $
+ */
+public class RemovalMetadataInsertionAction<T extends SynchronizationActionContext>
+ extends NoOpSynchronizationAction<T>
+{
+ private RepositoryItemMetadata replaced;
+
+ /**
+ * Create a new RemovalMetadataInsertionAction.
+ *
+ * @param synchronizationId
+ * @param modification
+ */
+ public RemovalMetadataInsertionAction(T context,
+ ContentModification modification)
+ {
+ super(context, modification);
+
+ if (modification.getItem().isRemoved() == false)
+ {
+ throw new IllegalArgumentException("Item " + modification.getItem() +
+ " is not marked as removed");
+ }
+ }
+
+ @Override
+ protected boolean doPrepare()
+ {
+ boolean ok = false;
+ ContentModification mod = getRepositoryContentModification();
+ RepositoryContentMetadata toUpdate = getContext().getInProgressMetadata();
+ RepositoryRootMetadata rmd = toUpdate.getRepositoryRootMetadata(mod.getRootName());
+ if (rmd != null)
+ {
+ replaced = rmd.getItemMetadata(mod.getItem().getRelativePathElements());
+ if (replaced == null)
+ {
+ rmd.addItemMetadata(mod.getItem());
+ ok = true;
+ }
+ }
+ return ok;
+ }
+
+ @Override
+ protected void doRollbackFromComplete()
+ {
+ if (replaced != null)
+ {
+ ContentModification mod = getRepositoryContentModification();
+ RepositoryContentMetadata toUpdate = getContext().getInProgressMetadata();
+ RepositoryRootMetadata rmd = toUpdate.getRepositoryRootMetadata(mod.getRootName());
+ if (rmd != null)
+ {
+ rmd.addItemMetadata(replaced);
+ }
+ }
+ }
+
+ @Override
+ protected void doRollbackFromPrepared()
+ {
+ ContentModification mod = getRepositoryContentModification();
+ RepositoryContentMetadata toUpdate = getContext().getInProgressMetadata();
+ RepositoryRootMetadata rmd = toUpdate.getRepositoryRootMetadata(mod.getRootName());
+ if (rmd != null)
+ {
+ rmd.removeItemMetadata(mod.getItem().getRelativePathElements());
+ }
+ }
+
+}
Property changes on: trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/RemovalMetadataInsertionAction.java
___________________________________________________________________
Name: svn:keywords
+
Added: trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/SimpleSynchronizationRemoteAction.java
===================================================================
--- trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/SimpleSynchronizationRemoteAction.java (rev 0)
+++ trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/SimpleSynchronizationRemoteAction.java 2009-03-31 22:10:16 UTC (rev 86553)
@@ -0,0 +1,67 @@
+/*
+ * 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;
+
+/**
+ * Simple implementation of marker interface {@link SynchronizationRemoteAction}.
+ *
+ * @author Brian Stansberry
+ *
+ * @version $Revision: $
+ */
+public class SimpleSynchronizationRemoteAction<T extends SynchronizationActionContext>
+ extends NoOpSynchronizationAction<T>
+ implements SynchronizationRemoteAction<T>
+{
+ private final boolean initiation;
+
+ /**
+ * Create a new NoOpRepositorySynchronizationRemoteAction.
+ *
+ * @param synchronizationId
+ * @param modification
+ */
+ public SimpleSynchronizationRemoteAction(T context,
+ ContentModification modification)
+ {
+ this(context, modification, false);
+ }
+
+ /**
+ * Create a new NoOpRepositorySynchronizationRemoteAction.
+ *
+ * @param synchronizationId
+ * @param modification
+ */
+ public SimpleSynchronizationRemoteAction(T context,
+ ContentModification modification, boolean initiation)
+ {
+ super(context, modification);
+ this.initiation = initiation;
+ }
+
+ public boolean isInitiation()
+ {
+ return initiation;
+ }
+}
Property changes on: trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/SimpleSynchronizationRemoteAction.java
___________________________________________________________________
Name: svn:keywords
+
Added: trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/StreamReadAction.java
===================================================================
--- trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/StreamReadAction.java (rev 0)
+++ trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/StreamReadAction.java 2009-03-31 22:10:16 UTC (rev 86553)
@@ -0,0 +1,169 @@
+/*
+ * 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.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.jboss.logging.Logger;
+
+/**
+ * {@link SynchronizationReadAction} that reads from a {@link File}.
+ *
+ * @author Brian Stansberry
+ *
+ * @version $Revision: $
+ */
+public class StreamReadAction<T extends SynchronizationActionContext> extends AbstractSynchronizationAction<T>
+ implements SynchronizationReadAction<T>
+{
+ private static final Logger log = Logger.getLogger(StreamReadAction.class);
+
+ /**
+ * Max file transfer buffer size that we read at a time.
+ * This influences the number of times that we will invoke disk read/write file
+ * operations versus how much memory we will consume for a file transfer.
+ */
+ public static final int MAX_CHUNK_BUFFER_SIZE = 512 * 1024;
+
+ private final InputStream stream;
+
+ /**
+ * Create a new StreamReadAction.
+ *
+ * @param stream the stream to read
+ * @param context the overall context of the modification
+ * @param modification the modification
+ */
+ public StreamReadAction(InputStream stream, T context,
+ ContentModification modification)
+ {
+ super(context, modification);
+ if (stream == null)
+ {
+ throw new IllegalArgumentException("Null stream");
+ }
+ this.stream = stream;
+ }
+
+ // ------------------------------------ RepositorySynchronizationReadAction
+
+ public ByteChunk getNextBytes() throws IOException
+ {
+ InputStream is = getInputStream();
+ byte[] b = null;
+ int read = -1;
+ synchronized (is)
+ {
+ b = new byte[MAX_CHUNK_BUFFER_SIZE];
+ read = is.read(b);
+ }
+ return new ByteChunk(b, read);
+ }
+
+ // -------------------------------------------------------------- Protected
+
+ @Override
+ protected void doCancel()
+ {
+ safeCloseStream();
+ }
+
+ @Override
+ protected void doCommit()
+ {
+ safeCloseStream();
+ }
+
+ @Override
+ protected void doComplete() throws Exception
+ {
+ safeCloseStream();
+ }
+
+ @Override
+ protected boolean doPrepare()
+ {
+ safeCloseStream();
+ return true;
+ }
+
+ @Override
+ protected void doRollbackFromCancelled()
+ {
+ safeCloseStream();
+ }
+
+ @Override
+ protected void doRollbackFromComplete()
+ {
+ safeCloseStream();
+ }
+
+ @Override
+ protected void doRollbackFromOpen()
+ {
+ safeCloseStream();
+ }
+
+ @Override
+ protected void doRollbackFromPrepared()
+ {
+ safeCloseStream();
+ }
+
+ @Override
+ protected void doRollbackFromRollbackOnly()
+ {
+ safeCloseStream();
+ }
+
+ private synchronized InputStream getInputStream() throws IOException
+ {
+ State s = getState();
+ if (s != State.OPEN && s != State.CANCELLED)
+ {
+ throw new IllegalStateException("Cannot read when state is " + s);
+ }
+ return stream;
+ }
+
+ private synchronized void safeCloseStream()
+ {
+ synchronized (stream)
+ {
+ try
+ {
+ stream.close();
+ }
+ catch (IOException e)
+ {
+ ContentModification mod = getRepositoryContentModification();
+ log.debug("Caught exception closing stream for " + mod.getRootName() +
+ " " + mod.getItem().getRelativePath(), e);
+ }
+ }
+ }
+
+}
Property changes on: trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/StreamReadAction.java
___________________________________________________________________
Name: svn:keywords
+
Added: trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/SynchronizationAction.java
===================================================================
--- trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/SynchronizationAction.java (rev 0)
+++ trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/SynchronizationAction.java 2009-03-31 22:10:16 UTC (rev 86553)
@@ -0,0 +1,74 @@
+/*
+ * 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;
+
+
+/**
+ * Encapsulates a single action needed to help synchronize the contents
+ * of one node's repository with the rest of the cluster.
+ *
+ * @author Brian Stansberry
+ *
+ * @version $Revision: $
+ */
+public interface SynchronizationAction<T extends SynchronizationActionContext>
+{
+ /**
+ * Gets the contextual information for the set of actions of which
+ * this object is a member.
+ *
+ * @return the context. Will not be <code>null</code>
+ */
+ T getContext();
+
+ /**
+ * Gets the metadata describing this action.
+ *
+ * @return the metadata. Will not be <code>null</code>
+ */
+ ContentModification getRepositoryContentModification();
+
+ /**
+ * Cancel the action.
+ */
+ void cancel();
+
+ /**
+ * Execute the action and if successful mark it as complete.
+ */
+ void complete();
+
+ /**
+ * Gets whether {@link #complete()} has been invoked.
+ *
+ * @return <code>true</code> if {@link #complete()} has been invoked
+ */
+ boolean isComplete();
+
+ /**
+ * Gets whether {@link #complete()} has been invoked.
+ *
+ * @return <code>true</code> if {@link #complete()} has been invoked
+ */
+ boolean isCancelled();
+}
Property changes on: trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/SynchronizationAction.java
___________________________________________________________________
Name: svn:keywords
+
Added: trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/SynchronizationActionContext.java
===================================================================
--- trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/SynchronizationActionContext.java (rev 0)
+++ trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/SynchronizationActionContext.java 2009-03-31 22:10:16 UTC (rev 86553)
@@ -0,0 +1,66 @@
+/*
+ * 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 org.jboss.system.server.profileservice.repository.clustered.metadata.RepositoryContentMetadata;
+
+
+/**
+ * Contextual information that a {@link SynchronizationAction} can use.
+ *
+ * @author Brian Stansberry
+ *
+ * @version $Revision: $
+ */
+public class SynchronizationActionContext
+{
+ private final SynchronizationId<?> id;
+ private final RepositoryContentMetadata inProgressMetadata;
+
+ public SynchronizationActionContext(SynchronizationId<?> id,
+ RepositoryContentMetadata inProgressMetadata)
+ {
+ if (id == null)
+ {
+ throw new IllegalArgumentException("Null id");
+ }
+ if (inProgressMetadata == null)
+ {
+ throw new IllegalArgumentException("Null inProgressMetadata");
+ }
+ this.id = id;
+ this.inProgressMetadata = inProgressMetadata;
+ }
+
+ public SynchronizationId<?> getId()
+ {
+ return id;
+ }
+
+ public RepositoryContentMetadata getInProgressMetadata()
+ {
+ return inProgressMetadata;
+ }
+
+
+}
Property changes on: trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/SynchronizationActionContext.java
___________________________________________________________________
Name: svn:keywords
+
Added: trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/SynchronizationId.java
===================================================================
--- trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/SynchronizationId.java (rev 0)
+++ trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/SynchronizationId.java 2009-03-31 22:10:16 UTC (rev 86553)
@@ -0,0 +1,104 @@
+/*
+ * 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.io.Serializable;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * Unique id for a set of changes needed to synchronize a node's
+ * repository content with the cluster.
+ *
+ * @author Brian Stansberry
+ *
+ * @version $Revision: $
+ *
+ * @param T the type of the originator
+ */
+public class SynchronizationId<T extends Serializable> implements Serializable
+{
+ /** The serialVersionUID */
+ private static final long serialVersionUID = -604832735573100571L;
+
+ private static final long vm_base = System.currentTimeMillis();
+ private static final AtomicInteger count = new AtomicInteger();
+
+ private final T originator;
+ private final long timestamp = vm_base;
+ private final int index = count.incrementAndGet();
+
+ public SynchronizationId(T originator)
+ {
+ if (originator == null)
+ {
+ throw new IllegalArgumentException("Null originator");
+ }
+ this.originator = originator;
+ }
+
+ public T getOriginator()
+ {
+ return originator;
+ }
+
+ @Override
+ public boolean equals(Object obj)
+ {
+ if (this == obj)
+ return true;
+
+ if (obj instanceof SynchronizationId)
+ {
+ SynchronizationId<?> other = (SynchronizationId<?>) obj;
+ return this.index == other.index
+ && this.timestamp == other.timestamp
+ && this.originator.equals(other.originator);
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode()
+ {
+ int result = 17;
+ result = 31 * result + index;
+ result = 31 * result + ((int) (timestamp ^ (timestamp >>>32)));
+ result = 31 * result + originator.hashCode();
+ return result;
+ }
+
+ @Override
+ public String toString()
+ {
+ return new StringBuilder(getClass().getName())
+ .append("[originator='")
+ .append(originator)
+ .append(",timestamp=")
+ .append(timestamp)
+ .append(",index=")
+ .append(index)
+ .append(']').toString();
+ }
+
+
+}
Property changes on: trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/SynchronizationId.java
___________________________________________________________________
Name: svn:keywords
+
Added: trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/SynchronizationInitiationAction.java
===================================================================
--- trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/SynchronizationInitiationAction.java (rev 0)
+++ trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/SynchronizationInitiationAction.java 2009-03-31 22:10:16 UTC (rev 86553)
@@ -0,0 +1,39 @@
+/*
+ * 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;
+
+/**
+ * Marker interface for a {@link SynchronizationAction} that initiates a
+ * process that subsequent actions will drive to comletion. An example of this
+ * would be an action to initiate the removal of a directory, which would
+ * be driven to completion by subsequent actions to remove child content
+ * and finally an action to remove the directory itself..
+ *
+ * @author Brian Stansberry
+ *
+ * @version $Revision: $
+ */
+public interface SynchronizationInitiationAction<T extends SynchronizationActionContext>
+ extends SynchronizationAction<T>
+{
+}
Property changes on: trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/SynchronizationInitiationAction.java
___________________________________________________________________
Name: svn:keywords
+
Added: trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/SynchronizationPolicy.java
===================================================================
--- trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/SynchronizationPolicy.java (rev 0)
+++ trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/SynchronizationPolicy.java 2009-03-31 22:10:16 UTC (rev 86553)
@@ -0,0 +1,186 @@
+/*
+ * 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 org.jboss.system.server.profileservice.repository.clustered.metadata.RepositoryContentMetadata;
+import org.jboss.system.server.profileservice.repository.clustered.metadata.RepositoryItemMetadata;
+
+/**
+ * Policy to decide how to handle content updates from nodes attempting
+ * to join the cluster or from cluster merges. The policy is consulted on
+ * the "authoritative" node, i.e. the master node for the service on the
+ * cluster.
+ *
+ * @author Brian Stansberry
+ *
+ * @version $Revision: $
+ */
+public interface SynchronizationPolicy
+{
+
+ /**
+ * Should the item represented by <code>toAdd</code> that is available
+ * on a newly <i>joining</i> node be accepted for use around the cluster
+ * when the cluster's records show no record of an item with the same path?
+ * Such a case potentially could mean newly joining node was unaware of an
+ * earlier removal that occurred while it was offline and that the cluster
+ * has also {@link #purgeRemovedItems(RepositoryContentMetadata) purged
+ * from its records}.
+ *
+ * @param toAdd the item to add
+ * @param joinersPrevious information, if available, on the timestamp of the
+ * item that was present on the joining node when
+ * it stopped. May be <code>null</code>, indicating
+ * the joining node was unaware of the item when stopped.
+ * @return <code>true</code> if the addition should be accepted
+ */
+ boolean acceptJoinAddition(RepositoryItemMetadata toAdd,
+ RepositoryItemMetadata joinersPrevious);
+
+ /**
+ * Should the item represented by <code>reincarnation</code> that is available
+ * on a newly <i>joining</i> node be accepted for use around the cluster when
+ * the cluster's records show an item with the same path was previously
+ * removed? Such a case potentially could mean the newly joining node
+ * was unaware of a removal that occurred while it was offline.
+ *
+ * @param reincarnation the new version of the item
+ * @param current the cluster's current version of the item, showing when
+ * it was removed and by whom. The "when" should reflect this
+ * node's time of removal, not the time on the node that originated
+ * the removal.
+ * @return <code>true</code> if the reincarnation should be accepted
+ */
+ boolean acceptJoinReincarnation(RepositoryItemMetadata reincarnation,
+ RepositoryItemMetadata current);
+
+ /**
+ * Should the item represented by <code>toRemove</code>that is unavailable
+ * on a merging set of nodes be removed from around the cluster when
+ * the cluster's records show an item with the same path? Such a case
+ * potentially could mean the newly joining node was unaware of a new
+ * deployment of the item that occurred while it was offline.
+ *
+ * @param current the cluster's current version of the item
+ * @param joinersItem the joining node's view of item to remove. May be null,
+ * indicating the sender is unaware of the item. If not null,
+ * the timestamp of this item should reflect when the item
+ * was removed, if known. If the time the item was removed is
+ * not known, the timestamp should reflect the last known
+ * timestamp of the item that was removed.
+ * @return <code>true</code> if the removal should be accepted
+ */
+ boolean acceptJoinRemoval(RepositoryItemMetadata current,
+ RepositoryItemMetadata joinersItem);
+ /**
+ * Should the item represented by <code>update</code> that is available
+ * on a newly <i>joining</i> node be accepted for use around the cluster when the
+ * cluster's records show an item with the same path with a different version?
+ * Such a case potentially could mean the newly joining node was unaware of
+ * changes that occurred while it was offline.
+ *
+ * @param update the new version of the item
+ * @param current the cluster's current version of the item
+ * @return <code>true</code> if the update should be accepted
+ */
+ boolean acceptJoinUpdate(RepositoryItemMetadata update,
+ RepositoryItemMetadata current);
+
+ /**
+ * Should the item represented by <code>toAdd</code> that is available
+ * on a merging set of nodes be accepted for use around the cluster when the
+ * cluster's records show no record of an item with the same path? Such a
+ * case potentially could mean the merging nodes were unaware of an earlier
+ * removal that occurred while the cluster was split and that the cluster
+ * has also {@link #purgeRemovedItems(RepositoryContentMetadata) purged
+ * from its records}.
+ *
+ * @param toAdd the item to add
+ * @return <code>true</code> if the addition should be accepted
+ */
+ boolean acceptMergeAddition(RepositoryItemMetadata toAdd);
+
+
+ /**
+ * Should the item represented by <code>reincarnation</code> that is available
+ * on a merging set of nodes be accepted for use around the cluster when the
+ * cluster's records show an item with the same path was previously removed?
+ * Such a case potentially could mean the merging nodes were unaware of a
+ * removal that occurred while the cluster was split.
+ *
+ * @param reincarnation the new version of the item
+ * @param current the cluster's current version of the item, showing when
+ * it was removed and by whom
+ * @return <code>true</code> if the reincarnation should be accepted
+ */
+ boolean acceptMergeReincarnation(RepositoryItemMetadata reincarnation,
+ RepositoryItemMetadata current);
+
+ /**
+ * Should the item represented by <code>toRemove</code> that is unavailable
+ * on a newly <i>joining</i> node be removed from around the cluster when
+ * the cluster's records show an item with the same path? Such a case
+ * potentially could mean the newly joining node was unaware of a new
+ * deployment of the item that occurred while the cluster was split.
+ *
+ * @param current the cluster's current version of the item
+ * @param mergersView the merging node's view of item to remove. May be null,
+ * indicating the sender is unaware of the item. If not null,
+ * the timestamp of this item should reflect when the item
+ * was removed, if known. If the time the item was removed is
+ * not known, the timestamp should reflect the last known
+ * timestamp of the item that was removed.
+ * @return <code>true</code> if the removal should be accepted
+ */
+ boolean acceptMergeRemoval(RepositoryItemMetadata current,
+ RepositoryItemMetadata mergersView);
+
+
+ /**
+ * Should the item represented by <code>update</code> that is available
+ * on a merging set of nodes be accepted for use around the cluster when the
+ * cluster's records show an item with the same path with a different version?
+ * Such a case potentially could mean the merging nodes were unaware of
+ * changes that occurred while the cluster was split.
+ *
+ * @param update the new version of the item
+ * @param current the cluster's current version of the item
+ * @return <code>true</code> if the update should be accepted
+ */
+ boolean acceptMergeUpdate(RepositoryItemMetadata update,
+ RepositoryItemMetadata current);
+
+ /**
+ * Request that the policy remove any {@link RepositoryItemMetadata} objects
+ * that are listed as {@link RepositoryItemMetadata#isRemoved() removed}
+ * if the policy no longer wishes to consider them in its decision making.
+ * Used to prevent perpetual growth in the size of the RepositoryContentMetadata
+ * by eventually purging records of removed items.
+ *
+ * @param content the content. Cannot be <code>null</code>.
+ *
+ * @return <code>true</code> if any items were purged, <code>false</code>
+ * if not
+ */
+ boolean purgeRemovedItems(RepositoryContentMetadata content);
+}
Property changes on: trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/SynchronizationPolicy.java
___________________________________________________________________
Name: svn:keywords
+
Added: trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/SynchronizationReadAction.java
===================================================================
--- trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/SynchronizationReadAction.java (rev 0)
+++ trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/SynchronizationReadAction.java 2009-03-31 22:10:16 UTC (rev 86553)
@@ -0,0 +1,49 @@
+/*
+ * 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.io.IOException;
+
+
+
+/**
+ * A {@ link SynchronizationAction} that involves reading content from an item.
+ *
+ * @author Brian Stansberry
+ *
+ * @version $Revision: $
+ */
+public interface SynchronizationReadAction<T extends SynchronizationActionContext>
+ extends SynchronizationAction<T>
+{
+ /**
+ * Gets the next chunk of bytes from the item associated with this action.
+ * Each call to this method will retrieve more bytes
+ *
+ * @return a ByteChunk.
+ *
+ * @throws IOException if there is a problem reading the bytes.
+ * @throws IllegalStateException if {@link #isComplete()} would return <code>true</code>
+ */
+ ByteChunk getNextBytes() throws IOException;
+}
Property changes on: trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/SynchronizationReadAction.java
___________________________________________________________________
Name: svn:keywords
+
Added: trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/SynchronizationRemoteAction.java
===================================================================
--- trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/SynchronizationRemoteAction.java (rev 0)
+++ trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/SynchronizationRemoteAction.java 2009-03-31 22:10:16 UTC (rev 86553)
@@ -0,0 +1,43 @@
+/*
+ * 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;
+
+/**
+ * Marker interface for a {@link SynchronizationAction} that a
+ * node should handle by telling the other nodes in the cluster to execute the
+ * {@link SynchronizationAction#getRepositoryContentModification() modification}.
+ * <p>
+ * An example of this would be handling of a
+ * {@link ContentModification.Type#REMOVE_TO_CLUSTER} wherein the
+ * node driving the synchronization would tell the other nodes to
+ * execute the remove by sending an RPC with the modification.
+ *
+ * @author Brian Stansberry
+ *
+ * @version $Revision: $
+ */
+public interface SynchronizationRemoteAction<T extends SynchronizationActionContext>
+ extends SynchronizationAction<T>
+{
+ boolean isInitiation();
+}
Property changes on: trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/SynchronizationRemoteAction.java
___________________________________________________________________
Name: svn:keywords
+
Added: trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/SynchronizationWriteAction.java
===================================================================
--- trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/SynchronizationWriteAction.java (rev 0)
+++ trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/SynchronizationWriteAction.java 2009-03-31 22:10:16 UTC (rev 86553)
@@ -0,0 +1,49 @@
+/*
+ * 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.io.IOException;
+
+
+/**
+ * A SynchronizationAction that involves writing content to an item.
+ *
+ * @author Brian Stansberry
+ *
+ * @version $Revision: $
+ */
+public interface SynchronizationWriteAction<T extends SynchronizationActionContext>
+ extends SynchronizationAction<T>
+{
+ /**
+ * Write the given bytes to the item referenced by this action.
+ *
+ * @param bytes the bytes. Cannot be <code>null</code>. The
+ * {@link ByteChunk#getByteCount() byte count} must be greater
+ * than -1.
+ *
+ * @throws IOException
+ * @throws IllegalStateException if {@link #isComplete()} would return <code>true</code>
+ */
+ void writeBytes(ByteChunk bytes) throws IOException;
+}
Property changes on: trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/SynchronizationWriteAction.java
___________________________________________________________________
Name: svn:keywords
+
Added: trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/TwoPhaseCommitAction.java
===================================================================
--- trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/TwoPhaseCommitAction.java (rev 0)
+++ trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/TwoPhaseCommitAction.java 2009-03-31 22:10:16 UTC (rev 86553)
@@ -0,0 +1,43 @@
+/*
+ * 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 org.jboss.system.server.profileservice.repository.clustered.local.LocalContentManager;
+
+
+/**
+ * View of a {@link SynchronizationAction} used by an
+ * implementation of {@link LocalContentManager}, allowing the persister
+ * to take the action through a two phase commit process.
+ *
+ * @author Brian Stansberry
+ *
+ * @version $Revision: $
+ */
+public interface TwoPhaseCommitAction<T extends SynchronizationActionContext>
+ extends SynchronizationAction<T>
+{
+ boolean prepare();
+ void commit();
+ void rollback();
+}
\ No newline at end of file
Property changes on: trunk/system/src/main/org/jboss/system/server/profileservice/repository/clustered/sync/TwoPhaseCommitAction.java
___________________________________________________________________
Name: svn:keywords
+
Added: trunk/system/src/tests/org/jboss/test/server/profileservice/clustered/test/JAXBRepositoryContentMetadataPersisterUnitTestCase.java
===================================================================
--- trunk/system/src/tests/org/jboss/test/server/profileservice/clustered/test/JAXBRepositoryContentMetadataPersisterUnitTestCase.java (rev 0)
+++ trunk/system/src/tests/org/jboss/test/server/profileservice/clustered/test/JAXBRepositoryContentMetadataPersisterUnitTestCase.java 2009-03-31 22:10:16 UTC (rev 86553)
@@ -0,0 +1,193 @@
+/*
+ * 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.test.server.profileservice.clustered.test;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStreamReader;
+import java.io.StringWriter;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.TimeZone;
+
+import junit.framework.TestCase;
+
+import org.jboss.profileservice.spi.ProfileKey;
+import org.jboss.system.server.profileservice.repository.clustered.local.JAXBRepositoryContentMetadataPersister;
+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;
+
+/**
+ * Tests of {@link JAXBRepositoryContentMetadataPersister}.
+ *
+ * @author Brian Stansberry
+ *
+ * @version $Revision: $
+ */
+public class JAXBRepositoryContentMetadataPersisterUnitTestCase extends TestCase
+{
+ private Set<File> toDelete = new HashSet<File>();
+
+ /**
+ * Create a new JAXBRepositoryContentMetadataPersisterUnitTestCase.
+ *
+ * @param name
+ */
+ public JAXBRepositoryContentMetadataPersisterUnitTestCase(String name)
+ {
+ super(name);
+ }
+
+ protected void tearDown() throws Exception
+ {
+ for (File f : toDelete)
+ {
+ f.delete();
+ }
+ }
+
+ public void testSerializationDeserialization() throws Exception
+ {
+ ProfileKey key = new ProfileKey("domain", "server", "name");
+ RepositoryContentMetadata rcm = new RepositoryContentMetadata(key);
+ RepositoryRootMetadata rrm = new RepositoryRootMetadata("normal");
+
+ RepositoryItemMetadata rim = new RepositoryItemMetadata();
+ rim.setRelativePath("/item");
+ rim.setOriginatingNode("192.168.100.1:1099");
+ rim.setTimestamp(1);
+ rrm.addItemMetadata(rim);
+
+ rim = new RepositoryItemMetadata();
+ rim.setRelativePath("/removed_item");
+ rim.setRemoved(true);
+ rim.setOriginatingNode("192.168.100.1:1099");
+ rim.setTimestamp(2);
+ rrm.addItemMetadata(rim);
+
+ rim = new RepositoryItemMetadata();
+ rim.setRelativePath("/dir.sar");
+ rim.setDirectory(true);
+ rim.setOriginatingNode("192.168.100.2:1099");
+ rim.setTimestamp(4);
+ rrm.addItemMetadata(rim);
+ rim = new RepositoryItemMetadata();
+ rim.setRelativePath("/dir.sar/item.jar");
+ rim.setOriginatingNode("192.168.100.2:1099");
+ rim.setTimestamp(4);
+ rrm.addItemMetadata(rim);
+ rim = new RepositoryItemMetadata();
+ rim.setRelativePath("/dir.sar/META-INF");
+ rim.setOriginatingNode("192.168.100.2:1099");
+ rim.setDirectory(true);
+ rim.setTimestamp(3);
+ rrm.addItemMetadata(rim);
+ rim = new RepositoryItemMetadata();
+ rim.setRelativePath("/dir.sar/META-INF/jboss-beans.xml");
+ rim.setDirectory(true);
+ rim.setOriginatingNode("192.168.100.2:1099");
+ rim.setTimestamp(3);
+ rrm.addItemMetadata(rim);
+
+ rim = new RepositoryItemMetadata();
+ rim.setRelativePath("/removed_dir.ear");
+ rim.setDirectory(true);
+ rim.setOriginatingNode("192.168.100.2:1099");
+ rim.setTimestamp(7);
+ rim.setRemoved(true);
+ rrm.addItemMetadata(rim);
+ rim = new RepositoryItemMetadata();
+ rim.setRelativePath("/removed_dir.ear/ejb.jar");
+ rim.setOriginatingNode("192.168.100.2:1099");
+ rim.setTimestamp(5);
+ rim.setRemoved(true);
+ rrm.addItemMetadata(rim);
+ rim = new RepositoryItemMetadata();
+ rim.setRelativePath("/removed_dir.ear/META-INF");
+ rim.setDirectory(true);
+ rim.setOriginatingNode("192.168.100.3:1099");
+ rim.setTimestamp(7);
+ rim.setRemoved(true);
+ rrm.addItemMetadata(rim);
+ rim = new RepositoryItemMetadata();
+ rim.setRelativePath("/removed_dir.ear/META-INF/application.xml");
+ rim.setDirectory(true);
+ rim.setOriginatingNode("192.168.100.3:1099");
+ rim.setTimestamp(7);
+ rim.setRemoved(true);
+ rrm.addItemMetadata(rim);
+ rim = new RepositoryItemMetadata();
+ rim.setRelativePath("/removed_dir.ear/war.war");
+ rim.setOriginatingNode("192.168.100.3:1099");
+ rim.setTimestamp(6);
+ rim.setRemoved(true);
+ rrm.addItemMetadata(rim);
+
+ RepositoryRootMetadata emptyRRM = new RepositoryRootMetadata("empty");
+ rim = new RepositoryItemMetadata();
+ rim.setRelativePath("/");
+ rim.setOriginatingNode("192.168.100.4:1099");
+ rim.setTimestamp(10);
+ rcm.setRepositories(Arrays.asList(new RepositoryRootMetadata[]{rrm, emptyRRM}));
+
+ rim = new RepositoryItemMetadata();
+ rim.setRelativePath("/");
+ rim.setOriginatingNode("127.0.0.1:1099");
+ rim.setTimestamp(20);
+ emptyRRM.addItemMetadata(rim);
+
+ File temp = new File(System.getProperty("java.io.tmpdir"));
+
+ JAXBRepositoryContentMetadataPersister testee = new JAXBRepositoryContentMetadataPersister(temp.toURI());
+ testee.store("test", rcm);
+
+ // Ensure we clean up
+ File stored = testee.getMetadataPath("test");
+ stored.deleteOnExit();
+ toDelete.add(stored);
+
+ InputStreamReader isr = new InputStreamReader(new FileInputStream(stored));
+ StringWriter writer = new StringWriter();
+ int read;
+ while ((read = isr.read()) != -1)
+ writer.write(read);
+ writer.close();
+ System.out.println(writer.toString());
+
+ RepositoryContentMetadata deserialized = testee.load("test");
+
+ assertEquals(rcm, deserialized);
+ }
+
+ public void testSplit()
+ {
+ String x = "/";
+ String[] split = x.split("/");
+ System.out.println(split.length);
+ for (String s : split)
+ System.out.println(s);
+ }
+
+}
Property changes on: trunk/system/src/tests/org/jboss/test/server/profileservice/clustered/test/JAXBRepositoryContentMetadataPersisterUnitTestCase.java
___________________________________________________________________
Name: svn:keywords
+
Added: trunk/system/src/tests/org/jboss/test/server/profileservice/clustered/test/MockSynchronizationPolicy.java
===================================================================
--- trunk/system/src/tests/org/jboss/test/server/profileservice/clustered/test/MockSynchronizationPolicy.java (rev 0)
+++ trunk/system/src/tests/org/jboss/test/server/profileservice/clustered/test/MockSynchronizationPolicy.java 2009-03-31 22:10:16 UTC (rev 86553)
@@ -0,0 +1,63 @@
+/*
+ * 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.test.server.profileservice.clustered.test;
+
+import org.jboss.system.server.profileservice.repository.clustered.metadata.RepositoryItemMetadata;
+import org.jboss.system.server.profileservice.repository.clustered.sync.AbstractSynchronizationPolicy;
+
+/**
+ *
+ *
+ * @author Brian Stansberry
+ *
+ * @version $Revision: $
+ */
+public class MockSynchronizationPolicy extends AbstractSynchronizationPolicy
+{
+
+ @Override
+ protected boolean acceptAddition(RepositoryItemMetadata toAdd, RepositoryItemMetadata joinersPrevious, boolean merge)
+ {
+ return true;
+ }
+
+ @Override
+ protected boolean acceptReincarnation(RepositoryItemMetadata reincarnation, RepositoryItemMetadata current,
+ boolean merge)
+ {
+ return true;
+ }
+
+ @Override
+ protected boolean acceptRemoval(RepositoryItemMetadata current, RepositoryItemMetadata sendersView, boolean merge)
+ {
+ return true;
+ }
+
+ @Override
+ protected boolean acceptUpdate(RepositoryItemMetadata update, RepositoryItemMetadata current, boolean merge)
+ {
+ return true;
+ }
+
+}
Property changes on: trunk/system/src/tests/org/jboss/test/server/profileservice/clustered/test/MockSynchronizationPolicy.java
___________________________________________________________________
Name: svn:keywords
+
Added: trunk/system/src/tests/org/jboss/test/server/profileservice/clustered/test/RemoteContentModificationGeneratorUnitTestCase.java
===================================================================
--- trunk/system/src/tests/org/jboss/test/server/profileservice/clustered/test/RemoteContentModificationGeneratorUnitTestCase.java (rev 0)
+++ trunk/system/src/tests/org/jboss/test/server/profileservice/clustered/test/RemoteContentModificationGeneratorUnitTestCase.java 2009-03-31 22:10:16 UTC (rev 86553)
@@ -0,0 +1,255 @@
+/*
+ * 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.test.server.profileservice.clustered.test;
+
+import java.util.List;
+
+import junit.framework.TestCase;
+
+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;
+import org.jboss.system.server.profileservice.repository.clustered.sync.ContentModification;
+import org.jboss.system.server.profileservice.repository.clustered.sync.RemoteContentModificationGenerator;
+import org.jboss.system.server.profileservice.repository.clustered.sync.ContentModification.Type;
+
+/**
+ *
+ *
+ * @author Brian Stansberry
+ *
+ * @version $Revision: $
+ */
+public class RemoteContentModificationGeneratorUnitTestCase extends TestCase
+{
+
+ /**
+ * Create a new RemoteContentModificationGeneratorUnitTestCase.
+ *
+ * @param name
+ */
+ public RemoteContentModificationGeneratorUnitTestCase(String name)
+ {
+ super(name);
+ }
+
+ public void testJoinAdditionToEmpty() throws Exception
+ {
+ RepositoryContentMetadata base = getNewRepositoryContentMetadata();
+
+ RepositoryContentMetadata modified = getNewRepositoryContentMetadata();
+
+ RepositoryItemMetadata item = new RepositoryItemMetadata();
+ item.setRelativePath("item");
+ item.setOriginatingNode("localhost");
+ item.setTimestamp(1);
+ modified.getRepositories().iterator().next().getContent().add(item);
+
+ RemoteContentModificationGenerator testee =
+ new RemoteContentModificationGenerator(new MockSynchronizationPolicy(), getNewRepositoryContentMetadata());
+
+ List<ContentModification> mods = testee.getModificationList(base, modified);
+
+ assertEquals(1, mods.size());
+ assertEquals(Type.PUSH_TO_CLUSTER, mods.get(0).getType());
+ assertEquals(item, mods.get(0).getItem());
+ }
+
+ public void testMergeAdditionToEmpty() throws Exception
+ {
+ RepositoryContentMetadata base = getNewRepositoryContentMetadata();
+
+ RepositoryContentMetadata modified = getNewRepositoryContentMetadata();
+
+ RepositoryItemMetadata item = new RepositoryItemMetadata();
+ item.setRelativePath("item");
+ item.setOriginatingNode("localhost");
+ item.setTimestamp(1);
+ modified.getRepositories().iterator().next().getContent().add(item);
+
+ RemoteContentModificationGenerator testee =
+ new RemoteContentModificationGenerator(new MockSynchronizationPolicy());
+
+ List<ContentModification> mods = testee.getModificationList(base, modified);
+
+ assertEquals(1, mods.size());
+ assertEquals(Type.PUSH_TO_CLUSTER, mods.get(0).getType());
+ assertEquals(item, mods.get(0).getItem());
+ }
+
+ public void testSimpleJoinAddition() throws Exception
+ {
+ RepositoryContentMetadata base = getNewRepositoryContentMetadata();
+
+ RepositoryItemMetadata item1 = new RepositoryItemMetadata();
+ item1.setRelativePath("item1");
+ item1.setOriginatingNode("localhost");
+ item1.setTimestamp(1);
+ base.getRepositories().iterator().next().getContent().add(item1);
+
+ RepositoryContentMetadata modified = getNewRepositoryContentMetadata();
+
+ item1 = new RepositoryItemMetadata();
+ item1.setRelativePath("item1");
+ item1.setOriginatingNode("localhost");
+ item1.setTimestamp(1);
+ modified.getRepositories().iterator().next().getContent().add(item1);
+
+ RepositoryItemMetadata item2 = new RepositoryItemMetadata();
+ item2.setRelativePath("item2");
+ item2.setOriginatingNode("localhost");
+ item2.setTimestamp(1);
+ modified.getRepositories().iterator().next().getContent().add(item2);
+
+ RemoteContentModificationGenerator testee =
+ new RemoteContentModificationGenerator(new MockSynchronizationPolicy(),
+ getNewRepositoryContentMetadata());
+
+ List<ContentModification> mods = testee.getModificationList(base, modified);
+
+ assertEquals(1, mods.size());
+ assertEquals(Type.PUSH_TO_CLUSTER, mods.get(0).getType());
+ assertEquals(item2, mods.get(0).getItem());
+ }
+
+ public void testSimpleMergeAddition() throws Exception
+ {
+ RepositoryContentMetadata base = getNewRepositoryContentMetadata();
+
+ RepositoryItemMetadata item1 = new RepositoryItemMetadata();
+ item1.setRelativePath("item1");
+ item1.setOriginatingNode("localhost");
+ item1.setTimestamp(1);
+ base.getRepositories().iterator().next().getContent().add(item1);
+
+ RepositoryContentMetadata modified = getNewRepositoryContentMetadata();
+
+ item1 = new RepositoryItemMetadata();
+ item1.setRelativePath("item1");
+ item1.setOriginatingNode("localhost");
+ item1.setTimestamp(1);
+ modified.getRepositories().iterator().next().getContent().add(item1);
+
+ RepositoryItemMetadata item2 = new RepositoryItemMetadata();
+ item2.setRelativePath("item2");
+ item2.setOriginatingNode("localhost");
+ item2.setTimestamp(1);
+ modified.getRepositories().iterator().next().getContent().add(item2);
+
+ RemoteContentModificationGenerator testee =
+ new RemoteContentModificationGenerator(new MockSynchronizationPolicy());
+
+ List<ContentModification> mods = testee.getModificationList(base, modified);
+
+ assertEquals(1, mods.size());
+ assertEquals(Type.PUSH_TO_CLUSTER, mods.get(0).getType());
+ assertEquals(item2, mods.get(0).getItem());
+ }
+
+ public void testJoinSwap() throws Exception
+ {
+ RepositoryContentMetadata base = getNewRepositoryContentMetadata();
+
+ RepositoryItemMetadata item1 = new RepositoryItemMetadata();
+ item1.setRelativePath("item1");
+ item1.setOriginatingNode("localhost");
+ item1.setTimestamp(1);
+ base.getRepositories().iterator().next().getContent().add(item1);
+
+ RepositoryContentMetadata modified = getNewRepositoryContentMetadata();
+
+ RepositoryItemMetadata item2 = new RepositoryItemMetadata();
+ item2.setRelativePath("item2");
+ item2.setOriginatingNode("localhost");
+ item2.setTimestamp(1);
+ modified.getRepositories().iterator().next().getContent().add(item2);
+
+ MockSynchronizationPolicy policy = new MockSynchronizationPolicy();
+ policy.setAllowJoinRemovals(Boolean.FALSE);
+
+ RemoteContentModificationGenerator testee =
+ new RemoteContentModificationGenerator(policy, getNewRepositoryContentMetadata());
+
+ List<ContentModification> mods = testee.getModificationList(base, modified);
+
+ assertEquals(2, mods.size());
+ assertEquals(Type.PULL_FROM_CLUSTER, mods.get(0).getType());
+ assertEquals(item1, mods.get(0).getItem());
+ assertEquals(Type.PUSH_TO_CLUSTER, mods.get(1).getType());
+ assertEquals(item2, mods.get(1).getItem());
+ }
+
+ public void testMergeSwap() throws Exception
+ {
+ RepositoryContentMetadata base = getNewRepositoryContentMetadata();
+
+ RepositoryItemMetadata item1 = new RepositoryItemMetadata();
+ item1.setRelativePath("item1");
+ item1.setOriginatingNode("localhost");
+ item1.setTimestamp(1);
+ base.getRepositories().iterator().next().getContent().add(item1);
+
+ RepositoryContentMetadata modified = getNewRepositoryContentMetadata();
+
+ RepositoryItemMetadata item2 = new RepositoryItemMetadata();
+ item2.setRelativePath("item2");
+ item2.setOriginatingNode("localhost");
+ item2.setTimestamp(1);
+ modified.getRepositories().iterator().next().getContent().add(item2);
+
+ MockSynchronizationPolicy policy = new MockSynchronizationPolicy();
+ policy.setAllowMergeRemovals(Boolean.FALSE);
+
+ RemoteContentModificationGenerator testee =
+ new RemoteContentModificationGenerator(policy);
+
+ List<ContentModification> mods = testee.getModificationList(base, modified);
+
+ assertEquals(2, mods.size());
+ assertEquals(Type.PULL_FROM_CLUSTER, mods.get(0).getType());
+ assertEquals(item1, mods.get(0).getItem());
+ assertEquals(Type.PUSH_TO_CLUSTER, mods.get(1).getType());
+ assertEquals(item2, mods.get(1).getItem());
+ }
+
+ private static RepositoryContentMetadata getNewRepositoryContentMetadata()
+ {
+ return getNewRepositoryContentMetadata("farm");
+ }
+
+ private static RepositoryContentMetadata getNewRepositoryContentMetadata(String ... rootNames)
+ {
+ RepositoryContentMetadata base = new RepositoryContentMetadata();
+ base.setDomain("domain");
+ base.setServer("server");
+ base.setName("name");
+ for (String rootName : rootNames)
+ {
+ RepositoryRootMetadata root = new RepositoryRootMetadata();
+ root.setName(rootName);
+ base.getRepositories().add(root);
+ }
+
+ return base;
+ }
+}
Property changes on: trunk/system/src/tests/org/jboss/test/server/profileservice/clustered/test/RemoteContentModificationGeneratorUnitTestCase.java
___________________________________________________________________
Name: svn:keywords
+
More information about the jboss-cvs-commits
mailing list