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 }