001 /* 002 * JBoss DNA (http://www.jboss.org/dna) 003 * See the COPYRIGHT.txt file distributed with this work for information 004 * regarding copyright ownership. Some portions may be licensed 005 * to Red Hat, Inc. under one or more contributor license agreements. 006 * See the AUTHORS.txt file in the distribution for a full listing of 007 * individual contributors. 008 * 009 * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA 010 * is licensed to you under the terms of the GNU Lesser General Public License as 011 * published by the Free Software Foundation; either version 2.1 of 012 * the License, or (at your option) any later version. 013 * 014 * JBoss DNA is distributed in the hope that it will be useful, 015 * but WITHOUT ANY WARRANTY; without even the implied warranty of 016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 017 * Lesser General Public License for more details. 018 * 019 * You should have received a copy of the GNU Lesser General Public 020 * License along with this software; if not, write to the Free 021 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 022 * 02110-1301 USA, or see the FSF site: http://www.fsf.org. 023 */ 024 package org.jboss.dna.connector.federation; 025 026 import java.util.ArrayList; 027 import java.util.Collections; 028 import java.util.HashMap; 029 import java.util.Iterator; 030 import java.util.LinkedList; 031 import java.util.List; 032 import java.util.Map; 033 import net.jcip.annotations.Immutable; 034 import org.jboss.dna.common.collection.Problems; 035 import org.jboss.dna.common.collection.ThreadSafeProblems; 036 import org.jboss.dna.common.util.CheckArg; 037 import org.jboss.dna.connector.federation.merge.strategy.MergeStrategy; 038 import org.jboss.dna.connector.federation.merge.strategy.OneContributionMergeStrategy; 039 import org.jboss.dna.connector.federation.merge.strategy.SimpleMergeStrategy; 040 import org.jboss.dna.graph.cache.CachePolicy; 041 import org.jboss.dna.graph.connector.RepositorySource; 042 043 /** 044 * The configuration of a federated repository. workspace The configuration defines, among other things, the set of 045 * {@link #getSourceProjections() source projections} in the federated workspace that each specify how and where content from a 046 * {@link RepositorySource source} is federated into the unified workspace. 047 * 048 * @author Randall Hauch 049 */ 050 @Immutable 051 public class FederatedWorkspace implements Comparable<FederatedWorkspace> { 052 053 private final Projection cacheProjection; 054 private final CachePolicy cachePolicy; 055 private final List<Projection> sourceProjections; 056 private final Map<String, List<Projection>> projectionsBySourceName; 057 private final Problems problems; 058 private final String name; 059 private final MergeStrategy mergingStrategy; 060 061 /** 062 * Create a configuration for a federated workspace. 063 * 064 * @param workspaceName the name of the federated workspace; may not be null 065 * @param cacheProjection the projection used for the cache; may not be null 066 * @param sourceProjections the source projections; may not be null 067 * @param cachePolicy the cache policy for this workspace; may be null if there is no policy 068 * @throws IllegalArgumentException if the name is null or is blank 069 */ 070 public FederatedWorkspace( String workspaceName, 071 Projection cacheProjection, 072 Iterable<Projection> sourceProjections, 073 CachePolicy cachePolicy ) { 074 this(workspaceName, cacheProjection, sourceProjections, cachePolicy, null); 075 } 076 077 /** 078 * Create a configuration for a federated workspace. 079 * 080 * @param workspaceName the name of the federated workspace; may not be null 081 * @param cacheProjection the projection used for the cache; may not be null 082 * @param sourceProjections the source projections; may not be null 083 * @param cachePolicy the cache policy for this workspace; may be null if there is no policy 084 * @param mergeStrategy the strategy that should be used to merge nodes, or null if the strategy should be chosen based 085 * automatically based upon the number of sources used by the projections 086 * @throws IllegalArgumentException if the name is null or is blank 087 */ 088 public FederatedWorkspace( String workspaceName, 089 Projection cacheProjection, 090 Iterable<Projection> sourceProjections, 091 CachePolicy cachePolicy, 092 MergeStrategy mergeStrategy ) { 093 CheckArg.isNotNull(workspaceName, "workspaceName"); 094 CheckArg.isNotNull(cacheProjection, "cacheProjection"); 095 this.name = workspaceName; 096 this.cachePolicy = cachePolicy; 097 this.problems = new ThreadSafeProblems(); 098 this.cacheProjection = cacheProjection; 099 List<Projection> projectionList = new ArrayList<Projection>(); 100 for (Projection projection : sourceProjections) { 101 if (projection == null) continue; 102 if (!projectionList.contains(projection)) { 103 projectionList.add(projection); 104 } 105 } 106 this.sourceProjections = Collections.unmodifiableList(projectionList); 107 CheckArg.isNotEmpty(this.sourceProjections, "sourceProjections"); 108 this.projectionsBySourceName = new HashMap<String, List<Projection>>(); 109 for (Projection projection : this.sourceProjections) { 110 String sourceName = projection.getSourceName(); 111 List<Projection> projectionsForSource = projectionsBySourceName.get(sourceName); 112 if (projectionsForSource == null) { 113 projectionsForSource = new LinkedList<Projection>(); 114 projectionsBySourceName.put(sourceName, projectionsForSource); 115 } 116 projectionsForSource.add(projection); 117 } 118 if (mergeStrategy != null) { 119 this.mergingStrategy = mergeStrategy; 120 } else { 121 if (this.sourceProjections.size() == 1 && this.sourceProjections.get(0).isSimple()) { 122 this.mergingStrategy = new OneContributionMergeStrategy(); 123 } else { 124 this.mergingStrategy = new SimpleMergeStrategy(); 125 } 126 } 127 assert this.mergingStrategy != null; 128 } 129 130 /** 131 * Get the name of this repository 132 * 133 * @return name 134 */ 135 public String getName() { 136 return this.name; 137 } 138 139 /** 140 * Get the cache policy for this workspace 141 * 142 * @return the workspace's cache policy; may be null 143 */ 144 public CachePolicy getCachePolicy() { 145 return cachePolicy; 146 } 147 148 /** 149 * Get the merging strategy used for this workspace 150 * 151 * @return the workspace's merging strategy; never null 152 */ 153 public MergeStrategy getMergingStrategy() { 154 return mergingStrategy; 155 } 156 157 /** 158 * Return the problem associated with this configuration. These problems may change at any time, although the returned 159 * {@link Problems} object is thread-safe. 160 * 161 * @return the thread-safe problems for this configuration 162 */ 163 public Problems getProblems() { 164 return problems; 165 } 166 167 /** 168 * Get the projection that defines the cache for this repository. This projection does not exist in the 169 * {@link #getSourceProjections() list of source projections}. 170 * 171 * @return the region used for caching; never null 172 */ 173 public Projection getCacheProjection() { 174 return cacheProjection; 175 } 176 177 /** 178 * Return the unmodifiable list of source projections. 179 * 180 * @return the source projections; never null and never empty 181 */ 182 public List<Projection> getSourceProjections() { 183 return sourceProjections; 184 } 185 186 /** 187 * Return the unmodifiable list of projections for the source name. 188 * 189 * @param sourceName the name of the source 190 * @return the list of projections for this source, or null if there are none 191 */ 192 public List<Projection> getProjectionsFor( String sourceName ) { 193 return this.projectionsBySourceName.get(sourceName); 194 } 195 196 /** 197 * Determine whether this workspace has a projection supplied contribution 198 * 199 * @param sourceName the name of the source 200 * @param workspaceName the name of the workspace 201 * @return true if this workspace contains a projection that uses the supplied source and workspace 202 */ 203 public boolean contains( String sourceName, 204 String workspaceName ) { 205 List<Projection> projections = this.projectionsBySourceName.get(sourceName); 206 if (projections != null) { 207 for (Projection projection : sourceProjections) { 208 if (projection.getWorkspaceName().equals(workspaceName)) return true; 209 } 210 } 211 return false; 212 } 213 214 /** 215 * {@inheritDoc} 216 * 217 * @see java.lang.Object#hashCode() 218 */ 219 @Override 220 public int hashCode() { 221 return this.name.hashCode(); 222 } 223 224 /** 225 * {@inheritDoc} 226 * 227 * @see java.lang.Object#equals(java.lang.Object) 228 */ 229 @Override 230 public boolean equals( Object obj ) { 231 if (obj == this) return true; 232 if (obj instanceof FederatedWorkspace) { 233 FederatedWorkspace that = (FederatedWorkspace)obj; 234 if (!this.getName().equals(that.getName())) return false; 235 if (!this.getCacheProjection().equals(that.getCacheProjection())) return false; 236 if (!this.getSourceProjections().equals(that.getSourceProjections())) return false; 237 return true; 238 } 239 return false; 240 } 241 242 /** 243 * {@inheritDoc} 244 * 245 * @see java.lang.Comparable#compareTo(java.lang.Object) 246 */ 247 public int compareTo( FederatedWorkspace that ) { 248 if (that == this) return 0; 249 int diff = this.getName().compareTo(that.getName()); 250 if (diff != 0) return diff; 251 diff = this.getCacheProjection().compareTo(that.getCacheProjection()); 252 if (diff != 0) return diff; 253 Iterator<Projection> thisIter = this.getSourceProjections().iterator(); 254 Iterator<Projection> thatIter = that.getSourceProjections().iterator(); 255 while (thisIter.hasNext() && thatIter.hasNext()) { 256 diff = thisIter.next().compareTo(thatIter.next()); 257 if (diff != 0) return diff; 258 } 259 if (thisIter.hasNext()) return 1; 260 if (thatIter.hasNext()) return -1; 261 return 0; 262 } 263 }