Author: rhauch
Date: 2008-07-01 16:17:39 -0400 (Tue, 01 Jul 2008)
New Revision: 324
Added:
trunk/dna-repository/src/main/java/org/jboss/dna/repository/federation/FederatedRepositoryConfig.java
trunk/dna-repository/src/test/java/org/jboss/dna/repository/federation/SimpleRepository.java
trunk/dna-repository/src/test/java/org/jboss/dna/repository/federation/SimpleRepositorySource.java
Modified:
trunk/dna-repository/src/main/java/org/jboss/dna/repository/RepositoryI18n.java
trunk/dna-repository/src/main/java/org/jboss/dna/repository/federation/FederatedRegion.java
trunk/dna-repository/src/main/java/org/jboss/dna/repository/federation/FederatedRepository.java
trunk/dna-repository/src/main/java/org/jboss/dna/repository/federation/FederatedRepositoryConnection.java
trunk/dna-repository/src/main/java/org/jboss/dna/repository/federation/FederationService.java
trunk/dna-repository/src/main/java/org/jboss/dna/repository/federation/RepositorySourceManager.java
trunk/dna-repository/src/main/resources/org/jboss/dna/repository/RepositoryI18n.properties
trunk/dna-repository/src/test/java/org/jboss/dna/repository/federation/FederatedRepositoryConnectionTest.java
trunk/dna-repository/src/test/java/org/jboss/dna/repository/federation/FederatedRepositoryTest.java
trunk/dna-repository/src/test/java/org/jboss/dna/repository/federation/FederationServiceTest.java
Log:
DNA-115 - Create federation service
http://jira.jboss.com/jira/browse/DNA-115
Added more tests for FederationService, including tests for those methods that read the
configuration repository. To allow the configuration of a FederatedRepository to be
updated in a concurrent environment, the configuration-related information (regions,
default cache policy) was pulled into a separate FederatedRepositoryConfig object. A
FederatedRepository's configuration can therefore be replaced wholesale.
Modified: trunk/dna-repository/src/main/java/org/jboss/dna/repository/RepositoryI18n.java
===================================================================
---
trunk/dna-repository/src/main/java/org/jboss/dna/repository/RepositoryI18n.java 2008-07-01
20:14:52 UTC (rev 323)
+++
trunk/dna-repository/src/main/java/org/jboss/dna/repository/RepositoryI18n.java 2008-07-01
20:17:39 UTC (rev 324)
@@ -124,6 +124,8 @@
public static I18n interruptedWhileConnectingToFederationConfigurationRepository;
public static I18n interruptedWhileUsingFederationConfigurationRepository;
public static I18n
interruptedWhileClosingConnectionToFederationConfigurationRepository;
+ public static I18n federatedRepositoryCannotBeFound;
+ public static I18n unableToConnectToFederationConfigurationRepository;
public static I18n unableToFindRepositorySourceByName;
public static I18n unableToCreateConnectionToFederatedRepository;
public static I18n unableToAuthenticateConnectionToFederatedRepository;
Modified:
trunk/dna-repository/src/main/java/org/jboss/dna/repository/federation/FederatedRegion.java
===================================================================
---
trunk/dna-repository/src/main/java/org/jboss/dna/repository/federation/FederatedRegion.java 2008-07-01
20:14:52 UTC (rev 323)
+++
trunk/dna-repository/src/main/java/org/jboss/dna/repository/federation/FederatedRegion.java 2008-07-01
20:17:39 UTC (rev 324)
@@ -44,19 +44,22 @@
private final Path pathInRepository;
private final Path pathInSource;
private final String sourceName;
+ private final String regionName;
/**
* Create a binding given a location in the repository below which the content from
the named source appears.
*
+ * @param regionName the name of the region
* @param pathInRepository the absolute path in the repository at which the content
appears
* @param pathInSource the path in the source defining the content
* @param sourceName the name of the source
* @throws IllegalArgumentException if any of the parameters is null, or if the
source name is empty/blank, or if the
* <code>pathInRepository</code> is not {@link Path#isAbsolute()
absolute}.
*/
- public FederatedRegion( Path pathInRepository,
- Path pathInSource,
- String sourceName ) {
+ public FederatedRegion( String regionName,
+ Path pathInRepository,
+ Path pathInSource,
+ String sourceName ) {
ArgCheck.isNotNull(pathInRepository, "pathInRepository");
ArgCheck.isNotNull(pathInSource, "pathInSource");
ArgCheck.isNotEmpty(sourceName, "sourceName");
@@ -64,12 +67,22 @@
String msg =
RepositoryI18n.repositoryPathInFederationBindingIsNotAbsolute.text(pathInRepository.getString());
throw new IllegalArgumentException(msg);
}
+ this.regionName = regionName;
this.pathInRepository = pathInRepository;
this.pathInSource = pathInSource;
this.sourceName = sourceName;
}
/**
+ * Get the name of this region.
+ *
+ * @return regionName
+ */
+ public String getRegionName() {
+ return regionName;
+ }
+
+ /**
* Get the location in the repository at which the source's content appears.
*
* @return the repository-based path for the top of the source's content
@@ -154,6 +167,7 @@
if (obj == this) return true;
if (obj instanceof FederatedRegion) {
FederatedRegion that = (FederatedRegion)obj;
+ if (!this.getRegionName().equals(that.getRegionName())) return false;
if (!this.getPathInRepository().equals(that.getPathInRepository())) return
false;
if (!this.getPathInSource().equals(that.getPathInSource())) return false;
if (!this.getSourceName().equals(that.getSourceName())) return false;
@@ -169,8 +183,10 @@
*/
public int compareTo( FederatedRegion that ) {
if (this == that) return 0;
- int diff = this.getPathInRepository().compareTo(that.getPathInRepository());
+ int diff = this.getRegionName().compareTo(that.getRegionName());
if (diff != 0) return diff;
+ diff = this.getPathInRepository().compareTo(that.getPathInRepository());
+ if (diff != 0) return diff;
diff = this.getPathInSource().compareTo(that.getPathInSource());
if (diff != 0) return diff;
return this.getSourceName().compareTo(that.getSourceName());
@@ -183,6 +199,15 @@
*/
@Override
public String toString() {
- return this.getPathInRepository().toString() + " -> \"" +
this.getSourceName() + "\":" + this.getPathInSource();
+ StringBuilder sb = new StringBuilder();
+ if (this.getRegionName() != null) {
+ sb.append(this.getRegionName());
+ }
+ sb.append(this.getPathInRepository());
+ sb.append(" -> \"");
+ sb.append(this.getSourceName());
+ sb.append("\":");
+ sb.append(this.getPathInSource());
+ return sb.toString();
}
}
Modified:
trunk/dna-repository/src/main/java/org/jboss/dna/repository/federation/FederatedRepository.java
===================================================================
---
trunk/dna-repository/src/main/java/org/jboss/dna/repository/federation/FederatedRepository.java 2008-07-01
20:14:52 UTC (rev 323)
+++
trunk/dna-repository/src/main/java/org/jboss/dna/repository/federation/FederatedRepository.java 2008-07-01
20:17:39 UTC (rev 324)
@@ -21,7 +21,6 @@
*/
package org.jboss.dna.repository.federation;
-import java.util.Collections;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.TimeUnit;
@@ -30,16 +29,15 @@
import org.jboss.dna.repository.RepositoryI18n;
import org.jboss.dna.repository.services.AbstractServiceAdministrator;
import org.jboss.dna.repository.services.ServiceAdministrator;
-import org.jboss.dna.spi.cache.CachePolicy;
import org.jboss.dna.spi.graph.connection.ExecutionEnvironment;
import org.jboss.dna.spi.graph.connection.RepositoryConnection;
import org.jboss.dna.spi.graph.connection.RepositorySource;
import org.jboss.dna.spi.graph.connection.RepositorySourceListener;
/**
- * The component in the {@link FederationService} that represents a single federated
repository. The federated repository manages
- * a set of {@link RepositorySource federated sources}, and provides the logic of
interacting with those sources and presenting a
- * single unified graph.
+ * The component in the {@link FederationService} that represents a single federated
repository. The federated repository uses a
+ * set of {@link RepositorySource federated sources} as designated by name through the
{@link #getConfiguration() configuration},
+ * and provides the logic of interacting with those sources and presenting a single
unified graph.
*
* @author Randall Hauch
*/
@@ -97,9 +95,8 @@
private final String name;
private final ExecutionEnvironment env;
private final RepositoryConnectionFactories connectionFactories;
- private final CopyOnWriteArrayList<FederatedRegion> regions = new
CopyOnWriteArrayList<FederatedRegion>();
+ private FederatedRepositoryConfig config;
private final CopyOnWriteArrayList<RepositorySourceListener> listeners = new
CopyOnWriteArrayList<RepositorySourceListener>();
- private CachePolicy defaultCachePolicy;
/**
* Create a federated repository instance, as managed by the supplied {@link
FederationService}.
@@ -107,17 +104,21 @@
* @param repositoryName the name of the repository
* @param env the execution environment
* @param connectionFactories the set of connection factories that should be used
+ * @param config the configuration for this repository
* @throws IllegalArgumentException if any of the parameters are null, or if the name
is blank
*/
public FederatedRepository( String repositoryName,
ExecutionEnvironment env,
- RepositoryConnectionFactories connectionFactories ) {
+ RepositoryConnectionFactories connectionFactories,
+ FederatedRepositoryConfig config ) {
ArgCheck.isNotNull(connectionFactories, "connectionFactories");
ArgCheck.isNotNull(env, "env");
+ ArgCheck.isNotNull(config, "config");
ArgCheck.isNotEmpty(repositoryName, "repositoryName");
this.name = repositoryName;
this.env = env;
this.connectionFactories = connectionFactories;
+ this.config = config;
}
/**
@@ -206,32 +207,6 @@
}
/**
- * Return the unmodifiable list of bindings.
- *
- * @return the bindings
- */
- public List<FederatedRegion> getRegions() {
- return Collections.unmodifiableList(regions);
- }
-
- /**
- * Add the supplied federation region to this repository, if it is not already in the
repository. . This method does not
- * attempt to check whether this region would result in a duplicate region.
- *
- * @param region the region to be added
- * @return true if the region was added, or false if there was already a duplicate
region
- * @throws IllegalArgumentException if the binding reference is null
- */
- protected boolean addRegionIfAbsent( FederatedRegion region ) {
- ArgCheck.isNotNull(region, "region");
- return this.regions.addIfAbsent(region);
- }
-
- protected boolean removeBinding( FederatedRegion region ) {
- return this.regions.remove(region);
- }
-
- /**
* Add a listener that is to receive notifications to changes to content within this
repository. This method does nothing if
* the supplied listener is null.
*
@@ -280,25 +255,28 @@
}
/**
- * Get the default cache policy for the repository with the supplied name
+ * Get the configuration of this repository. This configuration is immutable and may
be
+ * {@link #setConfiguration(FederatedRepositoryConfig) changed} as needed. Therefore,
when using a configuration and needing a
+ * consistent configuration, maintain a reference to the configuration during that
time (as the actual configuration may be
+ * replaced at any time).
*
- * @return the default cache policy
+ * @return the repository's configuration at the time this method is called.
*/
- public CachePolicy getDefaultCachePolicy() {
- return defaultCachePolicy;
+ public FederatedRepositoryConfig getConfiguration() {
+ return config;
}
/**
- * Set the default cache policy for the federated repository.
- * <p>
- * This method can safely be called while the federation repository is in use.
- * </p>
+ * Set the configuration for this repository. The configuration is immutable and
therefore may be replaced using this method.
+ * All interaction with the configuration is done in a thread-safe and concurrent
manner, and as such only valid
+ * configurations should be used.
*
- * @param defaultCachePolicy Sets defaultCachePolicy to the specified value.
+ * @param config the new configuration
+ * @throws IllegalArgumentException if the configuration is null
*/
- public void setDefaultCachePolicy( CachePolicy defaultCachePolicy ) {
- ArgCheck.isNotNull(defaultCachePolicy, "defaultCachePolicy");
- this.defaultCachePolicy = defaultCachePolicy;
+ public void setConfiguration( FederatedRepositoryConfig config ) {
+ ArgCheck.isNotNull(config, "config");
+ this.config = config;
}
}
Added:
trunk/dna-repository/src/main/java/org/jboss/dna/repository/federation/FederatedRepositoryConfig.java
===================================================================
---
trunk/dna-repository/src/main/java/org/jboss/dna/repository/federation/FederatedRepositoryConfig.java
(rev 0)
+++
trunk/dna-repository/src/main/java/org/jboss/dna/repository/federation/FederatedRepositoryConfig.java 2008-07-01
20:17:39 UTC (rev 324)
@@ -0,0 +1,108 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, 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.dna.repository.federation;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import net.jcip.annotations.Immutable;
+import org.jboss.dna.common.collection.Problems;
+import org.jboss.dna.common.collection.ThreadSafeProblems;
+import org.jboss.dna.common.util.ArgCheck;
+import org.jboss.dna.spi.cache.CachePolicy;
+import org.jboss.dna.spi.graph.connection.RepositorySource;
+
+/**
+ * The configuration of a {@link FederatedRepository}. The configuration defines, among
other things, the set of
+ * {@link #getRegions() regions} in the federated repository that each specify how and
where content from a
+ * {@link RepositorySource source} is federated into the unified repository.
+ *
+ * @author Randall Hauch
+ */
+@Immutable
+public class FederatedRepositoryConfig {
+
+ private final List<FederatedRegion> regions;
+ private final Problems problems;
+ private final String name;
+ private final CachePolicy defaultCachePolicy;
+
+ /**
+ * Create a federated repository instance, as managed by the supplied {@link
FederationService}.
+ *
+ * @param repositoryName the name of the repository
+ * @param regions the federated regions; may not be null
+ * @param defaultCachePolicy the default cache policy for this repository; may be
null
+ * @throws IllegalArgumentException if the name is null or is blank
+ */
+ public FederatedRepositoryConfig( String repositoryName,
+ Iterable<FederatedRegion> regions,
+ CachePolicy defaultCachePolicy ) {
+ ArgCheck.isNotEmpty(repositoryName, "repositoryName");
+ this.name = repositoryName;
+ this.problems = new ThreadSafeProblems();
+ this.defaultCachePolicy = defaultCachePolicy;
+ List<FederatedRegion> regionList = new ArrayList<FederatedRegion>();
+ for (FederatedRegion region : regions) {
+ if (region != null && !regionList.contains(region))
regionList.add(region);
+ }
+ this.regions = Collections.unmodifiableList(regionList);
+ ArgCheck.isNotEmpty(this.regions, "regions");
+ }
+
+ /**
+ * Get the name of this repository
+ *
+ * @return name
+ */
+ public String getName() {
+ return this.name;
+ }
+
+ /**
+ * Return the problem associated with this configuration. These problems may change
at any time, although the returned
+ * {@link Problems} object is thread-safe.
+ *
+ * @return the thread-safe problems for this configuration
+ */
+ public Problems getProblems() {
+ return problems;
+ }
+
+ /**
+ * Return the unmodifiable list of bindings.
+ *
+ * @return the bindings
+ */
+ public List<FederatedRegion> getRegions() {
+ return regions;
+ }
+
+ /**
+ * Get the default cache policy for the repository with the supplied name
+ *
+ * @return the default cache policy
+ */
+ public CachePolicy getDefaultCachePolicy() {
+ return defaultCachePolicy;
+ }
+}
Property changes on:
trunk/dna-repository/src/main/java/org/jboss/dna/repository/federation/FederatedRepositoryConfig.java
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Modified:
trunk/dna-repository/src/main/java/org/jboss/dna/repository/federation/FederatedRepositoryConnection.java
===================================================================
---
trunk/dna-repository/src/main/java/org/jboss/dna/repository/federation/FederatedRepositoryConnection.java 2008-07-01
20:14:52 UTC (rev 323)
+++
trunk/dna-repository/src/main/java/org/jboss/dna/repository/federation/FederatedRepositoryConnection.java 2008-07-01
20:17:39 UTC (rev 324)
@@ -83,7 +83,7 @@
* {@inheritDoc}
*/
public CachePolicy getDefaultCachePolicy() {
- return this.repository.getDefaultCachePolicy();
+ return this.repository.getConfiguration().getDefaultCachePolicy();
}
/**
Modified:
trunk/dna-repository/src/main/java/org/jboss/dna/repository/federation/FederationService.java
===================================================================
---
trunk/dna-repository/src/main/java/org/jboss/dna/repository/federation/FederationService.java 2008-07-01
20:14:52 UTC (rev 323)
+++
trunk/dna-repository/src/main/java/org/jboss/dna/repository/federation/FederationService.java 2008-07-01
20:17:39 UTC (rev 324)
@@ -23,6 +23,7 @@
import java.lang.reflect.InvocationTargetException;
import java.util.Collections;
+import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -41,6 +42,9 @@
import org.jboss.dna.repository.services.AbstractServiceAdministrator;
import org.jboss.dna.repository.services.AdministeredService;
import org.jboss.dna.repository.services.ServiceAdministrator;
+import org.jboss.dna.spi.cache.BasicCachePolicy;
+import org.jboss.dna.spi.cache.CachePolicy;
+import org.jboss.dna.spi.graph.InvalidPathException;
import org.jboss.dna.spi.graph.Name;
import org.jboss.dna.spi.graph.NameFactory;
import org.jboss.dna.spi.graph.Path;
@@ -65,6 +69,11 @@
protected static final String CLASSNAME_PROPERTY_NAME = "dna:classname";
protected static final String CLASSPATH_PROPERTY_NAME = "dna:classpath";
+ protected static final String REGION_PATH_IN_REPOSITORY_PROPERTY_NAME =
"dna:pathInRepository";
+ protected static final String REGION_PATH_IN_SOURCE_PROPERTY_NAME =
"dna:pathInSource";
+ protected static final String REGION_SOURCE_NAME = "dna:sourceName";
+ protected static final String CACHE_POLICY_TIME_TO_EXPIRE =
"dna:timeToExpire";
+ protected static final String CACHE_POLICY_TIME_TO_CACHE =
"dna:timeToCache";
/**
* The administrative component for this service.
@@ -74,7 +83,7 @@
protected class Administrator extends AbstractServiceAdministrator {
protected Administrator() {
- super(RepositoryI18n.federationServiceName, State.STARTED);
+ super(RepositoryI18n.federationServiceName, State.PAUSED);
}
/**
@@ -204,6 +213,9 @@
} catch (InterruptedException err) {
I18n msg =
RepositoryI18n.interruptedWhileConnectingToFederationConfigurationRepository;
throw new FederationException(msg.text(configurationSourceName));
+ } catch (Throwable err) {
+ I18n msg =
RepositoryI18n.unableToConnectToFederationConfigurationRepository;
+ throw new FederationException(msg.text(configurationSourceName));
}
//
------------------------------------------------------------------------------------
@@ -211,31 +223,41 @@
//
------------------------------------------------------------------------------------
ValueFactories valueFactories = env.getValueFactories();
PathFactory pathFactory = valueFactories.getPathFactory();
+ NameFactory nameFactory = valueFactories.getNameFactory();
+ // The root of the configuration repository should be converted using the
configuration region
+ // to the path in the source (since we're talking directly to the source)
...
+ Path configurationRoot = pathFactory.create("/");
+ configurationRoot =
configurationRegion.convertPathInRepositoryToPathInSource(configurationRoot,
pathFactory);
+
// Read the configuration and the repository sources, located as child
nodes/branches under "/dna:sources",
// and then instantiate and register each in the "sources" manager
try {
- Path sourcesNode = pathFactory.create("/dna:sources");
+ Path sourcesNode = pathFactory.create(configurationRoot,
nameFactory.create("dna:sources"));
BasicGetChildrenCommand getSources = new
BasicGetChildrenCommand(sourcesNode);
configurationConnection.execute(env, getSources);
+ if (getSources.hasNoError()) {
- // Build the commands to get each of the children ...
- List<Path.Segment> children = getSources.getChildren();
- if (children.isEmpty()) {
- BasicCompositeCommand commands = new BasicCompositeCommand();
- for (Path.Segment child : getSources.getChildren()) {
- final Path pathToSource = pathFactory.create(sourcesNode,
child);
- commands.add(new BasicGetNodeCommand(pathToSource));
- }
- configurationConnection.execute(env, commands);
+ // Build the commands to get each of the children ...
+ List<Path.Segment> children = getSources.getChildren();
+ if (!children.isEmpty()) {
+ BasicCompositeCommand commands = new BasicCompositeCommand();
+ for (Path.Segment child : getSources.getChildren()) {
+ final Path pathToSource = pathFactory.create(sourcesNode,
child);
+ commands.add(new BasicGetNodeCommand(pathToSource));
+ }
+ configurationConnection.execute(env, commands);
- // Iterate over each source node obtained ...
- for (GraphCommand command : commands) {
- BasicGetNodeCommand getSourceCommand =
(BasicGetNodeCommand)command;
- RepositorySource source =
createRepositorySource(getSourceCommand.getPath(),
-
getSourceCommand.getProperties(),
- problems);
- if (source != null) sources.addSource(source);
+ // Iterate over each source node obtained ...
+ for (GraphCommand command : commands) {
+ BasicGetNodeCommand getSourceCommand =
(BasicGetNodeCommand)command;
+ if (getSourceCommand.hasNoError()) {
+ RepositorySource source =
createRepositorySource(getSourceCommand.getPath(),
+
getSourceCommand.getProperties(),
+
problems);
+ if (source != null) sources.addSource(source, false);
+ }
+ }
}
}
} catch (InterruptedException err) {
@@ -268,14 +290,12 @@
if (classnameProperty == null) {
problems.addError(RepositoryI18n.requiredPropertyIsMissingFromNode,
CLASSNAME_PROPERTY_NAME, path);
}
- if (classpathProperty == null) {
- problems.addError(RepositoryI18n.requiredPropertyIsMissingFromNode,
CLASSPATH_PROPERTY_NAME, path);
- }
+ // If the classpath property is null or empty, the default classpath will be
used
if (problems.hasErrors()) return null;
// Create the instance ...
String classname = stringFactory.create(classnameProperty.getValues().next());
- String[] classpath = stringFactory.create(classpathProperty.getValuesAsArray());
+ String[] classpath = classpathProperty == null ? new String[] {} :
stringFactory.create(classpathProperty.getValuesAsArray());
ClassLoader classLoader = this.classLoaderFactory.getClassLoader(classpath);
RepositorySource source = null;
try {
@@ -289,8 +309,23 @@
problems.addError(err, RepositoryI18n.unableToInstantiateClassUsingClasspath,
classname, classpath);
}
+ // Try to set the name property to the local name of the node...
+ Reflection reflection = new Reflection(source.getClass());
+ try {
+ reflection.invokeSetterMethodOnTarget("name", source,
path.getLastSegment().getName().getLocalName());
+ } catch (SecurityException err) {
+ // Do nothing ... assume not a JavaBean property
+ } catch (NoSuchMethodException err) {
+ // Do nothing ... assume not a JavaBean property
+ } catch (IllegalArgumentException err) {
+ // Do nothing ... assume not a JavaBean property
+ } catch (IllegalAccessException err) {
+ // Do nothing ... assume not a JavaBean property
+ } catch (InvocationTargetException err) {
+ // Do nothing ... assume not a JavaBean property
+ }
+
// Now set all the properties that we can, ignoring any property that doesn't
fit pattern ...
- Reflection reflection = new Reflection(source.getClass());
for (Map.Entry<Name, Property> entry : properties.entrySet()) {
Name propertyName = entry.getKey();
Property property = entry.getValue();
@@ -332,10 +367,12 @@
FederatedRepository repository = this.repositories.get(name);
if (repository == null) {
// Look up the node representing the repository in the configuration ...
+ FederatedRepositoryConfig config = loadConfiguration(name);
+ assert config != null;
- // // New up a repository and configure it ...
- // Repository
- repository = new FederatedRepository(name, env, sources);
+ // New up a repository and configure it ...
+ repository = new FederatedRepository(name, env, sources, config);
+
// Now register it, being careful to not overwrite any added since
"get" call above ..
FederatedRepository existingRepository = this.repositories.putIfAbsent(name,
repository);
if (existingRepository != null) repository = existingRepository;
@@ -345,6 +382,117 @@
return repository;
}
+ protected FederatedRepositoryConfig loadConfiguration( String repositoryName ) {
+ Problems problems = new SimpleProblems();
+ ValueFactories valueFactories = env.getValueFactories();
+ PathFactory pathFactory = valueFactories.getPathFactory();
+ NameFactory nameFactory = valueFactories.getNameFactory();
+ ValueFactory<Long> longFactory = valueFactories.getLongFactory();
+
+ // The root of the configuration repository should be converted using the
configuration region
+ // to the path in the source (since we're talking directly to the source)
...
+ Path configurationRoot = pathFactory.create("/dna:repositories");
+ configurationRoot =
configurationRegion.convertPathInRepositoryToPathInSource(configurationRoot,
pathFactory);
+ Path repositoryNode = pathFactory.create(configurationRoot,
nameFactory.create(repositoryName));
+
+ try {
+ // Get the repository node ...
+ BasicGetNodeCommand getRepository = new BasicGetNodeCommand(repositoryNode);
+
+ // Get the regions for the repository ...
+ Path regionsNode = pathFactory.create(repositoryNode,
nameFactory.create("dna:regions"));
+ BasicGetChildrenCommand getRegions = new
BasicGetChildrenCommand(regionsNode);
+
+ configurationConnection.execute(env, getRepository, getRegions);
+ if (getRepository.hasError()) {
+ throw new
FederationException(RepositoryI18n.federatedRepositoryCannotBeFound.text(repositoryName));
+ }
+
+ // Build the commands to get each of the region branches ...
+ List<FederatedRegion> regions = new
LinkedList<FederatedRegion>();
+ if (getRegions.hasNoError() && !getRegions.getChildren().isEmpty())
{
+ BasicCompositeCommand commands = new BasicCompositeCommand();
+ for (Path.Segment child : getRegions.getChildren()) {
+ final Path pathToSource = pathFactory.create(regionsNode, child);
+ commands.add(new BasicGetNodeCommand(pathToSource));
+ }
+ configurationConnection.execute(env, commands);
+
+ // Iterate over each region node obtained ...
+ for (GraphCommand command : commands) {
+ BasicGetNodeCommand getRegionCommand = (BasicGetNodeCommand)command;
+ if (getRegionCommand.hasNoError()) {
+ FederatedRegion region =
createRegion(getRegionCommand.getPath(),
+
getRegionCommand.getProperties(),
+ problems);
+ if (region != null) regions.add(region);
+ }
+ }
+ }
+
+ // Look for the default cache policy ...
+ BasicCachePolicy cachePolicy = new BasicCachePolicy();
+ Property timeToExpireProperty =
getRepository.getProperties().get(nameFactory.create(CACHE_POLICY_TIME_TO_EXPIRE));
+ Property timeToCacheProperty =
getRepository.getProperties().get(nameFactory.create(CACHE_POLICY_TIME_TO_CACHE));
+ if (timeToCacheProperty != null && !timeToCacheProperty.isEmpty()) {
+
cachePolicy.setTimeToCache(longFactory.create(timeToCacheProperty.getValues().next()));
+ }
+ if (timeToExpireProperty != null && !timeToExpireProperty.isEmpty())
{
+
cachePolicy.setTimeToExpire(longFactory.create(timeToExpireProperty.getValues().next()));
+ }
+ CachePolicy defaultCachePolicy = cachePolicy.isEmpty() ? null :
cachePolicy.getUnmodifiable();
+ return new FederatedRepositoryConfig(repositoryName, regions,
defaultCachePolicy);
+ } catch (InvalidPathException err) {
+ I18n msg = RepositoryI18n.federatedRepositoryCannotBeFound;
+ throw new FederationException(msg.text(repositoryName));
+ } catch (InterruptedException err) {
+ I18n msg =
RepositoryI18n.interruptedWhileUsingFederationConfigurationRepository;
+ throw new FederationException(msg.text(repositoryName));
+ }
+
+ }
+
+ /**
+ * Instantiate the {@link FederatedRegion} described by the supplied properties.
+ *
+ * @param path the path to the node where these properties were found; never null
+ * @param properties the properties; never null
+ * @param problems the problems container in which any problems should be reported;
never null
+ * @return the region instance, or null if it could not be created
+ */
+ @SuppressWarnings( "null" )
+ protected FederatedRegion createRegion( Path path,
+ Map<Name, Property> properties,
+ Problems problems ) {
+ ValueFactories valueFactories = env.getValueFactories();
+ PathFactory pathFactory = valueFactories.getPathFactory();
+ NameFactory nameFactory = valueFactories.getNameFactory();
+ ValueFactory<String> stringFactory = valueFactories.getStringFactory();
+
+ String regionName = path.getLastSegment().getName().getLocalName();
+ Path pathInRepository = pathFactory.createRootPath();
+ Path pathInSource = pathInRepository;
+
+ // Get the classname and classpath ...
+ Property pathInRepositoryProperty =
properties.get(nameFactory.create(REGION_PATH_IN_REPOSITORY_PROPERTY_NAME));
+ Property pathInSourceProperty =
properties.get(nameFactory.create(REGION_PATH_IN_SOURCE_PROPERTY_NAME));
+ Property sourceNameProperty =
properties.get(nameFactory.create(REGION_SOURCE_NAME));
+ if (pathInRepositoryProperty != null &&
!pathInRepositoryProperty.isEmpty()) {
+ pathInRepository =
pathFactory.create(pathInRepositoryProperty.iterator().next());
+ }
+ if (pathInSourceProperty != null && !pathInSourceProperty.isEmpty()) {
+ pathInSource = pathFactory.create(pathInSourceProperty.iterator().next());
+ }
+ if (sourceNameProperty == null || sourceNameProperty.isEmpty()) {
+ problems.addError(RepositoryI18n.requiredPropertyIsMissingFromNode,
CLASSNAME_PROPERTY_NAME, path);
+ }
+ if (problems.hasErrors()) return null;
+
+ String sourceName = stringFactory.create(sourceNameProperty.iterator().next());
+ FederatedRegion region = new FederatedRegion(regionName, pathInRepository,
pathInSource, sourceName);
+ return region;
+ }
+
protected synchronized void shutdownService() {
if (this.configurationConnection != null) {
try {
Modified:
trunk/dna-repository/src/main/java/org/jboss/dna/repository/federation/RepositorySourceManager.java
===================================================================
---
trunk/dna-repository/src/main/java/org/jboss/dna/repository/federation/RepositorySourceManager.java 2008-07-01
20:14:52 UTC (rev 323)
+++
trunk/dna-repository/src/main/java/org/jboss/dna/repository/federation/RepositorySourceManager.java 2008-07-01
20:17:39 UTC (rev 324)
@@ -247,17 +247,23 @@
* </p>
*
* @param source the source to add
+ * @param force true if the valid source should be added even if there is an existing
source with the supplied name
* @return true if the source is added, or false if the reference is null or if there
is already an existing source with the
* supplied name.
*/
- public boolean addSource( RepositorySource source ) {
+ public boolean addSource( RepositorySource source,
+ boolean force ) {
if (source == null) return false;
try {
this.sourcesWriteLock.lock();
for (RepositorySource existingSource : this.sources.values()) {
if (existingSource.getName().equals(source.getName())) return false;
}
- this.sources.put(source.getName(), source);
+ if (force) {
+ this.sources.put(source.getName(), source);
+ } else {
+ this.sources.putIfAbsent(source.getName(), source);
+ }
} finally {
this.sourcesWriteLock.unlock();
}
Modified:
trunk/dna-repository/src/main/resources/org/jboss/dna/repository/RepositoryI18n.properties
===================================================================
---
trunk/dna-repository/src/main/resources/org/jboss/dna/repository/RepositoryI18n.properties 2008-07-01
20:14:52 UTC (rev 323)
+++
trunk/dna-repository/src/main/resources/org/jboss/dna/repository/RepositoryI18n.properties 2008-07-01
20:17:39 UTC (rev 324)
@@ -104,8 +104,10 @@
interruptedWhileConnectingToFederationConfigurationRepository = Interrupted while
connecting to federation configuration repository "{0}"
interruptedWhileUsingFederationConfigurationRepository = Interrupted while using
federation configuration repository "{0}"
interruptedWhileClosingConnectionToFederationConfigurationRepository = Interrupted while
closing connection to federation configuration repository "{0}"
+federatedRepositoryCannotBeFound = The federated repository "{0}" cannot be
found
+unableToConnectToFederationConfigurationRepository = Unable to connect to federation
configuration repository "{0}"
unableToFindRepositorySourceByName = Unable to find the repository source
"{0}"
-unableToCreateConnectionToFederatedRepository = Unable to create a connection to the
repository "{0}". Check the Federation Service configuration.
+unableToCreateConnectionToFederatedRepository = Unable to connect to the repository
"{0}". Check the Federation Service configuration.
unableToAuthenticateConnectionToFederatedRepository = Unable to authenticate
"{1}" for repository "{0}"
repositoryHasBeenShutDown = The "{0}" repository has been shut down and may no
longer be used.
repositoryPathInFederationBindingIsNotAbsolute = The repository path in a federation
binding must be absolute, but was "{0}"
Modified:
trunk/dna-repository/src/test/java/org/jboss/dna/repository/federation/FederatedRepositoryConnectionTest.java
===================================================================
---
trunk/dna-repository/src/test/java/org/jboss/dna/repository/federation/FederatedRepositoryConnectionTest.java 2008-07-01
20:14:52 UTC (rev 323)
+++
trunk/dna-repository/src/test/java/org/jboss/dna/repository/federation/FederatedRepositoryConnectionTest.java 2008-07-01
20:17:39 UTC (rev 324)
@@ -23,7 +23,6 @@
import static org.hamcrest.core.Is.is;
import static org.hamcrest.core.IsNull.nullValue;
-import static org.hamcrest.core.IsSame.sameInstance;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.stub;
@@ -31,7 +30,6 @@
import static org.mockito.Mockito.verify;
import java.util.concurrent.TimeUnit;
import org.jboss.dna.repository.services.ServiceAdministrator;
-import org.jboss.dna.spi.cache.CachePolicy;
import org.jboss.dna.spi.graph.commands.GraphCommand;
import org.jboss.dna.spi.graph.connection.ExecutionEnvironment;
import org.jboss.dna.spi.graph.connection.RepositorySourceException;
@@ -53,8 +51,6 @@
@Mock
private FederatedRepository repository;
@Mock
- private CachePolicy defaultCachePolicy;
- @Mock
private ServiceAdministrator repositoryAdmin;
@Before
@@ -62,7 +58,6 @@
MockitoAnnotations.initMocks(this);
sourceName = "Source X";
stub(source.getName()).toReturn(sourceName);
- stub(repository.getDefaultCachePolicy()).toReturn(defaultCachePolicy);
stub(repository.getAdministrator()).toReturn(repositoryAdmin);
connection = new FederatedRepositoryConnection(repository, source);
}
@@ -82,12 +77,6 @@
}
@Test
- public void shouldGetDefaultCachePolicyFromRepository() {
- assertThat(connection.getDefaultCachePolicy(),
is(sameInstance(defaultCachePolicy)));
- verify(repository, times(1)).getDefaultCachePolicy();
- }
-
- @Test
public void shouldHaveNoXaResource() {
assertThat(connection.getXAResource(), is(nullValue()));
}
Modified:
trunk/dna-repository/src/test/java/org/jboss/dna/repository/federation/FederatedRepositoryTest.java
===================================================================
---
trunk/dna-repository/src/test/java/org/jboss/dna/repository/federation/FederatedRepositoryTest.java 2008-07-01
20:14:52 UTC (rev 323)
+++
trunk/dna-repository/src/test/java/org/jboss/dna/repository/federation/FederatedRepositoryTest.java 2008-07-01
20:17:39 UTC (rev 324)
@@ -26,7 +26,6 @@
import static org.hamcrest.core.IsSame.sameInstance;
import static org.junit.Assert.assertThat;
import static org.junit.matchers.JUnitMatchers.hasItems;
-import static org.mockito.Mockito.mock;
import org.jboss.dna.spi.graph.connection.BasicExecutionEnvironment;
import org.jboss.dna.spi.graph.connection.ExecutionEnvironment;
import org.jboss.dna.spi.graph.connection.RepositorySourceListener;
@@ -44,6 +43,8 @@
private FederatedRepository repository;
private String name;
@Mock
+ private FederatedRepositoryConfig config;
+ @Mock
private RepositorySourceListener listener1;
@Mock
private RepositorySourceListener listener2;
@@ -57,7 +58,7 @@
MockitoAnnotations.initMocks(this);
env = new BasicExecutionEnvironment();
name = "Test repository";
- repository = new FederatedRepository(name, env, connectionFactories);
+ repository = new FederatedRepository(name, env, connectionFactories, config);
}
@Test
@@ -137,32 +138,9 @@
}
@Test
- public void shouldHaveNoFederationRegionsAfterInitialization() {
- assertThat(repository.getRegions(), is(notNullValue()));
- assertThat(repository.getRegions().isEmpty(), is(true));
+ public void shouldHaveConfigurationAfterInitialization() {
+ assertThat(repository.getConfiguration(), is(notNullValue()));
+ assertThat(repository.getConfiguration(), is(sameInstance(config)));
}
- @Test
- public void shouldAddFederationRegionThatIsNotAlreadyRegistered() {
- FederatedRegion region1 = mock(FederatedRegion.class);
- FederatedRegion region2 = mock(FederatedRegion.class);
-
- assertThat(repository.getRegions().isEmpty(), is(true));
- assertThat(repository.addRegionIfAbsent(region1), is(true));
- assertThat(repository.getRegions(), hasItems(region1));
- assertThat(repository.addRegionIfAbsent(region2), is(true));
- assertThat(repository.getRegions(), hasItems(region1, region2));
- }
-
- @Test
- public void shouldNotAddSourceThatIsAlreadyRegistered() {
- FederatedRegion region1 = mock(FederatedRegion.class);
-
- assertThat(repository.getRegions().isEmpty(), is(true));
- assertThat(repository.addRegionIfAbsent(region1), is(true));
- assertThat(repository.getRegions(), hasItems(region1));
- assertThat(repository.addRegionIfAbsent(region1), is(false));
- assertThat(repository.getRegions(), hasItems(region1));
- }
-
}
Modified:
trunk/dna-repository/src/test/java/org/jboss/dna/repository/federation/FederationServiceTest.java
===================================================================
---
trunk/dna-repository/src/test/java/org/jboss/dna/repository/federation/FederationServiceTest.java 2008-07-01
20:14:52 UTC (rev 323)
+++
trunk/dna-repository/src/test/java/org/jboss/dna/repository/federation/FederationServiceTest.java 2008-07-01
20:17:39 UTC (rev 324)
@@ -22,17 +22,31 @@
package org.jboss.dna.repository.federation;
import static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsInstanceOf.instanceOf;
import static org.hamcrest.core.IsNull.notNullValue;
+import static org.hamcrest.core.IsNull.nullValue;
import static org.hamcrest.core.IsSame.sameInstance;
import static org.junit.Assert.assertThat;
+import static org.junit.matchers.JUnitMatchers.hasItems;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.stub;
-import org.jboss.dna.repository.util.ExecutionContext;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import java.util.concurrent.TimeUnit;
+import org.jboss.dna.common.component.ClassLoaderFactory;
+import org.jboss.dna.repository.services.ServiceAdministrator;
+import org.jboss.dna.spi.graph.NamespaceRegistry;
import org.jboss.dna.spi.graph.Path;
import org.jboss.dna.spi.graph.PathFactory;
+import org.jboss.dna.spi.graph.PropertyFactory;
+import org.jboss.dna.spi.graph.connection.ExecutionEnvironment;
+import org.jboss.dna.spi.graph.connection.RepositorySource;
import org.jboss.dna.spi.graph.impl.BasicNamespaceRegistry;
+import org.jboss.dna.spi.graph.impl.BasicPropertyFactory;
import org.jboss.dna.spi.graph.impl.StandardValueFactories;
+import org.junit.After;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Test;
import org.mockito.MockitoAnnotations;
import org.mockito.MockitoAnnotations.Mock;
@@ -42,46 +56,250 @@
*/
public class FederationServiceTest {
+ public static final String CLASSNAME = FederationService.CLASSNAME_PROPERTY_NAME;
+ public static final String CLASSPATH = FederationService.CLASSPATH_PROPERTY_NAME;
+ public static final String PATH_IN_REPOSITORY =
FederationService.REGION_PATH_IN_REPOSITORY_PROPERTY_NAME;
+ public static final String PATH_IN_SOURCE =
FederationService.REGION_PATH_IN_SOURCE_PROPERTY_NAME;
+ public static final String SOURCE_NAME = FederationService.REGION_SOURCE_NAME;
+ public static final String TIME_TO_EXPIRE =
FederationService.CACHE_POLICY_TIME_TO_EXPIRE;
+ public static final String TIME_TO_CACHE =
FederationService.CACHE_POLICY_TIME_TO_CACHE;
+
private FederationService service;
private FederatedRegion configRegion;
private StandardValueFactories valueFactories;
+ private PropertyFactory propertyFactory;
private PathFactory pathFactory;
private String configSourceName;
+ private SimpleRepository configRepository;
+ private SimpleRepositorySource configRepositorySource;
@Mock
- private ExecutionContext env;
+ private ExecutionEnvironment env;
@Mock
private RepositorySourceManager sources;
@Before
public void beforeEach() throws Exception {
MockitoAnnotations.initMocks(this);
+ NamespaceRegistry registry = new BasicNamespaceRegistry();
+ registry.register("dna", "http://www.jboss.org/dna");
+ valueFactories = new StandardValueFactories(registry);
+ pathFactory = valueFactories.getPathFactory();
+ propertyFactory = new BasicPropertyFactory(valueFactories);
+ Path pathInRepository = pathFactory.create("/");
+ Path pathInSource = pathFactory.create("/reposX");
configSourceName = "configSource";
- valueFactories = new StandardValueFactories(new BasicNamespaceRegistry());
- pathFactory = valueFactories.getPathFactory();
- Path pathInRepository = pathFactory.create("/dna:system");
- Path pathInSource = pathFactory.create("/some/root");
- configRegion = new FederatedRegion(pathInRepository, pathInSource,
configSourceName);
+ configRegion = new FederatedRegion("config region", pathInRepository,
pathInSource, configSourceName);
+ configRepository = new SimpleRepository("Configuration Repository");
+ configRepositorySource = new SimpleRepositorySource();
+ configRepositorySource.setRepositoryName(configRepository.getRepositoryName());
+ configRepositorySource.setName(configSourceName);
+
stub(sources.getConnectionFactory(configSourceName)).toReturn(configRepositorySource);
stub(env.getValueFactories()).toReturn(valueFactories);
+ stub(env.getPropertyFactory()).toReturn(propertyFactory);
+ stub(env.getNamespaceRegistry()).toReturn(registry);
service = new FederationService(sources, configRegion, env, null);
}
- @Ignore
+ @After
+ public void afterEach() throws Exception {
+ service.getAdministrator().shutdown();
+ service.getAdministrator().awaitTermination(2, TimeUnit.SECONDS);
+ SimpleRepository.shutdownAll();
+ }
+
@Test
public void shouldHaveServiceAdministratorAfterInstantiation() {
assertThat(service.getAdministrator(), is(notNullValue()));
}
- @Ignore
@Test
public void shouldHaveConfigurationRegionAfterInstantiation() {
assertThat(service.getConfigurationRegion(), is(notNullValue()));
assertThat(service.getConfigurationRegion(), is(sameInstance(configRegion)));
}
- @Ignore
@Test
+ public void shouldHaveAnExecutionEnvironmentAfterInstantiation() {
+ assertThat(service.getExecutionEnvironment(), is(notNullValue()));
+ assertThat(service.getExecutionEnvironment(), is(sameInstance(env)));
+ }
+
+ @Test
public void
shouldHaveNonNullClassLoaderFactoryAfterInstantiatingWithNullClassLoaderFactoryReference()
{
assertThat(service.getClassLoaderFactory(), is(notNullValue()));
}
+ @Test
+ public void
shouldHaveNonNullClassLoaderFactoryAfterInstantiatingWithClassLoaderFactoryReference() {
+ ClassLoaderFactory classLoaderFactory = mock(ClassLoaderFactory.class);
+ service = new FederationService(sources, configRegion, env, classLoaderFactory);
+ assertThat(service.getClassLoaderFactory(), is(notNullValue()));
+ assertThat(service.getClassLoaderFactory(),
is(sameInstance(classLoaderFactory)));
+ }
+
+ @Test
+ public void shouldHaveNullJndiNameAfterInstantiation() {
+ assertThat(service.getJndiName(), is(nullValue()));
+ }
+
+ @Test
+ public void shouldAllowShuttingDownBeforeStartingUp() throws Exception {
+ assertThat(service.getAdministrator().getState(),
is(ServiceAdministrator.State.PAUSED));
+ service.getAdministrator().shutdown();
+ service.getAdministrator().awaitTermination(1, TimeUnit.SECONDS);
+ assertThat(service.getAdministrator().getState(),
is(ServiceAdministrator.State.TERMINATED));
+ }
+
+ @Test( expected = FederationException.class )
+ public void shouldFailToStartUpIfConfigurationRepositorySourceIsNotFound() {
+ stub(sources.getConnectionFactory(configSourceName)).toReturn(null);
+ service.getAdministrator().start();
+ }
+
+ @Test( expected = FederationException.class )
+ public void shouldFailToStartUpIfUnableToConnectToConfigurationRepository() throws
Exception {
+ RepositorySource mockSource = mock(RepositorySource.class);
+ stub(sources.getConnectionFactory(configSourceName)).toReturn(mockSource);
+ stub(mockSource.getConnection()).toThrow(new UnsupportedOperationException());
+ service.getAdministrator().start();
+ }
+
+ @Test( expected = FederationException.class )
+ public void
shouldFailToStartUpIfInterruptedWhileConnectingToConfigurationRepository() throws
Exception {
+ RepositorySource mockSource = mock(RepositorySource.class);
+ stub(sources.getConnectionFactory(configSourceName)).toReturn(mockSource);
+ stub(mockSource.getConnection()).toThrow(new InterruptedException());
+ service.getAdministrator().start();
+ }
+
+ @Test
+ public void shouldStartUpUsingConfigurationRepositoryThatContainsSomeSources() {
+ // Use a real source manager for this test ...
+ sources = new RepositorySourceManager(sources);
+ sources.addSource(configRepositorySource, true);
+ assertThat(sources.getSources(),
hasItems((RepositorySource)configRepositorySource));
+ assertThat(sources.getSources().size(), is(1));
+ service = new FederationService(sources, configRegion, env, null);
+
+ // Set up the configuration repository to contain 3 sources ...
+ configRepository.create(env, "/reposX/dna:sources");
+ configRepository.setProperty(env, "/reposX/dna:sources/source A",
CLASSNAME, SimpleRepositorySource.class.getName());
+ configRepository.setProperty(env, "/reposX/dna:sources/source A",
CLASSPATH, "");
+ configRepository.setProperty(env, "/reposX/dna:sources/source A",
"repositoryName", "sourceReposA");
+ configRepository.setProperty(env, "/reposX/dna:sources/source A",
"retryLimit", 3);
+
+ configRepository.setProperty(env, "/reposX/dna:sources/source B",
CLASSNAME, SimpleRepositorySource.class.getName());
+ configRepository.setProperty(env, "/reposX/dna:sources/source B",
CLASSPATH, "");
+ configRepository.setProperty(env, "/reposX/dna:sources/source B",
"repositoryName", "sourceReposB");
+
+ configRepository.setProperty(env, "/reposX/dna:sources/source C",
CLASSNAME, SimpleRepositorySource.class.getName());
+ configRepository.setProperty(env, "/reposX/dna:sources/source C",
CLASSPATH, "");
+ configRepository.setProperty(env, "/reposX/dna:sources/source C",
"repositoryName", "sourceReposC");
+
+ // Now, start up the service ...
+ service.getAdministrator().start();
+
+ // and verify that the sources were added to the manager...
+ assertThat(sources.getSources().size(), is(4));
+ assertThat(sources.getConnectionFactory("source A"),
is(instanceOf(SimpleRepositorySource.class)));
+ assertThat(sources.getConnectionFactory("source B"),
is(instanceOf(SimpleRepositorySource.class)));
+ assertThat(sources.getConnectionFactory("source C"),
is(instanceOf(SimpleRepositorySource.class)));
+
+ SimpleRepositorySource sourceA =
(SimpleRepositorySource)sources.getConnectionFactory("source A");
+ assertThat(sourceA.getName(), is("source A"));
+ assertThat(sourceA.getRepositoryName(), is("sourceReposA"));
+ assertThat(sourceA.getRetryLimit(), is(3));
+
+ SimpleRepositorySource sourceB =
(SimpleRepositorySource)sources.getConnectionFactory("source B");
+ assertThat(sourceB.getName(), is("source B"));
+ assertThat(sourceB.getRepositoryName(), is("sourceReposB"));
+ assertThat(sourceB.getRetryLimit(),
is(SimpleRepositorySource.DEFAULT_RETRY_LIMIT));
+
+ SimpleRepositorySource sourceC =
(SimpleRepositorySource)sources.getConnectionFactory("source C");
+ assertThat(sourceC.getName(), is("source C"));
+ assertThat(sourceC.getRepositoryName(), is("sourceReposC"));
+ assertThat(sourceC.getRetryLimit(),
is(SimpleRepositorySource.DEFAULT_RETRY_LIMIT));
+ }
+
+ @Test
+ public void shouldStartUpUsingConfigurationRepositoryThatContainsNoSources() {
+ // Set up the configuration repository to contain NO sources ...
+ configRepository.create(env, "/reposX/dna:sources");
+
+ // Now, start up the service ...
+ service.getAdministrator().start();
+
+ // and verify that the configuration source was obtained from the manager ...
+ verify(sources, times(1)).getConnectionFactory(configSourceName);
+
+ // and verify that the sources were never added to the manager...
+ verifyNoMoreInteractions(sources);
+ }
+
+ @Test
+ public void
shouldStartUpAndCreateRepositoryUsingConfigurationRepositoryThatContainsNoSources() {
+ // Set up the configuration repository ...
+ configRepository.create(env, "/reposX/dna:sources");
+ configRepository.setProperty(env, "/reposX/dna:sources/source A",
CLASSNAME, SimpleRepositorySource.class.getName());
+ configRepository.setProperty(env, "/reposX/dna:sources/source A",
CLASSPATH, "");
+ configRepository.setProperty(env, "/reposX/dna:sources/source A",
"repositoryName", "sourceReposA");
+ configRepository.setProperty(env, "/reposX/dna:sources/source A",
"retryLimit", 3);
+
+ String fedReposPath = "/reposX/dna:repositories/fed repos/";
+ configRepository.setProperty(env, fedReposPath, TIME_TO_CACHE,
"10000");
+ configRepository.setProperty(env, fedReposPath, TIME_TO_EXPIRE,
"20000");
+ configRepository.setProperty(env, fedReposPath + "dna:regions/region1",
PATH_IN_REPOSITORY, "/a/b/c");
+ configRepository.setProperty(env, fedReposPath + "dna:regions/region1",
PATH_IN_SOURCE, "/sx/sy");
+ configRepository.setProperty(env, fedReposPath + "dna:regions/region1",
SOURCE_NAME, "source A");
+ configRepository.setProperty(env, fedReposPath + "dna:regions/region2",
PATH_IN_REPOSITORY, "/");
+ configRepository.setProperty(env, fedReposPath + "dna:regions/region2",
PATH_IN_SOURCE, "/");
+ configRepository.setProperty(env, fedReposPath + "dna:regions/region2",
SOURCE_NAME, "source B");
+ configRepository.setProperty(env, fedReposPath + "dna:regions/region3",
PATH_IN_REPOSITORY, "/d/e/f");
+ configRepository.setProperty(env, fedReposPath + "dna:regions/region3",
PATH_IN_SOURCE, "/");
+ configRepository.setProperty(env, fedReposPath + "dna:regions/region3",
SOURCE_NAME, "source C");
+ configRepository.setProperty(env, fedReposPath + "dna:regions/region4",
PATH_IN_REPOSITORY, "/");
+ configRepository.setProperty(env, fedReposPath + "dna:regions/region4",
PATH_IN_SOURCE, "/x/y/z");
+ configRepository.setProperty(env, fedReposPath + "dna:regions/region4",
SOURCE_NAME, "source A");
+
+ // Now, start up the service ...
+ service.getAdministrator().start();
+
+ // Create the repository ...
+ FederatedRepository repository = service.getRepository("fed repos");
+ assertThat(repository, is(notNullValue()));
+ assertThat(repository.getName(), is("fed repos"));
+
assertThat(repository.getConfiguration().getDefaultCachePolicy().getTimeToCache(),
is(10000l));
+
assertThat(repository.getConfiguration().getDefaultCachePolicy().getTimeToExpire(),
is(20000l));
+ assertThat(repository.getConfiguration().getRegions().get(0).getRegionName(),
is("region1"));
+
assertThat(repository.getConfiguration().getRegions().get(0).getPathInRepository(),
is(pathFactory.create("/a/b/c")));
+ assertThat(repository.getConfiguration().getRegions().get(0).getPathInSource(),
is(pathFactory.create("/sx/sy")));
+ assertThat(repository.getConfiguration().getRegions().get(0).getSourceName(),
is("source A"));
+ assertThat(repository.getConfiguration().getRegions().get(1).getRegionName(),
is("region2"));
+
assertThat(repository.getConfiguration().getRegions().get(1).getPathInRepository(),
is(pathFactory.create("/")));
+ assertThat(repository.getConfiguration().getRegions().get(1).getPathInSource(),
is(pathFactory.create("/")));
+ assertThat(repository.getConfiguration().getRegions().get(1).getSourceName(),
is("source B"));
+ assertThat(repository.getConfiguration().getRegions().get(2).getRegionName(),
is("region3"));
+
assertThat(repository.getConfiguration().getRegions().get(2).getPathInRepository(),
is(pathFactory.create("/d/e/f")));
+ assertThat(repository.getConfiguration().getRegions().get(2).getPathInSource(),
is(pathFactory.create("/")));
+ assertThat(repository.getConfiguration().getRegions().get(2).getSourceName(),
is("source C"));
+ assertThat(repository.getConfiguration().getRegions().get(3).getRegionName(),
is("region4"));
+
assertThat(repository.getConfiguration().getRegions().get(3).getPathInRepository(),
is(pathFactory.create("/")));
+ assertThat(repository.getConfiguration().getRegions().get(3).getPathInSource(),
is(pathFactory.create("/x/y/z")));
+ assertThat(repository.getConfiguration().getRegions().get(3).getSourceName(),
is("source A"));
+
+ for (int i = 0; i != 10; ++i) {
+ assertThat(service.getRepository("fed repos"),
is(sameInstance(repository)));
+ }
+ }
+
+ @Test( expected = FederationException.class )
+ public void
shouldThrowExceptionWhenGettingRepositoryThatDoesNotExistInConfigurationRepository() {
+ // No sources, no repositories in configuration repository ...
+
+ // Now, start up the service ...
+ service.getAdministrator().start();
+
+ // Get the non-existant repository ...
+ service.getRepository("non-existant repos");
+ }
}
Added:
trunk/dna-repository/src/test/java/org/jboss/dna/repository/federation/SimpleRepository.java
===================================================================
---
trunk/dna-repository/src/test/java/org/jboss/dna/repository/federation/SimpleRepository.java
(rev 0)
+++
trunk/dna-repository/src/test/java/org/jboss/dna/repository/federation/SimpleRepository.java 2008-07-01
20:17:39 UTC (rev 324)
@@ -0,0 +1,178 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, 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.dna.repository.federation;
+
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import net.jcip.annotations.ThreadSafe;
+import org.jboss.dna.spi.graph.Name;
+import org.jboss.dna.spi.graph.NameFactory;
+import org.jboss.dna.spi.graph.Path;
+import org.jboss.dna.spi.graph.PathFactory;
+import org.jboss.dna.spi.graph.Property;
+import org.jboss.dna.spi.graph.PropertyFactory;
+import org.jboss.dna.spi.graph.connection.ExecutionEnvironment;
+
+/**
+ * A very simple repository that maintains properties for nodes identified by a path, and
computes the children based upon the set
+ * of paths registered in the {@link #getData() data}.
+ * <p>
+ * Note that the repository does not automatically rename same-name siblings when nodes
are
+ * {@link #delete(ExecutionEnvironment, String) deleted} or {@link
#create(ExecutionEnvironment, String) explicitly} or
+ * {@link #setProperty(ExecutionEnvironment, String, String, Object...) implicitly}
created.
+ * </p>
+ *
+ * @author Randall Hauch
+ */
+@ThreadSafe
+public class SimpleRepository {
+
+ private static final ConcurrentMap<String, SimpleRepository> repositoriesByName
= new ConcurrentHashMap<String, SimpleRepository>();
+
+ public static SimpleRepository get( String name ) {
+ return repositoriesByName.get(name);
+ }
+
+ public static void shutdownAll() {
+ for (SimpleRepository repository : repositoriesByName.values()) {
+ repository.shutdown();
+ }
+ }
+
+ private ConcurrentMap<Path, Map<Name, Property>> data = new
ConcurrentHashMap<Path, Map<Name, Property>>();
+ private final String repositoryName;
+ private boolean shutdown = false;
+
+ public SimpleRepository( String repositoryName ) {
+ this.repositoryName = repositoryName;
+ if (repositoriesByName.putIfAbsent(repositoryName, this) != null) {
+ throw new IllegalArgumentException("Repository \"" +
repositoryName + "\" already exists and may not be recreated");
+ }
+ }
+
+ /**
+ * @return repositoryName
+ */
+ public String getRepositoryName() {
+ return repositoryName;
+ }
+
+ /**
+ * Get the current modifiable map of property data
+ *
+ * @return data
+ */
+ public ConcurrentMap<Path, Map<Name, Property>> getData() {
+ return data;
+ }
+
+ /**
+ * Utility method to help set the property on the node given by the supplied path. If
the node does not exist, it will be
+ * created.
+ *
+ * @param env the environment; may not be null
+ * @param path the path to the node; may not be null
+ * @param propertyName the property name; may not be null
+ * @param values the values of the property
+ * @return this repository, for method chaining
+ */
+ public SimpleRepository setProperty( ExecutionEnvironment env,
+ String path,
+ String propertyName,
+ Object... values ) {
+ PathFactory pathFactory = env.getValueFactories().getPathFactory();
+ NameFactory nameFactory = env.getValueFactories().getNameFactory();
+ PropertyFactory propertyFactory = env.getPropertyFactory();
+ Path pathObj = pathFactory.create(path);
+ create(env, pathObj.getAncestor().getString(env.getNamespaceRegistry()));
+ Property property = propertyFactory.create(nameFactory.create(propertyName),
values);
+ Map<Name, Property> properties = new HashMap<Name, Property>();
+ Map<Name, Property> existingProperties = data.putIfAbsent(pathObj,
properties);
+ if (existingProperties == null) existingProperties = properties;
+ existingProperties.put(property.getName(), property);
+ return this;
+ }
+
+ /**
+ * Create the node if it does not exist.
+ *
+ * @param env the environment; may not be null
+ * @param path the path to the node; may not be null
+ * @return this repository, for method chaining
+ */
+ public SimpleRepository create( ExecutionEnvironment env,
+ String path ) {
+ PathFactory pathFactory = env.getValueFactories().getPathFactory();
+ Path pathObj = pathFactory.create(path);
+ Path ancestorPath = pathObj.getAncestor();
+ while (!ancestorPath.isRoot()) {
+ data.putIfAbsent(ancestorPath, new HashMap<Name, Property>());
+ ancestorPath = ancestorPath.getAncestor();
+ }
+ data.putIfAbsent(pathObj, new HashMap<Name, Property>());
+ return this;
+ }
+
+ /**
+ * Delete the branch rooted at the supplied path, if it exists.
+ *
+ * @param env the environment; may not be null
+ * @param path the path to the branch's top node; may not be null
+ * @return this repository, for method chaining
+ */
+ public SimpleRepository delete( ExecutionEnvironment env,
+ String path ) {
+ PathFactory pathFactory = env.getValueFactories().getPathFactory();
+ Path pathObj = pathFactory.create(path);
+ List<Path> pathsToRemove = new LinkedList<Path>();
+ for (Path nodePath : data.keySet()) {
+ if (nodePath.equals(pathObj) || nodePath.isDecendantOf(pathObj)) {
+ pathsToRemove.add(nodePath);
+ }
+ }
+ for (Path pathToRemove : pathsToRemove) {
+ data.remove(pathToRemove);
+ }
+ return this;
+ }
+
+ /**
+ * @param data new new map of property data
+ */
+ public void setData( ConcurrentMap<Path, Map<Name, Property>> data ) {
+ this.data = data;
+ }
+
+ public boolean isShutdown() {
+ return shutdown;
+ }
+
+ public void shutdown() {
+ shutdown = true;
+ repositoriesByName.remove(this.repositoryName);
+ }
+
+}
Property changes on:
trunk/dna-repository/src/test/java/org/jboss/dna/repository/federation/SimpleRepository.java
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Added:
trunk/dna-repository/src/test/java/org/jboss/dna/repository/federation/SimpleRepositorySource.java
===================================================================
---
trunk/dna-repository/src/test/java/org/jboss/dna/repository/federation/SimpleRepositorySource.java
(rev 0)
+++
trunk/dna-repository/src/test/java/org/jboss/dna/repository/federation/SimpleRepositorySource.java 2008-07-01
20:17:39 UTC (rev 324)
@@ -0,0 +1,294 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, 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.dna.repository.federation;
+
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+import javax.naming.Reference;
+import javax.transaction.xa.XAResource;
+import org.jboss.dna.spi.cache.CachePolicy;
+import org.jboss.dna.spi.graph.InvalidPathException;
+import org.jboss.dna.spi.graph.Name;
+import org.jboss.dna.spi.graph.Path;
+import org.jboss.dna.spi.graph.Property;
+import org.jboss.dna.spi.graph.commands.CompositeCommand;
+import org.jboss.dna.spi.graph.commands.GetChildrenCommand;
+import org.jboss.dna.spi.graph.commands.GetPropertiesCommand;
+import org.jboss.dna.spi.graph.commands.GraphCommand;
+import org.jboss.dna.spi.graph.connection.ExecutionEnvironment;
+import org.jboss.dna.spi.graph.connection.RepositoryConnection;
+import org.jboss.dna.spi.graph.connection.RepositorySource;
+import org.jboss.dna.spi.graph.connection.RepositorySourceException;
+import org.jboss.dna.spi.graph.connection.RepositorySourceListener;
+
+/**
+ * A {@link RepositorySource} for a {@link SimpleRepository simple repository}.
+ *
+ * @author Randall Hauch
+ */
+public class SimpleRepositorySource implements RepositorySource {
+
+ private static final long serialVersionUID = 1L;
+
+ public static final int DEFAULT_RETRY_LIMIT = 5;
+
+ private String repositoryName;
+ private String name;
+ private int retryLimit = DEFAULT_RETRY_LIMIT;
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.spi.graph.connection.RepositorySource#getRetryLimit()
+ */
+ public int getRetryLimit() {
+ return retryLimit;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.spi.graph.connection.RepositorySource#setRetryLimit(int)
+ */
+ public void setRetryLimit( int limit ) {
+ retryLimit = limit;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.spi.graph.connection.RepositoryConnectionFactory#getName()
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * @param name Sets name to the specified value.
+ */
+ public void setName( String name ) {
+ this.name = name;
+ }
+
+ /**
+ * @return repositoryName
+ */
+ public String getRepositoryName() {
+ return repositoryName;
+ }
+
+ /**
+ * @param repositoryName Sets repositoryName to the specified value.
+ */
+ public void setRepositoryName( String repositoryName ) {
+ this.repositoryName = repositoryName;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see javax.naming.Referenceable#getReference()
+ */
+ public Reference getReference() {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.spi.graph.connection.RepositoryConnectionFactory#getConnection()
+ */
+ public RepositoryConnection getConnection() throws RepositorySourceException {
+ String reposName = this.repositoryName;
+ SimpleRepository repository = SimpleRepository.get(reposName);
+ if (repository == null) {
+ throw new RepositorySourceException(this.getName(), "Unable to find
repository \"" + reposName + "\"");
+ }
+ return new Connection(repository);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ return name.hashCode();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals( Object obj ) {
+ if (obj == this) return true;
+ if (obj instanceof SimpleRepositorySource) {
+ SimpleRepositorySource that = (SimpleRepositorySource)obj;
+ if (!this.getName().equals(that.getName())) return false;
+ if (!this.getRepositoryName().equals(that.getRepositoryName())) return
false;
+ return true;
+ }
+ return false;
+ }
+
+ protected class Connection implements RepositoryConnection {
+
+ private RepositorySourceListener listener;
+ private final SimpleRepository repository;
+
+ protected Connection( SimpleRepository repository ) {
+ assert repository != null;
+ this.repository = repository;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.spi.graph.connection.RepositoryConnection#close()
+ */
+ public void close() {
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.spi.graph.connection.RepositoryConnection#execute(org.jboss.dna.spi.graph.connection.ExecutionEnvironment,
+ * org.jboss.dna.spi.graph.commands.GraphCommand[])
+ */
+ public void execute( ExecutionEnvironment env,
+ GraphCommand... commands ) throws RepositorySourceException
{
+ assert env != null;
+ if (repository.isShutdown()) {
+ throw new RepositorySourceException(getName(), "The repository
\"" + repository.getRepositoryName()
+ + "\" is no
longer available");
+ }
+ for (GraphCommand command : commands) {
+ executeCommand(env, command);
+ }
+ }
+
+ protected void executeCommand( ExecutionEnvironment env,
+ GraphCommand command ) {
+ if (command == null) return;
+ if (command instanceof CompositeCommand) {
+ CompositeCommand composite = (CompositeCommand)command;
+ for (GraphCommand nestedCommand : composite) {
+ executeCommand(env, nestedCommand);
+ }
+ }
+ Map<Path, Map<Name, Property>> data = repository.getData();
+ // Only commands we recognize are the following; everything else is ignored
...
+ if (command instanceof GetPropertiesCommand) { // super of GetNodeCommand
+ GetPropertiesCommand getProperties = (GetPropertiesCommand)command;
+ Path targetPath = getProperties.getPath();
+ Map<Name, Property> properties = data.get(targetPath);
+ if (properties == null) {
+ getProperties.setError(new InvalidPathException("Non-existant
node: " + targetPath));
+ return;
+ }
+ for (Property property : properties.values()) {
+ getProperties.setProperty(property);
+ }
+ }
+ if (command instanceof GetChildrenCommand) { // super of GetNodeCommand
+ GetChildrenCommand getChildren = (GetChildrenCommand)command;
+ Path targetPath = getChildren.getPath();
+ if (data.get(targetPath) == null) {
+ getChildren.setError(new InvalidPathException("Non-existant
node: " + targetPath));
+ return;
+ }
+ // Iterate through all of the properties, looking for any paths that are
children of the path ...
+ List<Path.Segment> childSegments = new
LinkedList<Path.Segment>();
+ for (Path path : data.keySet()) {
+ if (path.getAncestor().equals(targetPath)) {
+ childSegments.add(path.getLastSegment());
+ }
+ }
+ // This does not store children order, so sort ...
+ Collections.sort(childSegments);
+ getChildren.setChildren(childSegments);
+ }
+
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.spi.graph.connection.RepositoryConnection#getDefaultCachePolicy()
+ */
+ public CachePolicy getDefaultCachePolicy() {
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.spi.graph.connection.RepositoryConnection#getSourceName()
+ */
+ public String getSourceName() {
+ return SimpleRepositorySource.this.getName();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.spi.graph.connection.RepositoryConnection#getXAResource()
+ */
+ public XAResource getXAResource() {
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.spi.graph.connection.RepositoryConnection#ping(long,
java.util.concurrent.TimeUnit)
+ */
+ public boolean ping( long time,
+ TimeUnit unit ) {
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.spi.graph.connection.RepositoryConnection#setListener(org.jboss.dna.spi.graph.connection.RepositorySourceListener)
+ */
+ public void setListener( RepositorySourceListener listener ) {
+ this.listener = listener;
+ }
+
+ /**
+ * @return listener
+ */
+ public RepositorySourceListener getListener() {
+ return listener;
+ }
+
+ }
+
+}
Property changes on:
trunk/dna-repository/src/test/java/org/jboss/dna/repository/federation/SimpleRepositorySource.java
___________________________________________________________________
Name: svn:mime-type
+ text/plain