DNA SVN: r914 - in trunk: dna-graph/src/main/java/org/jboss/dna/graph/observe and 2 other directories.
by dna-commits@lists.jboss.org
Author: rhauch
Date: 2009-05-20 16:30:39 -0400 (Wed, 20 May 2009)
New Revision: 914
Added:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/observe/Observer.java
Modified:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/RepositoryContext.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/observe/ChangeObserver.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/observe/ChangeObservers.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/observe/Changes.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/observe/Observable.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/observe/package-info.java
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/test/AbstractConnectorTest.java
trunk/dna-repository/src/main/java/org/jboss/dna/repository/RepositoryLibrary.java
Log:
DNA-252 Complete support for events to the connector framework
Additional changes.
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/RepositoryContext.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/RepositoryContext.java 2009-05-19 20:15:05 UTC (rev 913)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/RepositoryContext.java 2009-05-20 20:30:39 UTC (rev 914)
@@ -24,6 +24,7 @@
package org.jboss.dna.graph.connector;
import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.observe.Observer;
/**
* The context for a repository. This interface need not be implemented by a {@link RepositorySource}, as it is normally provided
@@ -47,4 +48,10 @@
*/
RepositoryConnectionFactory getRepositoryConnectionFactory();
+ /**
+ * Get the observer that the connector may use to publish changes.
+ *
+ * @return the observer, or null if the are no listeners and publishing is not required/requested
+ */
+ Observer getObserver();
}
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/observe/ChangeObserver.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/observe/ChangeObserver.java 2009-05-19 20:15:05 UTC (rev 913)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/observe/ChangeObserver.java 2009-05-20 20:30:39 UTC (rev 914)
@@ -38,7 +38,7 @@
* </p>
*/
@ThreadSafe
-public abstract class ChangeObserver {
+public abstract class ChangeObserver implements Observer {
private final CopyOnWriteArraySet<ChangeSourceReference> sources = new CopyOnWriteArraySet<ChangeSourceReference>();
@@ -155,5 +155,4 @@
return false;
}
}
-
}
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/observe/ChangeObservers.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/observe/ChangeObservers.java 2009-05-19 20:15:05 UTC (rev 913)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/observe/ChangeObservers.java 2009-05-20 20:30:39 UTC (rev 914)
@@ -24,7 +24,9 @@
package org.jboss.dna.graph.observe;
import java.lang.ref.WeakReference;
+import java.util.Iterator;
import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.atomic.AtomicBoolean;
import net.jcip.annotations.ThreadSafe;
import org.jboss.dna.common.util.CheckArg;
import org.jboss.dna.common.util.Logger;
@@ -35,7 +37,8 @@
@ThreadSafe
public class ChangeObservers implements Observable {
- private CopyOnWriteArrayList<ObserverReference> observers = new CopyOnWriteArrayList<ObserverReference>();
+ private final CopyOnWriteArrayList<ObserverReference> observers = new CopyOnWriteArrayList<ObserverReference>();
+ private final AtomicBoolean shutdown = new AtomicBoolean(false);
public ChangeObservers() {
}
@@ -46,7 +49,7 @@
* @see org.jboss.dna.graph.observe.Observable#register(org.jboss.dna.graph.observe.ChangeObserver)
*/
public boolean register( ChangeObserver observer ) {
- if (observer != null && observers.addIfAbsent(new ObserverReference(observer))) {
+ if (observer != null && !shutdown.get() && observers.addIfAbsent(new ObserverReference(observer))) {
observer.registeredWith(this);
return true;
}
@@ -67,6 +70,30 @@
}
/**
+ * Unregister all registered observers, and mark this as no longer accepting new registered observers.
+ */
+ public void shutdown() {
+ shutdown.set(true);
+ while (!observers.isEmpty()) {
+ Iterator<ObserverReference> iter = observers.iterator(); // gets snapshot
+ observers.clear();
+ while (iter.hasNext()) {
+ ObserverReference reference = iter.next();
+ if (reference.get() != null) reference.get().unregisteredWith(this);
+ }
+ }
+ }
+
+ /**
+ * Determine whether there are any observers at the time this method is called.
+ *
+ * @return true if there are currently no observers, or false if there is at least one observer
+ */
+ public boolean isEmpty() {
+ return observers.isEmpty();
+ }
+
+ /**
* Broadcast the supplied changes to the registered observers.
*
* @param changes the changes to broadcast
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/observe/Changes.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/observe/Changes.java 2009-05-19 20:15:05 UTC (rev 913)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/observe/Changes.java 2009-05-20 20:30:39 UTC (rev 914)
@@ -23,6 +23,9 @@
*/
package org.jboss.dna.graph.observe;
+import java.io.Serializable;
+import java.util.Iterator;
+import java.util.List;
import javax.security.auth.Subject;
import net.jcip.annotations.Immutable;
import org.jboss.dna.graph.property.DateTime;
@@ -32,26 +35,109 @@
* A set of changes that were made atomically. Each change is in the form of a frozen {@link ChangeRequest}.
*/
@Immutable
-public interface Changes extends Iterable<ChangeRequest> {
+public final class Changes implements Iterable<ChangeRequest>, Comparable<Changes>, Serializable {
+ private static final long serialVersionUID = 1L;
+
+ private final String processId;
+ private final Subject subject;
+ private final String sourceName;
+ private final DateTime timestamp;
+ private final List<ChangeRequest> changeRequests;
+
+ public Changes( String processId,
+ Subject subject,
+ String sourceName,
+ DateTime timestamp,
+ List<ChangeRequest> requests ) {
+ this.subject = subject;
+ this.sourceName = sourceName;
+ this.timestamp = timestamp;
+ this.changeRequests = requests;
+ this.processId = processId != null ? processId : "";
+ }
+
/**
* Get the user that made these changes.
*
* @return the user; never null
*/
- public Subject getUser();
+ public Subject getSubject() {
+ return this.subject;
+ }
/**
* Get the name of the source that was changed.
*
* @return the source name; never null
*/
- public String getSourceName();
+ public String getSourceName() {
+ return this.sourceName;
+ }
/**
* Get the timestamp that the changes were made. All changes within the change set were all made at this instant in time.
*
* @return the timestamp of the changes; never null
*/
- public DateTime getTimestamp();
+ public DateTime getTimestamp() {
+ return this.timestamp;
+ }
+
+ /**
+ * Get the identifier of the process where these changes originated. This identifier may be useful in preventing feedbacks.
+ *
+ * @return the process identifier; never null
+ */
+ public String getProcessId() {
+ return processId;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Iterable#iterator()
+ */
+ public Iterator<ChangeRequest> iterator() {
+ return this.changeRequests.iterator();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ return getTimestamp().hashCode();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Comparable#compareTo(java.lang.Object)
+ */
+ public int compareTo( Changes that ) {
+ if (this == that) return 0;
+ return this.getTimestamp().compareTo(that.getTimestamp());
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals( Object obj ) {
+ if (obj == this) return true;
+ if (obj instanceof Changes) {
+ Changes that = (Changes)obj;
+ if (!this.getProcessId().equals(that.getProcessId())) return false;
+ if (!this.getSourceName().equals(that.getSourceName())) return false;
+ if (!this.getTimestamp().equals(that.getTimestamp())) return false;
+ if (!this.getSubject().equals(that.getSubject())) return false;
+ return true;
+ }
+ return false;
+ }
}
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/observe/Observable.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/observe/Observable.java 2009-05-19 20:15:05 UTC (rev 913)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/observe/Observable.java 2009-05-20 20:30:39 UTC (rev 914)
@@ -36,7 +36,8 @@
* Register the supplied observer. This method does nothing if the observer reference is null.
*
* @param observer the observer to be added; may be null
- * @return true if the observer was added, or false if the observer was null or if the observer was already registered
+ * @return true if the observer was added, or false if the observer was null, if the observer was already registered, or if
+ * the observer could not be added
*/
boolean register( ChangeObserver observer );
Copied: trunk/dna-graph/src/main/java/org/jboss/dna/graph/observe/Observer.java (from rev 913, trunk/dna-graph/src/main/java/org/jboss/dna/graph/observe/Changes.java)
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/observe/Observer.java (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/observe/Observer.java 2009-05-20 20:30:39 UTC (rev 914)
@@ -0,0 +1,38 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ *
+ * JBoss DNA 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.graph.observe;
+
+/**
+ *
+ */
+public interface Observer {
+
+ /**
+ * Method that is called for each {@link Changes set of changes} from the {@link Observable} instance(s) with which this
+ * observer is registered.
+ *
+ * @param changes the changes that are being published
+ */
+ void notify( Changes changes );
+}
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/observe/package-info.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/observe/package-info.java 2009-05-19 20:15:05 UTC (rev 913)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/observe/package-info.java 2009-05-20 20:30:39 UTC (rev 914)
@@ -87,20 +87,6 @@
* Third, the requests that make up a {@link Changes} instance can actually be replayed. Consider the case of a cache
* that is backed by a {@link org.jboss.dna.graph.connector.RepositorySource}, which might use an observer to keep the cache in sync.
* As the cache is notified of Changes, the cache can simply replay the changes against its source.
- * </p>
- * <h3>Change feed and timelines</h3>
- * <p>
- * While it is often sufficient to register observers and receive notifications of the changes as they occur, it
- * is also often useful to be able to ask for the changes that have occurred since some time or date. This allows
- * a component to poll for changes or, for example, to catch up on the changes that occurred previous to registering
- * an observer.
- * </p>
- * <p>
- * Components that implement {@link Observable} may also implement the {@link ChangeFeed} interface to allow for
- * components to {@link ChangeFeed#changesSince(org.jboss.dna.graph.property.DateTime) poll for changes}. The result
- * of this method is a {@link ChangeTimeline} object that contains the {@link Changes} as well as the time period during which
- * the change sets occurred. Note that an empty {@link ChangeTimeline} object implies there were no changes made during the time period.
- * </p>
*/
package org.jboss.dna.graph.observe;
Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/test/AbstractConnectorTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/test/AbstractConnectorTest.java 2009-05-19 20:15:05 UTC (rev 913)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/test/AbstractConnectorTest.java 2009-05-20 20:30:39 UTC (rev 914)
@@ -51,6 +51,7 @@
import org.jboss.dna.graph.connector.RepositoryContext;
import org.jboss.dna.graph.connector.RepositorySource;
import org.jboss.dna.graph.connector.RepositorySourceException;
+import org.jboss.dna.graph.observe.Observer;
import org.jboss.dna.graph.property.DateTime;
import org.jboss.dna.graph.property.Name;
import org.jboss.dna.graph.property.Path;
@@ -123,6 +124,10 @@
public RepositoryConnectionFactory getRepositoryConnectionFactory() {
return connectionFactory;
}
+
+ public Observer getObserver() {
+ return null; // no observers here
+ }
});
// And set up the graph instance ...
Modified: trunk/dna-repository/src/main/java/org/jboss/dna/repository/RepositoryLibrary.java
===================================================================
--- trunk/dna-repository/src/main/java/org/jboss/dna/repository/RepositoryLibrary.java 2009-05-19 20:15:05 UTC (rev 913)
+++ trunk/dna-repository/src/main/java/org/jboss/dna/repository/RepositoryLibrary.java 2009-05-20 20:30:39 UTC (rev 914)
@@ -41,6 +41,11 @@
import org.jboss.dna.graph.connector.RepositoryConnectionPool;
import org.jboss.dna.graph.connector.RepositoryContext;
import org.jboss.dna.graph.connector.RepositorySource;
+import org.jboss.dna.graph.observe.ChangeObserver;
+import org.jboss.dna.graph.observe.ChangeObservers;
+import org.jboss.dna.graph.observe.Changes;
+import org.jboss.dna.graph.observe.Observable;
+import org.jboss.dna.graph.observe.Observer;
import org.jboss.dna.repository.mimetype.MimeTypeDetectors;
import org.jboss.dna.repository.service.AbstractServiceAdministrator;
import org.jboss.dna.repository.service.ServiceAdministrator;
@@ -52,7 +57,7 @@
* @author Randall Hauch
*/
@ThreadSafe
-public class RepositoryLibrary implements RepositoryConnectionFactory {
+public class RepositoryLibrary implements RepositoryConnectionFactory, Observable {
/**
* The administrative component for this service.
@@ -106,8 +111,9 @@
private final ReadWriteLock sourcesLock = new ReentrantReadWriteLock();
private final CopyOnWriteArrayList<RepositoryConnectionPool> pools = new CopyOnWriteArrayList<RepositoryConnectionPool>();
private RepositoryConnectionFactory delegate;
- protected final ExecutionContext executionContext;
+ private final ExecutionContext executionContext;
private final RepositoryContext repositoryContext;
+ private final ObservationBus observationBus = new InMemoryObservationBus();
/**
* Create a new manager instance.
@@ -146,11 +152,12 @@
* this manager; may be null if there is no delegate
* @throws IllegalArgumentException if the <code>executionContextFactory</code> reference is null
*/
- public RepositoryLibrary( ExecutionContext executionContext,
+ public RepositoryLibrary( final ExecutionContext executionContext,
RepositoryConnectionFactory delegate ) {
CheckArg.isNotNull(executionContext, "executionContext");
this.delegate = delegate;
this.executionContext = executionContext;
+ final ObservationBus observationBus = this.observationBus;
this.repositoryContext = new RepositoryContext() {
/**
* {@inheritDoc}
@@ -158,7 +165,7 @@
* @see org.jboss.dna.graph.connector.RepositoryContext#getExecutionContext()
*/
public ExecutionContext getExecutionContext() {
- return RepositoryLibrary.this.executionContext;
+ return executionContext;
}
/**
@@ -170,10 +177,37 @@
return RepositoryLibrary.this;
}
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.connector.RepositoryContext#getObserver()
+ */
+ public Observer getObserver() {
+ return observationBus.hasObservers() ? observationBus : null;
+ }
+
};
}
/**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.observe.Observable#register(org.jboss.dna.graph.observe.ChangeObserver)
+ */
+ public boolean register( ChangeObserver observer ) {
+ return observationBus.register(observer);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.observe.Observable#unregister(org.jboss.dna.graph.observe.ChangeObserver)
+ */
+ public boolean unregister( ChangeObserver observer ) {
+ return observationBus.unregister(observer);
+ }
+
+ /**
* @return executionContextFactory
*/
public ExecutionContext getExecutionContext() {
@@ -235,6 +269,8 @@
} finally {
this.sourcesLock.readLock().unlock();
}
+ // Remove all listeners ...
+ this.observationBus.shutdown();
}
/**
@@ -462,4 +498,65 @@
}
return null;
}
+
+ protected interface ObservationBus extends Observable, Observer {
+ boolean hasObservers();
+
+ void shutdown();
+ }
+
+ protected class InMemoryObservationBus implements ObservationBus {
+ private final ChangeObservers observers = new ChangeObservers();
+
+ protected InMemoryObservationBus() {
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.observe.Observable#register(org.jboss.dna.graph.observe.ChangeObserver)
+ */
+ public boolean register( ChangeObserver observer ) {
+ return observers.register(observer);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.observe.Observable#unregister(org.jboss.dna.graph.observe.ChangeObserver)
+ */
+ public boolean unregister( ChangeObserver observer ) {
+ return observers.unregister(observer);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.observe.Observer#notify(org.jboss.dna.graph.observe.Changes)
+ */
+ public void notify( Changes changes ) {
+ if (changes != null) {
+ // Broadcast the changes to the registered observers ...
+ observers.broadcast(changes);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.repository.RepositoryLibrary.ObservationBus#hasObservers()
+ */
+ public boolean hasObservers() {
+ return !observers.isEmpty();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.repository.RepositoryLibrary.ObservationBus#shutdown()
+ */
+ public void shutdown() {
+ observers.shutdown();
+ }
+ }
}
16 years, 11 months
DNA SVN: r913 - in trunk: dna-graph/src/main/java/org/jboss/dna/graph/connector/federation and 13 other directories.
by dna-commits@lists.jboss.org
Author: rhauch
Date: 2009-05-19 16:15:05 -0400 (Tue, 19 May 2009)
New Revision: 913
Removed:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/RepositorySourceListener.java
Modified:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/RepositoryConnection.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/RepositoryConnectionPool.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/RepositorySourceCapabilities.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/FederatedRepositoryConnection.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/inmemory/InMemoryRepositoryConnection.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/observe/ChangeObservers.java
trunk/dna-graph/src/test/java/org/jboss/dna/graph/GraphTest.java
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/MockRepositoryConnection.java
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/TimeDelayingRepositorySource.java
trunk/dna-graph/src/test/java/org/jboss/dna/graph/io/GraphImporterTest.java
trunk/extensions/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/FederatedRepository.java
trunk/extensions/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/FederatedRepositoryConnection.java
trunk/extensions/dna-connector-federation/src/test/java/org/jboss/dna/connector/federation/FederatedRepositoryConnectionTest.java
trunk/extensions/dna-connector-federation/src/test/java/org/jboss/dna/connector/federation/FederatedRepositoryTest.java
trunk/extensions/dna-connector-filesystem/src/main/java/org/jboss/dna/connector/filesystem/FileSystemConnection.java
trunk/extensions/dna-connector-jbosscache/src/main/java/org/jboss/dna/connector/jbosscache/JBossCacheConnection.java
trunk/extensions/dna-connector-jdbc-metadata/src/main/java/org/jboss/dna/connector/jdbc/JdbcConnection.java
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/JpaConnection.java
trunk/extensions/dna-connector-svn/src/main/java/org/jboss/dna/connector/svn/SVNRepositoryConnection.java
trunk/extensions/dna-connector-svn/src/test/java/org/jboss/dna/connector/svn/SVNRepositoryConnectionTest.java
Log:
DNA-252 Complete support for events to the connector framework
Removed the older RepositorySourceListener and all uses of it.
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/RepositoryConnection.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/RepositoryConnection.java 2009-05-19 17:40:25 UTC (rev 912)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/RepositoryConnection.java 2009-05-19 20:15:05 UTC (rev 913)
@@ -47,11 +47,6 @@
* returning the connection from a connection pool.
* </p>
* <p>
- * DNA hasn't yet defined the event mechanism, so connectors don't have any methods to invoke on the
- * {@link RepositorySourceListener}. This will be defined in the next release, so feel free to manage the listeners now. Note that
- * by default the {@link RepositorySourceCapabilities} returns false for supportsEvents().
- * </p>
- * <p>
* The most important method on this interface, though, is the {@link #execute(ExecutionContext, Request)} method, which serves as
* the mechanism by which the component using the connector access and manipulates the content exposed by the connector. The first
* parameter to this method is the {@link ExecutionContext}, which contains the information about environment as well as the
@@ -149,13 +144,6 @@
TimeUnit unit ) throws InterruptedException;
/**
- * Set the listener that is to receive notifications to changes to content within this source.
- *
- * @param listener the new listener, or null if no component is interested in the change notifications
- */
- void setListener( RepositorySourceListener listener );
-
- /**
* Get the default cache policy for this repository. If none is provided, a global cache policy will be used.
*
* @return the default cache policy
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/RepositoryConnectionPool.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/RepositoryConnectionPool.java 2009-05-19 17:40:25 UTC (rev 912)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/RepositoryConnectionPool.java 2009-05-19 20:15:05 UTC (rev 913)
@@ -1011,14 +1011,6 @@
returnConnection(this);
}
}
-
- /**
- * {@inheritDoc}
- */
- public void setListener( RepositorySourceListener listener ) {
- if (!closed) this.original.setListener(listener);
- }
-
}
}
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/RepositorySourceCapabilities.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/RepositorySourceCapabilities.java 2009-05-19 17:40:25 UTC (rev 912)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/RepositorySourceCapabilities.java 2009-05-19 20:15:05 UTC (rev 913)
@@ -122,7 +122,7 @@
}
/**
- * Return whether the source supports events through {@link RepositorySourceListener}s.
+ * Return whether the source supports publishing change events.
*
* @return true if events are supported, or false if the source is not capable of generating events
*/
Deleted: trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/RepositorySourceListener.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/RepositorySourceListener.java 2009-05-19 17:40:25 UTC (rev 912)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/RepositorySourceListener.java 2009-05-19 20:15:05 UTC (rev 913)
@@ -1,35 +0,0 @@
-/*
- * JBoss DNA (http://www.jboss.org/dna)
- * See the COPYRIGHT.txt file distributed with this work for information
- * regarding copyright ownership. Some portions may be licensed
- * to Red Hat, Inc. under one or more contributor license agreements.
- * See the AUTHORS.txt file in the distribution for a full listing of
- * individual contributors.
- *
- * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
- * is licensed to you 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.
- *
- * JBoss DNA 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.graph.connector;
-
-/**
- * Interface for a component that wants to listen to changes occurring in a {@link RepositorySource}.
- *
- * @author Randall Hauch
- */
-public interface RepositorySourceListener {
-
- void notify( String sourceName,
- Object... events );
-}
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/FederatedRepositoryConnection.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/FederatedRepositoryConnection.java 2009-05-19 17:40:25 UTC (rev 912)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/FederatedRepositoryConnection.java 2009-05-19 20:15:05 UTC (rev 913)
@@ -37,7 +37,6 @@
import org.jboss.dna.graph.cache.CachePolicy;
import org.jboss.dna.graph.connector.RepositoryConnection;
import org.jboss.dna.graph.connector.RepositorySourceException;
-import org.jboss.dna.graph.connector.RepositorySourceListener;
import org.jboss.dna.graph.property.DateTime;
import org.jboss.dna.graph.request.CompositeRequest;
import org.jboss.dna.graph.request.Request;
@@ -255,11 +254,4 @@
// do nothing else, since we don't currently hold any state
}
- /**
- * {@inheritDoc}
- *
- * @see org.jboss.dna.graph.connector.RepositoryConnection#setListener(org.jboss.dna.graph.connector.RepositorySourceListener)
- */
- public void setListener( RepositorySourceListener listener ) {
- }
}
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/inmemory/InMemoryRepositoryConnection.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/inmemory/InMemoryRepositoryConnection.java 2009-05-19 17:40:25 UTC (rev 912)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/inmemory/InMemoryRepositoryConnection.java 2009-05-19 20:15:05 UTC (rev 913)
@@ -32,7 +32,6 @@
import org.jboss.dna.graph.cache.CachePolicy;
import org.jboss.dna.graph.connector.RepositoryConnection;
import org.jboss.dna.graph.connector.RepositorySourceException;
-import org.jboss.dna.graph.connector.RepositorySourceListener;
import org.jboss.dna.graph.request.Request;
import org.jboss.dna.graph.request.processor.RequestProcessor;
@@ -41,20 +40,8 @@
*/
public class InMemoryRepositoryConnection implements RepositoryConnection {
- protected static final RepositorySourceListener NO_OP_LISTENER = new RepositorySourceListener() {
-
- /**
- * {@inheritDoc}
- */
- public void notify( String sourceName,
- Object... events ) {
- // do nothing
- }
- };
-
private final InMemoryRepositorySource source;
private final InMemoryRepository repository;
- private RepositorySourceListener listener = NO_OP_LISTENER;
InMemoryRepositoryConnection( InMemoryRepositorySource source,
InMemoryRepository repository ) {
@@ -96,13 +83,6 @@
/**
* {@inheritDoc}
*/
- public void setListener( RepositorySourceListener listener ) {
- this.listener = listener != null ? listener : NO_OP_LISTENER;
- }
-
- /**
- * {@inheritDoc}
- */
public void close() {
// do nothing
}
@@ -144,13 +124,6 @@
}
/**
- * @return listener
- */
- protected RepositorySourceListener getListener() {
- return this.listener;
- }
-
- /**
* {@inheritDoc}
*
* @see java.lang.Object#toString()
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/observe/ChangeObservers.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/observe/ChangeObservers.java 2009-05-19 17:40:25 UTC (rev 912)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/observe/ChangeObservers.java 2009-05-19 20:15:05 UTC (rev 913)
@@ -23,8 +23,11 @@
*/
package org.jboss.dna.graph.observe;
+import java.lang.ref.WeakReference;
import java.util.concurrent.CopyOnWriteArrayList;
import net.jcip.annotations.ThreadSafe;
+import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.common.util.Logger;
/**
* Reusable manager of change listeners, typically employed by another {@link Observable} implementation.
@@ -32,7 +35,7 @@
@ThreadSafe
public class ChangeObservers implements Observable {
- private CopyOnWriteArrayList<ChangeObserver> observers = new CopyOnWriteArrayList<ChangeObserver>();
+ private CopyOnWriteArrayList<ObserverReference> observers = new CopyOnWriteArrayList<ObserverReference>();
public ChangeObservers() {
}
@@ -43,7 +46,7 @@
* @see org.jboss.dna.graph.observe.Observable#register(org.jboss.dna.graph.observe.ChangeObserver)
*/
public boolean register( ChangeObserver observer ) {
- if (observer != null && observers.addIfAbsent(observer)) {
+ if (observer != null && observers.addIfAbsent(new ObserverReference(observer))) {
observer.registeredWith(this);
return true;
}
@@ -63,4 +66,68 @@
return false;
}
+ /**
+ * Broadcast the supplied changes to the registered observers.
+ *
+ * @param changes the changes to broadcast
+ * @throws IllegalArgumentException if the changes reference is null
+ */
+ public void broadcast( Changes changes ) {
+ CheckArg.isNotNull(changes, "changes");
+ for (ObserverReference observerReference : observers) {
+ ChangeObserver observer = observerReference.get();
+ if (observer == null) {
+ observers.remove(observerReference);
+ continue;
+ }
+ try {
+ observer.notify(changes);
+ } catch (Throwable t) {
+ Logger.getLogger(getClass()).debug(t, "Exception while notifying");
+ }
+ }
+ }
+
+ /**
+ * A {@link WeakReference} implementation that provides a valid
+ */
+ protected final class ObserverReference extends WeakReference<ChangeObserver> {
+ final int hc;
+
+ protected ObserverReference( ChangeObserver source ) {
+ super(source);
+ this.hc = source.hashCode();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ return hc;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals( Object obj ) {
+ if (obj == this) return true;
+ if (obj instanceof ObserverReference) {
+ ObserverReference that = (ObserverReference)obj;
+ ChangeObserver thisSource = this.get();
+ ChangeObserver thatSource = that.get();
+ return thisSource == thatSource; // reference equality, not object equality!
+ }
+ if (obj instanceof ChangeObserver) {
+ ChangeObserver that = (ChangeObserver)obj;
+ return this.get() == that; // reference equality, not object equality!
+ }
+ return false;
+ }
+ }
}
Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/GraphTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/GraphTest.java 2009-05-19 17:40:25 UTC (rev 912)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/GraphTest.java 2009-05-19 20:15:05 UTC (rev 913)
@@ -48,7 +48,7 @@
import org.jboss.dna.graph.connector.RepositoryConnection;
import org.jboss.dna.graph.connector.RepositoryConnectionFactory;
import org.jboss.dna.graph.connector.RepositorySourceException;
-import org.jboss.dna.graph.connector.RepositorySourceListener;
+import org.jboss.dna.graph.observe.ChangeObserver;
import org.jboss.dna.graph.property.DateTime;
import org.jboss.dna.graph.property.InvalidPathException;
import org.jboss.dna.graph.property.Name;
@@ -817,8 +817,16 @@
assertNextRequestIsMove(Location.create(validUuid), Location.create(validPath));
assertNoMoreRequests();
- graph.batch().move(validPath).into(validIdProperty1, validIdProperty2).and().move(validPathString).into(validIdProperty1,
- validIdProperty2).and().move(validUuid).into(validPath).execute();
+ graph.batch()
+ .move(validPath)
+ .into(validIdProperty1, validIdProperty2)
+ .and()
+ .move(validPathString)
+ .into(validIdProperty1, validIdProperty2)
+ .and()
+ .move(validUuid)
+ .into(validPath)
+ .execute();
assertThat(numberOfExecutions, is(1));
extractRequestsFromComposite();
assertNextRequestIsMove(Location.create(validPath), Location.create(validIdProperty1, validIdProperty2));
@@ -844,8 +852,16 @@
assertNextRequestIsCopy(Location.create(validUuid), Location.create(validPath));
assertNoMoreRequests();
- graph.batch().copy(validPath).into(validIdProperty1, validIdProperty2).and().copy(validPathString).into(validIdProperty1,
- validIdProperty2).and().copy(validUuid).into(validPath).execute();
+ graph.batch()
+ .copy(validPath)
+ .into(validIdProperty1, validIdProperty2)
+ .and()
+ .copy(validPathString)
+ .into(validIdProperty1, validIdProperty2)
+ .and()
+ .copy(validUuid)
+ .into(validPath)
+ .execute();
assertThat(numberOfExecutions, is(1));
extractRequestsFromComposite();
assertNextRequestIsCopy(Location.create(validPath), Location.create(validIdProperty1, validIdProperty2));
@@ -1227,8 +1243,7 @@
return true;
}
- public void setListener( RepositorySourceListener listener ) {
+ public void setObserver( ChangeObserver observer ) {
}
-
}
}
Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/MockRepositoryConnection.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/MockRepositoryConnection.java 2009-05-19 17:40:25 UTC (rev 912)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/MockRepositoryConnection.java 2009-05-19 20:15:05 UTC (rev 913)
@@ -134,13 +134,4 @@
return true;
}
- /**
- * {@inheritDoc}
- *
- * @see org.jboss.dna.graph.connector.RepositoryConnection#setListener(org.jboss.dna.graph.connector.RepositorySourceListener)
- */
- public void setListener( RepositorySourceListener listener ) {
- throw new UnsupportedOperationException();
- }
-
}
Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/TimeDelayingRepositorySource.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/TimeDelayingRepositorySource.java 2009-05-19 17:40:25 UTC (rev 912)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/TimeDelayingRepositorySource.java 2009-05-19 20:15:05 UTC (rev 913)
@@ -36,12 +36,6 @@
import net.jcip.annotations.ThreadSafe;
import org.jboss.dna.graph.ExecutionContext;
import org.jboss.dna.graph.cache.CachePolicy;
-import org.jboss.dna.graph.connector.RepositoryConnection;
-import org.jboss.dna.graph.connector.RepositoryContext;
-import org.jboss.dna.graph.connector.RepositorySource;
-import org.jboss.dna.graph.connector.RepositorySourceCapabilities;
-import org.jboss.dna.graph.connector.RepositorySourceException;
-import org.jboss.dna.graph.connector.RepositorySourceListener;
import org.jboss.dna.graph.request.Request;
/**
@@ -373,12 +367,6 @@
return this.closeCount.get();
}
- /**
- * {@inheritDoc}
- */
- public void setListener( RepositorySourceListener listener ) {
- }
-
}
}
Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/io/GraphImporterTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/io/GraphImporterTest.java 2009-05-19 17:40:25 UTC (rev 912)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/io/GraphImporterTest.java 2009-05-19 20:15:05 UTC (rev 913)
@@ -45,7 +45,7 @@
import org.jboss.dna.graph.connector.RepositoryConnection;
import org.jboss.dna.graph.connector.RepositoryConnectionFactory;
import org.jboss.dna.graph.connector.RepositorySourceException;
-import org.jboss.dna.graph.connector.RepositorySourceListener;
+import org.jboss.dna.graph.observe.ChangeObserver;
import org.jboss.dna.graph.property.Name;
import org.jboss.dna.graph.property.Path;
import org.jboss.dna.graph.property.Property;
@@ -114,7 +114,9 @@
"repositoryName=repositoryB",
"jcr:primaryType={http://www.jcp.org/jcr/nt/1.0}unstructured",
"dna:classname=org.jboss.dna.connector.inmemory.InMemoryRepositorySource");
- assertCreateProperties(iter, "/a/b/dna:system[1]/dna:sources[1]", "jcr:primaryType={http://www.jcp.org/jcr/nt/1.0}unstructured");
+ assertCreateProperties(iter,
+ "/a/b/dna:system[1]/dna:sources[1]",
+ "jcr:primaryType={http://www.jcp.org/jcr/nt/1.0}unstructured");
assertCreateProperties(iter, "/a/b/dna:system[1]", "jcr:primaryType={http://www.jcp.org/jcr/nt/1.0}unstructured");
assertThat(iter.hasNext(), is(false));
}
@@ -129,7 +131,7 @@
Path parentPath = createNode.under().getPath();
assertThat(parentPath, is(expectedPath.getParent()));
assertThat(createNode.named(), is(expectedPath.getLastSegment().getName()));
-
+
if (properties.length > 0) {
Map<Name, Property> propertiesByName = new HashMap<Name, Property>();
for (Property prop : createNode.properties()) {
@@ -162,21 +164,20 @@
String path,
String... properties ) {
Request nextCommand = iterator.next();
-
+
if (nextCommand instanceof UpdatePropertiesRequest) {
- assertUpdateProperties((UpdatePropertiesRequest) nextCommand, path, properties);
- }
- else if (nextCommand instanceof SetPropertyRequest) {
- assertSetProperty((SetPropertyRequest) nextCommand, path, properties);
- }
- else {
+ assertUpdateProperties((UpdatePropertiesRequest)nextCommand, path, properties);
+ } else if (nextCommand instanceof SetPropertyRequest) {
+ assertSetProperty((SetPropertyRequest)nextCommand, path, properties);
+ } else {
fail("Invalid next request type: " + nextCommand.getClass().getName());
}
-
+
}
+
public void assertUpdateProperties( UpdatePropertiesRequest createNode,
- String path,
- String... properties ) {
+ String path,
+ String... properties ) {
Path expectedPath = context.getValueFactories().getPathFactory().create(path);
Path parentPath = createNode.changedLocation().getPath().getParent();
assertThat(parentPath, is(expectedPath.getParent()));
@@ -208,39 +209,38 @@
}
public void assertSetProperty( SetPropertyRequest createNode,
- String path,
- String... properties ) {
- Path expectedPath = context.getValueFactories().getPathFactory().create(path);
- Path parentPath = createNode.changedLocation().getPath().getParent();
- assertThat(parentPath, is(expectedPath.getParent()));
- assertThat(createNode.changedLocation().getPath().getLastSegment().getName(), is(expectedPath.getLastSegment().getName()));
- Map<Name, Property> propertiesByName = new HashMap<Name, Property>();
- Property prop = createNode.property();
- propertiesByName.put(prop.getName(), prop);
+ String path,
+ String... properties ) {
+ Path expectedPath = context.getValueFactories().getPathFactory().create(path);
+ Path parentPath = createNode.changedLocation().getPath().getParent();
+ assertThat(parentPath, is(expectedPath.getParent()));
+ assertThat(createNode.changedLocation().getPath().getLastSegment().getName(), is(expectedPath.getLastSegment().getName()));
+ Map<Name, Property> propertiesByName = new HashMap<Name, Property>();
+ Property prop = createNode.property();
+ propertiesByName.put(prop.getName(), prop);
- for (String propertyStr : properties) {
- if (propertyStr == "any properties") {
- propertiesByName.clear();
- break;
- }
- Matcher matcher = Pattern.compile("([^=]+)=(.*)").matcher(propertyStr);
- if (!matcher.matches()) continue;
- System.out.println("Property: " + propertyStr + " ==> " + matcher);
- Name propertyName = context.getValueFactories().getNameFactory().create(matcher.group(1));
- System.out.println("Property name: " + matcher.group(1));
- String value = matcher.group(2); // doesn't handle multiple values!!
- if (value.trim().length() == 0) value = null;
- Property actual = propertiesByName.remove(propertyName);
- Property expectedProperty = context.getPropertyFactory().create(propertyName, value);
- assertThat("missing property " + propertyName, actual, is(expectedProperty));
- }
- if (!propertiesByName.isEmpty()) {
- System.out.println("Properties for " + path + "\n" + propertiesByName);
- }
- assertThat(propertiesByName.isEmpty(), is(true));
- }
+ for (String propertyStr : properties) {
+ if (propertyStr == "any properties") {
+ propertiesByName.clear();
+ break;
+ }
+ Matcher matcher = Pattern.compile("([^=]+)=(.*)").matcher(propertyStr);
+ if (!matcher.matches()) continue;
+ System.out.println("Property: " + propertyStr + " ==> " + matcher);
+ Name propertyName = context.getValueFactories().getNameFactory().create(matcher.group(1));
+ System.out.println("Property name: " + matcher.group(1));
+ String value = matcher.group(2); // doesn't handle multiple values!!
+ if (value.trim().length() == 0) value = null;
+ Property actual = propertiesByName.remove(propertyName);
+ Property expectedProperty = context.getPropertyFactory().create(propertyName, value);
+ assertThat("missing property " + propertyName, actual, is(expectedProperty));
+ }
+ if (!propertiesByName.isEmpty()) {
+ System.out.println("Properties for " + path + "\n" + propertiesByName);
+ }
+ assertThat(propertiesByName.isEmpty(), is(true));
+ }
-
protected class MockRepositoryConnection implements RepositoryConnection {
public void close() {
}
@@ -276,7 +276,7 @@
return true;
}
- public void setListener( RepositorySourceListener listener ) {
+ public void setObserver( ChangeObserver observer ) {
}
}
Modified: trunk/extensions/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/FederatedRepository.java
===================================================================
--- trunk/extensions/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/FederatedRepository.java 2009-05-19 17:40:25 UTC (rev 912)
+++ trunk/extensions/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/FederatedRepository.java 2009-05-19 20:15:05 UTC (rev 913)
@@ -25,9 +25,7 @@
import java.util.Collections;
import java.util.HashMap;
-import java.util.List;
import java.util.Map;
-import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -38,7 +36,6 @@
import org.jboss.dna.graph.connector.RepositoryConnection;
import org.jboss.dna.graph.connector.RepositoryConnectionFactory;
import org.jboss.dna.graph.connector.RepositorySource;
-import org.jboss.dna.graph.connector.RepositorySourceListener;
import org.jboss.dna.graph.request.processor.RequestProcessor;
/**
@@ -59,7 +56,6 @@
private final AtomicInteger openExecutors = new AtomicInteger(0);
private final CountDownLatch shutdownLatch = new CountDownLatch(1);
private final AtomicBoolean shutdownRequested = new AtomicBoolean(false);
- private final CopyOnWriteArrayList<RepositorySourceListener> listeners = new CopyOnWriteArrayList<RepositorySourceListener>();
/**
* Create a federated repository instance.
@@ -163,42 +159,6 @@
}
/**
- * 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.
- *
- * @param listener the new listener
- * @return true if the listener was added, or false if the listener was not added (if reference is null, or if non-null
- * listener is already an existing listener)
- */
- public boolean addListener( RepositorySourceListener listener ) {
- if (listener == null) return false;
- return this.listeners.addIfAbsent(listener);
- }
-
- /**
- * Remove the supplied listener. This method does nothing if the supplied listener is null.
- * <p>
- * This method can safely be called while the federation repository is in use.
- * </p>
- *
- * @param listener the listener to remove
- * @return true if the listener was removed, or false if the listener was not registered
- */
- public boolean removeListener( RepositorySourceListener listener ) {
- if (listener == null) return false;
- return this.listeners.remove(listener);
- }
-
- /**
- * Get the list of listeners, which is the actual list used by the repository.
- *
- * @return the listeners
- */
- public List<RepositorySourceListener> getListeners() {
- return this.listeners;
- }
-
- /**
* Authenticate the supplied username with the supplied credentials, and return whether authentication was successful.
*
* @param source the {@link RepositorySource} that should be affiliated with the resulting connection
Modified: trunk/extensions/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/FederatedRepositoryConnection.java
===================================================================
--- trunk/extensions/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/FederatedRepositoryConnection.java 2009-05-19 17:40:25 UTC (rev 912)
+++ trunk/extensions/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/FederatedRepositoryConnection.java 2009-05-19 20:15:05 UTC (rev 913)
@@ -24,14 +24,12 @@
package org.jboss.dna.connector.federation;
import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicReference;
import javax.transaction.xa.XAResource;
import net.jcip.annotations.ThreadSafe;
import org.jboss.dna.graph.ExecutionContext;
import org.jboss.dna.graph.cache.CachePolicy;
import org.jboss.dna.graph.connector.RepositoryConnection;
import org.jboss.dna.graph.connector.RepositorySourceException;
-import org.jboss.dna.graph.connector.RepositorySourceListener;
import org.jboss.dna.graph.request.Request;
import org.jboss.dna.graph.request.processor.RequestProcessor;
@@ -41,16 +39,8 @@
@ThreadSafe
public class FederatedRepositoryConnection implements RepositoryConnection {
- protected static final RepositorySourceListener NO_OP_LISTENER = new RepositorySourceListener() {
- public void notify( String sourceName,
- Object... events ) {
- // do nothing
- }
- };
-
private final FederatedRepository repository;
private final String sourceName;
- private final AtomicReference<RepositorySourceListener> listener;
protected FederatedRepositoryConnection( FederatedRepository repository,
String sourceName ) {
@@ -58,7 +48,6 @@
assert repository != null;
this.sourceName = sourceName;
this.repository = repository;
- this.listener = new AtomicReference<RepositorySourceListener>(NO_OP_LISTENER);
this.repository.register(this);
}
@@ -93,18 +82,6 @@
/**
* {@inheritDoc}
*/
- public void setListener( RepositorySourceListener listener ) {
- if (listener == null) listener = NO_OP_LISTENER;
- RepositorySourceListener oldListener = this.listener.getAndSet(listener);
- this.repository.addListener(listener);
- if (oldListener != NO_OP_LISTENER) {
- this.repository.removeListener(oldListener);
- }
- }
-
- /**
- * {@inheritDoc}
- */
public boolean ping( long time,
TimeUnit unit ) {
return this.repository.isRunning();
@@ -136,11 +113,6 @@
* {@inheritDoc}
*/
public void close() {
- try {
- this.repository.removeListener(this.listener.get());
- } finally {
- this.repository.unregister(this);
- }
}
}
Modified: trunk/extensions/dna-connector-federation/src/test/java/org/jboss/dna/connector/federation/FederatedRepositoryConnectionTest.java
===================================================================
--- trunk/extensions/dna-connector-federation/src/test/java/org/jboss/dna/connector/federation/FederatedRepositoryConnectionTest.java 2009-05-19 17:40:25 UTC (rev 912)
+++ trunk/extensions/dna-connector-federation/src/test/java/org/jboss/dna/connector/federation/FederatedRepositoryConnectionTest.java 2009-05-19 20:15:05 UTC (rev 913)
@@ -33,7 +33,6 @@
import java.util.concurrent.TimeUnit;
import org.jboss.dna.graph.ExecutionContext;
import org.jboss.dna.graph.connector.RepositorySourceException;
-import org.jboss.dna.graph.connector.RepositorySourceListener;
import org.jboss.dna.graph.request.Request;
import org.junit.Before;
import org.junit.Test;
@@ -93,29 +92,4 @@
verify(repository, times(1)).isRunning();
}
- @Test
- public void shouldAddListenerToRepositoryWhenSetOnConnection() {
- // Old listener is no-op, so it is not removed from repository ...
- RepositorySourceListener listener = mock(RepositorySourceListener.class);
- connection.setListener(listener);
- verify(repository, times(1)).addListener(listener);
-
- // Old listener is NOT no-op, so it is removed from repository ...
- RepositorySourceListener listener2 = mock(RepositorySourceListener.class);
- connection.setListener(listener2);
- verify(repository, times(1)).removeListener(listener);
- verify(repository, times(1)).addListener(listener2);
- }
-
- @Test
- public void shouldRemoveListenerFromRepositoryWhenConnectionIsClosed() {
- // Old listener is NOT no-op, so it is removed from repository ...
- RepositorySourceListener listener2 = mock(RepositorySourceListener.class);
- connection.setListener(listener2);
- verify(repository, times(1)).addListener(listener2);
-
- // Closing connection will remove listener ...
- connection.close();
- verify(repository, times(1)).removeListener(listener2);
- }
}
Modified: trunk/extensions/dna-connector-federation/src/test/java/org/jboss/dna/connector/federation/FederatedRepositoryTest.java
===================================================================
--- trunk/extensions/dna-connector-federation/src/test/java/org/jboss/dna/connector/federation/FederatedRepositoryTest.java 2009-05-19 17:40:25 UTC (rev 912)
+++ trunk/extensions/dna-connector-federation/src/test/java/org/jboss/dna/connector/federation/FederatedRepositoryTest.java 2009-05-19 20:15:05 UTC (rev 913)
@@ -27,13 +27,11 @@
import static org.hamcrest.core.IsNull.notNullValue;
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.stub;
import java.util.ArrayList;
import java.util.Collection;
import org.jboss.dna.graph.ExecutionContext;
import org.jboss.dna.graph.connector.RepositoryConnectionFactory;
-import org.jboss.dna.graph.connector.RepositorySourceListener;
import org.junit.Before;
import org.junit.Test;
import org.mockito.MockitoAnnotations;
@@ -50,10 +48,6 @@
@Mock
private FederatedWorkspace config;
@Mock
- private RepositorySourceListener listener1;
- @Mock
- private RepositorySourceListener listener2;
- @Mock
private RepositoryConnectionFactory connectionFactory;
@Before
@@ -73,72 +67,6 @@
}
@Test
- public void shouldHaveNoListenersUponCreation() {
- assertThat(repository.getListeners(), is(notNullValue()));
- assertThat(repository.getListeners().isEmpty(), is(true));
- }
-
- @Test
- public void shouldNotAddNullListener() {
- assertThat(repository.addListener(null), is(false));
- }
-
- @Test
- public void shouldNotAddListenerIfAlreadyInList() {
- assertThat(repository.getListeners().size(), is(0));
- assertThat(repository.addListener(listener1), is(true));
- assertThat(repository.getListeners().size(), is(1));
- assertThat(repository.addListener(listener1), is(false));
- assertThat(repository.getListeners().size(), is(1));
- }
-
- @Test
- public void shouldAddDifferentListeners() {
- assertThat(repository.getListeners().size(), is(0));
- assertThat(repository.addListener(listener1), is(true));
- assertThat(repository.getListeners().size(), is(1));
- assertThat(repository.addListener(listener2), is(true));
- assertThat(repository.getListeners().size(), is(2));
- assertThat(repository.getListeners(), hasItems(listener1, listener2));
- assertThat(repository.getListeners().get(0), is(sameInstance(listener1)));
- assertThat(repository.getListeners().get(1), is(sameInstance(listener2)));
- }
-
- @Test
- public void shouldAllowReorderingOfListeners() {
- assertThat(repository.getListeners().size(), is(0));
- assertThat(repository.addListener(listener1), is(true));
- assertThat(repository.addListener(listener2), is(true));
- assertThat(repository.getListeners().size(), is(2));
- assertThat(repository.getListeners(), hasItems(listener1, listener2));
- repository.getListeners().remove(0);
- repository.getListeners().add(1, listener1);
- assertThat(repository.getListeners(), hasItems(listener2, listener1));
- assertThat(repository.getListeners().get(0), is(sameInstance(listener2)));
- assertThat(repository.getListeners().get(1), is(sameInstance(listener1)));
- }
-
- @Test
- public void shouldAllowRemovalOfListeners() {
- assertThat(repository.getListeners().size(), is(0));
- assertThat(repository.addListener(listener1), is(true));
- assertThat(repository.addListener(listener2), is(true));
- assertThat(repository.getListeners(), hasItems(listener1, listener2));
- assertThat(repository.removeListener(listener1), is(true));
- assertThat(repository.getListeners(), hasItems(listener2));
- assertThat(repository.removeListener(listener2), is(true));
- assertThat(repository.getListeners(), hasItems(new RepositorySourceListener[] {}));
- }
-
- @Test
- public void shouldNotRemoveListenerThatIsNotAlreadyRegistered() {
- assertThat(repository.getListeners().size(), is(0));
- assertThat(repository.addListener(listener1), is(true));
- assertThat(repository.getListeners().size(), is(1));
- assertThat(repository.removeListener(listener2), is(false));
- }
-
- @Test
public void shouldHaveConfigurationAfterInitialization() {
assertThat(repository.getWorkspaceConfigurations(), is(notNullValue()));
assertThat(repository.getWorkspaceConfigurations().get("workspace"), is(sameInstance(config)));
Modified: trunk/extensions/dna-connector-filesystem/src/main/java/org/jboss/dna/connector/filesystem/FileSystemConnection.java
===================================================================
--- trunk/extensions/dna-connector-filesystem/src/main/java/org/jboss/dna/connector/filesystem/FileSystemConnection.java 2009-05-19 17:40:25 UTC (rev 912)
+++ trunk/extensions/dna-connector-filesystem/src/main/java/org/jboss/dna/connector/filesystem/FileSystemConnection.java 2009-05-19 20:15:05 UTC (rev 913)
@@ -26,14 +26,12 @@
import java.io.File;
import java.io.FilenameFilter;
import java.util.Set;
-import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.TimeUnit;
import javax.transaction.xa.XAResource;
import org.jboss.dna.graph.ExecutionContext;
import org.jboss.dna.graph.cache.CachePolicy;
import org.jboss.dna.graph.connector.RepositoryConnection;
import org.jboss.dna.graph.connector.RepositorySourceException;
-import org.jboss.dna.graph.connector.RepositorySourceListener;
import org.jboss.dna.graph.request.Request;
import org.jboss.dna.graph.request.processor.RequestProcessor;
@@ -50,7 +48,6 @@
private final CachePolicy cachePolicy;
private final Set<String> availableWorkspaceNames;
private final boolean creatingWorkspacesAllowed;
- private final CopyOnWriteArrayList<RepositorySourceListener> listeners = new CopyOnWriteArrayList<RepositorySourceListener>();
private final FilenameFilter filenameFilter;
private final boolean updatesAllowed;
@@ -103,17 +100,6 @@
/**
* {@inheritDoc}
*
- * @see org.jboss.dna.graph.connector.RepositoryConnection#setListener(org.jboss.dna.graph.connector.RepositorySourceListener)
- */
- public void setListener( RepositorySourceListener listener ) {
- if (listener != null) {
- listeners.addIfAbsent(listener);
- }
- }
-
- /**
- * {@inheritDoc}
- *
* @see org.jboss.dna.graph.connector.RepositoryConnection#ping(long, java.util.concurrent.TimeUnit)
*/
public boolean ping( long time,
Modified: trunk/extensions/dna-connector-jbosscache/src/main/java/org/jboss/dna/connector/jbosscache/JBossCacheConnection.java
===================================================================
--- trunk/extensions/dna-connector-jbosscache/src/main/java/org/jboss/dna/connector/jbosscache/JBossCacheConnection.java 2009-05-19 17:40:25 UTC (rev 912)
+++ trunk/extensions/dna-connector-jbosscache/src/main/java/org/jboss/dna/connector/jbosscache/JBossCacheConnection.java 2009-05-19 20:15:05 UTC (rev 913)
@@ -29,7 +29,6 @@
import org.jboss.dna.graph.cache.CachePolicy;
import org.jboss.dna.graph.connector.RepositoryConnection;
import org.jboss.dna.graph.connector.RepositorySourceException;
-import org.jboss.dna.graph.connector.RepositorySourceListener;
import org.jboss.dna.graph.request.Request;
import org.jboss.dna.graph.request.processor.RequestProcessor;
@@ -40,20 +39,8 @@
*/
public class JBossCacheConnection implements RepositoryConnection {
- protected static final RepositorySourceListener NO_OP_LISTENER = new RepositorySourceListener() {
-
- /**
- * {@inheritDoc}
- */
- public void notify( String sourceName,
- Object... events ) {
- // do nothing
- }
- };
-
private final JBossCacheSource source;
private final JBossCacheWorkspaces workspaces;
- private RepositorySourceListener listener = NO_OP_LISTENER;
JBossCacheConnection( JBossCacheSource source,
JBossCacheWorkspaces workspaces ) {
@@ -95,13 +82,6 @@
/**
* {@inheritDoc}
*/
- public void setListener( RepositorySourceListener listener ) {
- this.listener = listener != null ? listener : NO_OP_LISTENER;
- }
-
- /**
- * {@inheritDoc}
- */
public void close() {
// do nothing
}
@@ -123,11 +103,4 @@
processor.close();
}
}
-
- /**
- * @return listener
- */
- protected RepositorySourceListener getListener() {
- return this.listener;
- }
}
Modified: trunk/extensions/dna-connector-jdbc-metadata/src/main/java/org/jboss/dna/connector/jdbc/JdbcConnection.java
===================================================================
--- trunk/extensions/dna-connector-jdbc-metadata/src/main/java/org/jboss/dna/connector/jdbc/JdbcConnection.java 2009-05-19 17:40:25 UTC (rev 912)
+++ trunk/extensions/dna-connector-jdbc-metadata/src/main/java/org/jboss/dna/connector/jdbc/JdbcConnection.java 2009-05-19 20:15:05 UTC (rev 913)
@@ -26,17 +26,14 @@
import java.sql.Connection;
import java.sql.SQLException;
import java.util.UUID;
-import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.TimeUnit;
-import javax.transaction.xa.XAResource;
import javax.sql.XAConnection;
-
+import javax.transaction.xa.XAResource;
import org.jboss.dna.common.util.Logger;
import org.jboss.dna.graph.ExecutionContext;
import org.jboss.dna.graph.cache.CachePolicy;
import org.jboss.dna.graph.connector.RepositoryConnection;
import org.jboss.dna.graph.connector.RepositorySourceException;
-import org.jboss.dna.graph.connector.RepositorySourceListener;
import org.jboss.dna.graph.request.Request;
import org.jboss.dna.graph.request.processor.RequestProcessor;
@@ -53,14 +50,13 @@
private final String name;
private final CachePolicy cachePolicy;
- private final CopyOnWriteArrayList<RepositorySourceListener> listeners = new CopyOnWriteArrayList<RepositorySourceListener>();
private final Connection connection;
private final UUID rootNodeUuid;
/*package*/JdbcConnection( String sourceName,
- CachePolicy cachePolicy,
- Connection connection,
- UUID rootNodeUuid) {
+ CachePolicy cachePolicy,
+ Connection connection,
+ UUID rootNodeUuid ) {
assert sourceName != null;
assert connection != null;
assert rootNodeUuid != null;
@@ -82,17 +78,6 @@
/**
* {@inheritDoc}
*
- * @see org.jboss.dna.graph.connector.RepositoryConnection#setListener(org.jboss.dna.graph.connector.RepositorySourceListener)
- */
- public void setListener( RepositorySourceListener listener ) {
- if (listener != null) {
- listeners.addIfAbsent(listener);
- }
- }
-
- /**
- * {@inheritDoc}
- *
* @see org.jboss.dna.graph.connector.RepositoryConnection#getDefaultCachePolicy()
*/
public CachePolicy getDefaultCachePolicy() {
@@ -110,13 +95,13 @@
try {
return ((XAConnection)connection).getXAResource();
} catch (SQLException e) {
- // handle an exception silently so far and write it to the log
+ // handle an exception silently so far and write it to the log
log.error(e, JdbcMetadataI18n.unableToGetXAResource, getSourceName());
return null;
}
}
// default
- return null;
+ return null;
}
/**
@@ -129,7 +114,7 @@
try {
// JDBC 4 has a method to check validity of a connection (connection.isValid(timeout))
// but many drivers didn't get updated with latest spec
- return connection != null && ! connection.isClosed();
+ return connection != null && !connection.isClosed();
} catch (SQLException e) {
// debug
if (log.isDebugEnabled()) {
@@ -147,8 +132,8 @@
*/
public void execute( ExecutionContext context,
Request request ) throws RepositorySourceException {
- // create processor and delegate handling
- RequestProcessor proc = new JdbcRequestProcesor(getSourceName(),context, connection, rootNodeUuid);
+ // create processor and delegate handling
+ RequestProcessor proc = new JdbcRequestProcesor(getSourceName(), context, connection, rootNodeUuid);
try {
proc.process(request);
} finally {
@@ -164,7 +149,7 @@
public void close() {
try {
// release the JDBC connection resource
- if (connection != null && ! connection.isClosed()) {
+ if (connection != null && !connection.isClosed()) {
connection.close();
}
} catch (Exception e) {
Modified: trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/JpaConnection.java
===================================================================
--- trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/JpaConnection.java 2009-05-19 17:40:25 UTC (rev 912)
+++ trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/JpaConnection.java 2009-05-19 20:15:05 UTC (rev 913)
@@ -24,7 +24,6 @@
package org.jboss.dna.connector.store.jpa;
import java.util.UUID;
-import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.TimeUnit;
import javax.persistence.EntityManager;
import javax.transaction.xa.XAResource;
@@ -32,7 +31,6 @@
import org.jboss.dna.graph.cache.CachePolicy;
import org.jboss.dna.graph.connector.RepositoryConnection;
import org.jboss.dna.graph.connector.RepositorySourceException;
-import org.jboss.dna.graph.connector.RepositorySourceListener;
import org.jboss.dna.graph.request.Request;
import org.jboss.dna.graph.request.processor.RequestProcessor;
@@ -43,7 +41,6 @@
private final String name;
private final CachePolicy cachePolicy;
- private final CopyOnWriteArrayList<RepositorySourceListener> listeners = new CopyOnWriteArrayList<RepositorySourceListener>();
private final EntityManager entityManager;
private final Model model;
private final UUID rootNodeUuid;
@@ -94,17 +91,6 @@
/**
* {@inheritDoc}
*
- * @see org.jboss.dna.graph.connector.RepositoryConnection#setListener(org.jboss.dna.graph.connector.RepositorySourceListener)
- */
- public void setListener( RepositorySourceListener listener ) {
- if (listener != null) {
- listeners.addIfAbsent(listener);
- }
- }
-
- /**
- * {@inheritDoc}
- *
* @see org.jboss.dna.graph.connector.RepositoryConnection#getDefaultCachePolicy()
*/
public CachePolicy getDefaultCachePolicy() {
Modified: trunk/extensions/dna-connector-svn/src/main/java/org/jboss/dna/connector/svn/SVNRepositoryConnection.java
===================================================================
--- trunk/extensions/dna-connector-svn/src/main/java/org/jboss/dna/connector/svn/SVNRepositoryConnection.java 2009-05-19 17:40:25 UTC (rev 912)
+++ trunk/extensions/dna-connector-svn/src/main/java/org/jboss/dna/connector/svn/SVNRepositoryConnection.java 2009-05-19 20:15:05 UTC (rev 913)
@@ -30,7 +30,6 @@
import org.jboss.dna.graph.cache.CachePolicy;
import org.jboss.dna.graph.connector.RepositoryConnection;
import org.jboss.dna.graph.connector.RepositorySourceException;
-import org.jboss.dna.graph.connector.RepositorySourceListener;
import org.jboss.dna.graph.property.PathFactory;
import org.jboss.dna.graph.property.PropertyFactory;
import org.jboss.dna.graph.request.Request;
@@ -48,22 +47,10 @@
*/
public class SVNRepositoryConnection implements RepositoryConnection {
- protected static final RepositorySourceListener NO_OP_LISTENER = new RepositorySourceListener() {
-
- /**
- * {@inheritDoc}
- */
- public void notify( String sourceName,
- Object... events ) {
- // do nothing
- }
- };
-
private final String sourceName;
private final CachePolicy cachePolicy;
private final SVNRepository repository;
private final boolean updatesAllowed;
- private RepositorySourceListener listener = NO_OP_LISTENER;
public SVNRepositoryConnection( String sourceName,
CachePolicy cachePolicy,
@@ -142,13 +129,6 @@
/**
* {@inheritDoc}
- */
- public void setListener( RepositorySourceListener listener ) {
- this.listener = listener != null ? listener : NO_OP_LISTENER;
- }
-
- /**
- * {@inheritDoc}
*
* @see org.jboss.dna.graph.connector.RepositoryConnection#close()
*/
@@ -176,11 +156,4 @@
processor.close();
}
}
-
- /**
- * @return listener
- */
- protected RepositorySourceListener getListener() {
- return this.listener;
- }
}
Modified: trunk/extensions/dna-connector-svn/src/test/java/org/jboss/dna/connector/svn/SVNRepositoryConnectionTest.java
===================================================================
--- trunk/extensions/dna-connector-svn/src/test/java/org/jboss/dna/connector/svn/SVNRepositoryConnectionTest.java 2009-05-19 17:40:25 UTC (rev 912)
+++ trunk/extensions/dna-connector-svn/src/test/java/org/jboss/dna/connector/svn/SVNRepositoryConnectionTest.java 2009-05-19 20:15:05 UTC (rev 913)
@@ -23,8 +23,6 @@
*/
package org.jboss.dna.connector.svn;
-import static org.mockito.Mockito.stub;
-import static org.mockito.Mockito.verify;
import static org.hamcrest.core.Is.is;
import static org.hamcrest.core.IsNull.notNullValue;
import static org.hamcrest.core.IsSame.sameInstance;
@@ -37,7 +35,6 @@
import java.util.List;
import java.util.concurrent.TimeUnit;
import org.jboss.dna.common.text.UrlEncoder;
-import org.jboss.dna.common.util.FileUtil;
import org.jboss.dna.graph.DnaLexicon;
import org.jboss.dna.graph.ExecutionContext;
import org.jboss.dna.graph.Graph;
@@ -46,7 +43,6 @@
import org.jboss.dna.graph.Location;
import org.jboss.dna.graph.Node;
import org.jboss.dna.graph.cache.CachePolicy;
-import org.jboss.dna.graph.connector.RepositorySourceListener;
import org.jboss.dna.graph.property.Binary;
import org.jboss.dna.graph.property.DateTimeFactory;
import org.jboss.dna.graph.property.Name;
@@ -148,31 +144,11 @@
@Test
public void shouldGetTheSVNRepositoryRootFromTheSVNRepositoryWhenPinged() throws Exception {
CachePolicy policy = mock(CachePolicy.class);
- repository = SVNConnectorTestUtil.createRepository(svnUrl, "sp", "");
+ repository = SVNConnectorTestUtil.createRepository(svnUrl, "sp", "");
connection = new SVNRepositoryConnection("the source name", policy, false, repository);
assertThat(connection.ping(1, TimeUnit.SECONDS), is(true));
}
- @Test
- public void shouldHaveNoOpListenerWhenCreated() {
- assertThat(connection.getListener(), is(sameInstance(SVNRepositoryConnection.NO_OP_LISTENER)));
- }
-
- @Test
- public void shouldUseNoOpListenerWhenSettingListenerToNull() {
- connection.setListener(null);
- assertThat(connection.getListener(), is(sameInstance(SVNRepositoryConnection.NO_OP_LISTENER)));
- }
-
- @Test
- public void shouldSetListenerToNonNullValue() {
- RepositorySourceListener listener = mock(RepositorySourceListener.class);
- connection.setListener(listener);
- assertThat(connection.getListener(), is(sameInstance(listener)));
- connection.setListener(null);
- assertThat(connection.getListener(), is(sameInstance(SVNRepositoryConnection.NO_OP_LISTENER)));
- }
-
@Test( expected = PathNotFoundException.class )
public void shouldFailToGetChildrenFromAWrongRequestedPath() {
List<Location> l = graph.getChildren().of(pathFactory.create("wrongRequestedPath"));
@@ -253,16 +229,22 @@
@Test
public void shouldAddAndDeleteChildUnderRootNode() throws Exception {
- graph.batch().create("/nodeC").with(propertyFactory().create(JcrLexicon.PRIMARY_TYPE, JcrNtLexicon.FOLDER)).and(propertyFactory().create(JcrLexicon.CREATED,
- dateFactory().create(new Date()))).execute();
+ graph.batch()
+ .create("/nodeC")
+ .with(propertyFactory().create(JcrLexicon.PRIMARY_TYPE, JcrNtLexicon.FOLDER))
+ .and(propertyFactory().create(JcrLexicon.CREATED, dateFactory().create(new Date())))
+ .execute();
// Now look up the root node ...
Node root = graph.getNodeAt("/");
assertThat(root, is(notNullValue()));
assertThat(root.getChildren(), hasChild(child("nodeC")));
SVNNodeKind nodeCKind = repository.checkPath("nodeC", -1);
assertThat(nodeCKind, is(SVNNodeKind.DIR));
- graph.batch().create("/nodeC/nodeC_1").with(propertyFactory().create(JcrLexicon.PRIMARY_TYPE, JcrNtLexicon.FOLDER)).and(propertyFactory().create(JcrLexicon.CREATED,
- dateFactory().create(new Date()))).execute();
+ graph.batch()
+ .create("/nodeC/nodeC_1")
+ .with(propertyFactory().create(JcrLexicon.PRIMARY_TYPE, JcrNtLexicon.FOLDER))
+ .and(propertyFactory().create(JcrLexicon.CREATED, dateFactory().create(new Date())))
+ .execute();
// Now look up the root node ...
Node nodeC = graph.getNodeAt("/nodeC");
assertThat(nodeC, is(notNullValue()));
@@ -272,8 +254,12 @@
byte[] content1 = "My content".getBytes();
Property jcrDataProperty = propertyFactory().create(JcrLexicon.DATA, binaryFactory().create(content1));
- graph.batch().create("/nodeC/nodeC_1/file1.txt").with(propertyFactory().create(JcrLexicon.PRIMARY_TYPE, JcrNtLexicon.FILE)).and(propertyFactory().create(JcrLexicon.CREATED,
- new Date())).and(jcrDataProperty).execute();
+ graph.batch()
+ .create("/nodeC/nodeC_1/file1.txt")
+ .with(propertyFactory().create(JcrLexicon.PRIMARY_TYPE, JcrNtLexicon.FILE))
+ .and(propertyFactory().create(JcrLexicon.CREATED, new Date()))
+ .and(jcrDataProperty)
+ .execute();
// Look up the file
Node nodeC1 = graph.getNodeAt("/nodeC/nodeC_1");
16 years, 11 months
DNA SVN: r912 - in trunk: dna-graph/src/main/java/org/jboss/dna/graph/connector/federation and 5 other directories.
by dna-commits@lists.jboss.org
Author: rhauch
Date: 2009-05-19 13:40:25 -0400 (Tue, 19 May 2009)
New Revision: 912
Added:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/observe/
trunk/dna-graph/src/main/java/org/jboss/dna/graph/observe/ChangeFeed.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/observe/ChangeObserver.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/observe/ChangeObservers.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/observe/ChangeTimeline.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/observe/Changes.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/observe/Observable.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/observe/package-info.java
Modified:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/GraphI18n.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/ForkRequestProcessor.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CacheableRequest.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ChangeRequest.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CloneWorkspaceRequest.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CompositeRequest.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CopyBranchRequest.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CreateNodeRequest.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CreateWorkspaceRequest.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/DeleteBranchRequest.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/DeleteChildrenRequest.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/GetWorkspacesRequest.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/MoveBranchRequest.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadAllChildrenRequest.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadAllPropertiesRequest.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadBlockOfChildrenRequest.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadBranchRequest.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadNextBlockOfChildrenRequest.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadNodeRequest.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadPropertyRequest.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/RemovePropertyRequest.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/RenameNodeRequest.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/Request.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/SetPropertyRequest.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/UpdatePropertiesRequest.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/VerifyNodeExistsRequest.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/VerifyWorkspaceRequest.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/processor/RequestProcessor.java
trunk/dna-graph/src/main/resources/org/jboss/dna/graph/GraphI18n.properties
trunk/extensions/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/FederatingRequestProcessor.java
Log:
DNA-252 Complete event support in the connector API
Added initial version/proposal for events/observation in the JBoss DNA graph API into the 'org.jboss.dna.graph.observe' package. The framework consists of a ChangeObserver abstract class (this is the listener); an Observable interface with which observers can be registered and unregistered; a ChangeObservers class that provides a default Observable implementation ready for use in other Observable implementations; a ChangeFeed interface used to poll for changes during a specified timeline; a Changes class containing the individual changes in an atomic operation and that is passed to the observers.
This design reuses the Request classes as the events, allowing for more advanced observation (leverage the additional information in change requests, such as in UpdatePropertiesRequest) as well as replaying events.
This commit adds the framework (including several thread-safe implementation classes), but does not make use of the framework in the connector API or graph API. Those changes will be coming subsequently.
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/GraphI18n.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/GraphI18n.java 2009-05-18 15:36:58 UTC (rev 911)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/GraphI18n.java 2009-05-19 17:40:25 UTC (rev 912)
@@ -84,6 +84,7 @@
public static I18n actualOldLocationMustHavePath;
public static I18n actualNewLocationMustHaveSameParentAsOldLocation;
public static I18n actualNewLocationMustHaveSameNameAsRequest;
+ public static I18n requestIsFrozenAndMayNotBeChanged;
public static I18n errorImportingContent;
public static I18n unableToFindRepositorySourceWithName;
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/ForkRequestProcessor.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/ForkRequestProcessor.java 2009-05-18 15:36:58 UTC (rev 911)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/ForkRequestProcessor.java 2009-05-19 17:40:25 UTC (rev 912)
@@ -669,6 +669,16 @@
/**
* {@inheritDoc}
*
+ * @see org.jboss.dna.graph.request.processor.RequestProcessor#completeRequest(org.jboss.dna.graph.request.Request)
+ */
+ @Override
+ protected void completeRequest( Request request ) {
+ // Do nothing here, as this is the federated request
+ }
+
+ /**
+ * {@inheritDoc}
+ *
* @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.VerifyNodeExistsRequest)
*/
@Override
Copied: trunk/dna-graph/src/main/java/org/jboss/dna/graph/observe/ChangeFeed.java (from rev 911, trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CacheableRequest.java)
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/observe/ChangeFeed.java (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/observe/ChangeFeed.java 2009-05-19 17:40:25 UTC (rev 912)
@@ -0,0 +1,52 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ *
+ * JBoss DNA 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.graph.observe;
+
+import org.jboss.dna.graph.property.DateTime;
+
+/**
+ * Interface used to poll for the changes that have occurred during a specified time period.
+ */
+public interface ChangeFeed {
+
+ /**
+ * Get the changes that were made since the supplied timestamp.
+ *
+ * @param timestamp the timestamp after which all changes should be returned
+ * @return the iterator over the changes that occurred after the supplied timestamp.
+ */
+ ChangeTimeline changesSince( DateTime timestamp );
+
+ /**
+ * Get the changes that were made since the supplied timestamp.
+ *
+ * @param beginning the timestamp at the beginning of the timeline (exclusive), or null if the earliest timestamp should be
+ * used
+ * @param end the timestamp at the end of the timeline (inclusive), or null if the current timestamp should be used
+ * @return the iterator over the changes that occurred after the supplied timestamp.
+ */
+ ChangeTimeline changesBetween( DateTime beginning,
+ DateTime end );
+
+}
Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/observe/ChangeObserver.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/observe/ChangeObserver.java (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/observe/ChangeObserver.java 2009-05-19 17:40:25 UTC (rev 912)
@@ -0,0 +1,159 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ *
+ * JBoss DNA 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.graph.observe;
+
+import java.lang.ref.WeakReference;
+import java.util.concurrent.CopyOnWriteArraySet;
+import net.jcip.annotations.ThreadSafe;
+import org.jboss.dna.common.util.Logger;
+
+/**
+ * Abstract class that is used to signal that a change set has occurred. This class is typically subclassed by those that wish to
+ * observe changes in content, and {@link Observable#register(ChangeObserver) registered} with a {@link Observable}.
+ * <p>
+ * This class maintains a (weak) reference to the ChangeSource instances with which it is registered. Therefore, the observers
+ * will not keep a ChangeSource from being garbage collected. And, if a change source is garbage collected, calling
+ * {@link #unregister()} will clean up naturally.
+ * </p>
+ */
+@ThreadSafe
+public abstract class ChangeObserver {
+
+ private final CopyOnWriteArraySet<ChangeSourceReference> sources = new CopyOnWriteArraySet<ChangeSourceReference>();
+
+ protected ChangeObserver() {
+ }
+
+ /**
+ * Records that this listener has successfully registered by the supplied {@link Observable}.
+ *
+ * @param source the source with which this listener was registered
+ */
+ final void registeredWith( Observable source ) {
+ sources.add(new ChangeSourceReference(source));
+ }
+
+ /**
+ * Records that this listener has successfully unregistered by the supplied {@link Observable}.
+ *
+ * @param source the source with which this listener was registered
+ */
+ final void unregisteredWith( Observable source ) {
+ sources.remove(new ChangeSourceReference(source));
+ }
+
+ /**
+ * Unregister this listener from all {@link Observable sources} that it was registered with. This is preferred over calling
+ * {@link Observable#unregister(ChangeObserver)} directly.
+ */
+ public void unregister() {
+ doUnregister();
+ }
+
+ /**
+ * Method called by {@link #unregister()} that actually does the unregistering. This method is final.
+ */
+ protected final void doUnregister() {
+ // Unregister this listener from each source ...
+ for (ChangeSourceReference sourceReference : sources) {
+ Observable source = sourceReference.get();
+ if (source != null) {
+ try {
+ source.unregister(this);
+ } catch (Throwable t) {
+ Logger.getLogger(getClass()).debug(t, "Error while unregistering {0} from {1}", this, source);
+ }
+ }
+ }
+ }
+
+ /**
+ * Determine whether this observer is currently registered with any {@link Observable} instances.
+ * <p>
+ * Although an observer might be registered with an {@link Observable}, if that Observable is garbage collected, then this
+ * observer will no longer be registered with it.
+ * </p>
+ *
+ * @return true if this observer is registered with at least one {@link Observable} instance, or false if this observer is not
+ * currently registered with any {@link Observable} instances.
+ */
+ public boolean isRegistered() {
+ for (ChangeSourceReference reference : sources) {
+ if (reference.get() != null) return true;
+ }
+ return false;
+ }
+
+ /**
+ * Method that is called for each {@link Changes} from the {@link Observable} instance(s) with which this listener is
+ * registered.
+ *
+ * @param changeSet the change set
+ */
+ public abstract void notify( Changes changeSet );
+
+ /**
+ * A {@link WeakReference} implementation that provides a valid
+ */
+ protected final class ChangeSourceReference extends WeakReference<Observable> {
+ final int hc;
+
+ protected ChangeSourceReference( Observable source ) {
+ super(source);
+ this.hc = source.hashCode();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ return hc;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals( Object obj ) {
+ if (obj == this) return true;
+ if (obj instanceof ChangeSourceReference) {
+ ChangeSourceReference that = (ChangeSourceReference)obj;
+ Observable thisSource = this.get();
+ Observable thatSource = that.get();
+ return thisSource == thatSource; // reference equality, not object equality!
+ }
+ if (obj instanceof Observable) {
+ Observable that = (Observable)obj;
+ return this.get() == that; // reference equality, not object equality!
+ }
+ return false;
+ }
+ }
+
+}
Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/observe/ChangeObserver.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Copied: trunk/dna-graph/src/main/java/org/jboss/dna/graph/observe/ChangeObservers.java (from rev 911, trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CacheableRequest.java)
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/observe/ChangeObservers.java (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/observe/ChangeObservers.java 2009-05-19 17:40:25 UTC (rev 912)
@@ -0,0 +1,66 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ *
+ * JBoss DNA 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.graph.observe;
+
+import java.util.concurrent.CopyOnWriteArrayList;
+import net.jcip.annotations.ThreadSafe;
+
+/**
+ * Reusable manager of change listeners, typically employed by another {@link Observable} implementation.
+ */
+@ThreadSafe
+public class ChangeObservers implements Observable {
+
+ private CopyOnWriteArrayList<ChangeObserver> observers = new CopyOnWriteArrayList<ChangeObserver>();
+
+ public ChangeObservers() {
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.observe.Observable#register(org.jboss.dna.graph.observe.ChangeObserver)
+ */
+ public boolean register( ChangeObserver observer ) {
+ if (observer != null && observers.addIfAbsent(observer)) {
+ observer.registeredWith(this);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.observe.Observable#unregister(org.jboss.dna.graph.observe.ChangeObserver)
+ */
+ public boolean unregister( ChangeObserver observer ) {
+ if (observer != null && observers.remove(observer)) {
+ observer.unregisteredWith(this);
+ return true;
+ }
+ return false;
+ }
+
+}
Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/observe/ChangeTimeline.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/observe/ChangeTimeline.java (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/observe/ChangeTimeline.java 2009-05-19 17:40:25 UTC (rev 912)
@@ -0,0 +1,93 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ *
+ * JBoss DNA 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.graph.observe;
+
+import java.util.Collection;
+import java.util.Iterator;
+import net.jcip.annotations.Immutable;
+import org.jboss.dna.graph.property.DateTime;
+
+/**
+ * The changes that were made since some time.
+ */
+@Immutable
+public class ChangeTimeline implements Iterable<Changes> {
+
+ private final Collection<Changes> changes;
+ private final DateTime after;
+ private final DateTime until;
+
+ public ChangeTimeline( Collection<Changes> changes,
+ DateTime after,
+ DateTime until ) {
+ this.changes = changes;
+ this.after = after;
+ this.until = until;
+ }
+
+ /**
+ * Get the timestamp after which all the changes occur.
+ *
+ * @return the timestamp of the changes; never null
+ */
+ public DateTime after() {
+ return after;
+ }
+
+ /**
+ * Get the timestamp of the last change set.
+ *
+ * @return the timestamp of the changes; never null
+ */
+ public DateTime until() {
+ return until;
+ }
+
+ /**
+ * Get the number of change sets.
+ *
+ * @return the number of change sets
+ */
+ public int size() {
+ return changes.size();
+ }
+
+ /**
+ * Deterine if there were no changes durign this timeline.
+ *
+ * @return true if this timeline is empty, or false if there is at least one change
+ */
+ public boolean isEmpty() {
+ return changes.isEmpty();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Iterable#iterator()
+ */
+ public Iterator<Changes> iterator() {
+ return changes.iterator();
+ }
+}
Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/observe/ChangeTimeline.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Copied: trunk/dna-graph/src/main/java/org/jboss/dna/graph/observe/Changes.java (from rev 911, trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CacheableRequest.java)
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/observe/Changes.java (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/observe/Changes.java 2009-05-19 17:40:25 UTC (rev 912)
@@ -0,0 +1,57 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ *
+ * JBoss DNA 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.graph.observe;
+
+import javax.security.auth.Subject;
+import net.jcip.annotations.Immutable;
+import org.jboss.dna.graph.property.DateTime;
+import org.jboss.dna.graph.request.ChangeRequest;
+
+/**
+ * A set of changes that were made atomically. Each change is in the form of a frozen {@link ChangeRequest}.
+ */
+@Immutable
+public interface Changes extends Iterable<ChangeRequest> {
+
+ /**
+ * Get the user that made these changes.
+ *
+ * @return the user; never null
+ */
+ public Subject getUser();
+
+ /**
+ * Get the name of the source that was changed.
+ *
+ * @return the source name; never null
+ */
+ public String getSourceName();
+
+ /**
+ * Get the timestamp that the changes were made. All changes within the change set were all made at this instant in time.
+ *
+ * @return the timestamp of the changes; never null
+ */
+ public DateTime getTimestamp();
+}
Copied: trunk/dna-graph/src/main/java/org/jboss/dna/graph/observe/Observable.java (from rev 911, trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CacheableRequest.java)
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/observe/Observable.java (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/observe/Observable.java 2009-05-19 17:40:25 UTC (rev 912)
@@ -0,0 +1,52 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ *
+ * JBoss DNA 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.graph.observe;
+
+import net.jcip.annotations.ThreadSafe;
+
+/**
+ * Interface used to register {@link ChangeObserver listeners}. Implementations should use a {@link ChangeObservers} to actually
+ * manage the listeners.
+ */
+@ThreadSafe
+public interface Observable {
+
+ /**
+ * Register the supplied observer. This method does nothing if the observer reference is null.
+ *
+ * @param observer the observer to be added; may be null
+ * @return true if the observer was added, or false if the observer was null or if the observer was already registered
+ */
+ boolean register( ChangeObserver observer );
+
+ /**
+ * Unregister the supplied observer. This method does nothing if the observer reference is null.
+ *
+ * @param observer the observer to be removed; may not be null
+ * @return true if the observer was removed, or false if the observer was null or if the observer was not registered on this
+ * source
+ */
+ boolean unregister( ChangeObserver observer );
+
+}
Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/observe/package-info.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/observe/package-info.java (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/observe/package-info.java 2009-05-19 17:40:25 UTC (rev 912)
@@ -0,0 +1,107 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ *
+ * JBoss DNA 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.
+ */
+/**
+ * The Observation API provides several mechanisms for asynchronously observing changes to content.
+ * <p>
+ * Many event frameworks define the listeners and sources as interfaces. While this is often useful, it requires
+ * the implementations properly address the thread-safe semantics of managing and calling the listeners.
+ * This observation framework uses abstract or concrete classes to minimize the effort required for implementing
+ * {@link ChangeObserver} or {@link Observable}. The classes also allow the framework to implement a number of
+ * utility methods, such as the {@link ChangeObserver#unregister() unregister()} method on ChangeObserver, that
+ * also save effort and code.
+ * </p>
+ * <p>
+ * However, one of the more important reasons for providing classes is that {@link ChangeObserver} uses
+ * {@link java.lang.ref.WeakReference weak references} to track the {@link Observable} instances, and the {@link ChangeObservers}
+ * class uses weak references for the listeners. This means that if an observers do not prevent {@link Observable} instances
+ * from being garbage collected, nor do observers prevent {@link Observable} instances from being garbage collected.
+ * </p>
+ * <h3>Observable</h3>
+ * <p>
+ * Any component that can have changes and be observed can implement the {@link Observable} interface. This interface
+ * allows Observers to register (or be registered) to receive notifications of the changes. However, a concrete and thread-safe
+ * implementation of this interface, called {@link ChangeObservers}, is available and should be used where possible, since it
+ * automatically manages the registered {@link ChangeObserver} instances and properly implements the register and unregister mechanisms.
+ * </p>
+ * <h3>Observers</h3>
+ * <p>
+ * Components that are to recieve notifications of changes are called <i>observers</i>. To create an observer, simply extend
+ * the {@link ChangeObserver} abstract class and provide an implementation of the {@link ChangeObserver#notify(Changes)} method.
+ * Then, register the observer with an {@link Observable} using its {@link Observable#register(ChangeObserver)} method.
+ * The observer's {@link ChangeObserver#notify(Changes)} method will then be called with the changes that have
+ * been made to the Observable.
+ * </p>
+ * <p>When an observer is no longer needed, it should be unregistered from all {@link Observable} instances with which
+ * it was registered. The {@link ChangeObserver} class automatically tracks which {@link Observable} instances it is
+ * registered with, and calling the observer's {@link ChangeObserver#unregister()} will unregister the observer from
+ * all of these Observables. Alternatively, an observer can be unregistered from a single Observable using the
+ * Observable's {@link Observable#unregister(ChangeObserver)} method.
+ * </p>
+ * <h3>Changes</h3>
+ * <p>
+ * The {@link Changes} class represents the set of individual changes that have been made during a single, atomic
+ * operation. Each {@link Changes} instance has information about the source of the changes, the timestamp at which
+ * the changes occurred, and the individual changes that were made. These individual changes take the form of
+ * {@link org.jboss.dna.graph.request.ChangeRequest} objects, such as {@link org.jboss.dna.graph.request.CreateNodeRequest},
+ * {@link org.jboss.dna.graph.request.DeleteBranchRequest}, etc. Each request is
+ * {@link org.jboss.dna.graph.request.Request#isFrozen() frozen}, meaning it is immutable and will not change. Also
+ * none of the requests will be {@link org.jboss.dna.graph.request.Request#isCancelled() cancelled}.
+ * </p>
+ * <p>
+ * Using the actual {@link org.jboss.dna.graph.request.ChangeRequest} objects as the "events" has a number of advantages.
+ * First, there are already a number of existing {@link org.jboss.dna.graph.request.ChangeRequest} subclasses that describe
+ * various types of changes in quite a bit of detail; thus no need to duplicate the structure or come up with a generic
+ * event class.
+ * </p>
+ * <p>
+ * Second, the requests have all the state required for an event, plus they often will have more. For example,
+ * the DeleteBranchRequest has the actual location of the branch that was delete (and in this way is not much different than
+ * a more generic event), but the CreateNodeRequest has the actual location of the created node along with the properties
+ * of that node. Additionally, the RemovePropertyRequest has the actual location of the node along with the name of the property
+ * that was removed. In many cases, these requests have all the information a more general event class might have but
+ * then hopefully enough information for many observers to use directly without having to read the graph to decide what
+ * actually changed.
+ * </p>
+ * <p>
+ * Third, the requests that make up a {@link Changes} instance can actually be replayed. Consider the case of a cache
+ * that is backed by a {@link org.jboss.dna.graph.connector.RepositorySource}, which might use an observer to keep the cache in sync.
+ * As the cache is notified of Changes, the cache can simply replay the changes against its source.
+ * </p>
+ * <h3>Change feed and timelines</h3>
+ * <p>
+ * While it is often sufficient to register observers and receive notifications of the changes as they occur, it
+ * is also often useful to be able to ask for the changes that have occurred since some time or date. This allows
+ * a component to poll for changes or, for example, to catch up on the changes that occurred previous to registering
+ * an observer.
+ * </p>
+ * <p>
+ * Components that implement {@link Observable} may also implement the {@link ChangeFeed} interface to allow for
+ * components to {@link ChangeFeed#changesSince(org.jboss.dna.graph.property.DateTime) poll for changes}. The result
+ * of this method is a {@link ChangeTimeline} object that contains the {@link Changes} as well as the time period during which
+ * the change sets occurred. Note that an empty {@link ChangeTimeline} object implies there were no changes made during the time period.
+ * </p>
+ */
+
+package org.jboss.dna.graph.observe;
+
Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/observe/package-info.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CacheableRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CacheableRequest.java 2009-05-18 15:36:58 UTC (rev 911)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CacheableRequest.java 2009-05-19 17:40:25 UTC (rev 912)
@@ -62,16 +62,20 @@
/**
* {@inheritDoc}
*
+ * @throws IllegalStateException if the request is frozen
* @see org.jboss.dna.graph.cache.Cacheable#setCachePolicy(org.jboss.dna.graph.cache.CachePolicy)
*/
public void setCachePolicy( CachePolicy cachePolicy ) {
+ checkNotFrozen();
policy = cachePolicy;
}
/**
* @param timeLoaded Sets timeLoaded to the specified value.
+ * @throws IllegalStateException if the request is frozen
*/
public void setTimeLoaded( DateTime timeLoaded ) {
+ checkNotFrozen();
this.timeLoaded = timeLoaded;
}
}
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ChangeRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ChangeRequest.java 2009-05-18 15:36:58 UTC (rev 911)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ChangeRequest.java 2009-05-19 17:40:25 UTC (rev 912)
@@ -27,10 +27,15 @@
import org.jboss.dna.graph.property.Path;
/**
- *
+ * A Request to make changes in a graph.
*/
-public interface ChangeRequest {
+public abstract class ChangeRequest extends Request {
+ private static final long serialVersionUID = 1L;
+
+ protected ChangeRequest() {
+ }
+
/**
* Determine if this request changes the branch at the given path.
*
@@ -38,13 +43,13 @@
* @param path the path; may not be null
* @return true if this request changes a node under the given path
*/
- boolean changes( String workspace,
- Path path );
+ public abstract boolean changes( String workspace,
+ Path path );
/**
* Get the location of the top-most node that is to be changed by this request.
*
* @return the location changed by this request
*/
- Location changedLocation();
+ public abstract Location changedLocation();
}
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CloneWorkspaceRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CloneWorkspaceRequest.java 2009-05-18 15:36:58 UTC (rev 911)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CloneWorkspaceRequest.java 2009-05-19 17:40:25 UTC (rev 912)
@@ -143,8 +143,10 @@
* name}.
*
* @param actualWorkspaceName the actual name of the workspace that was created, or null if a workspace was not created
+ * @throws IllegalStateException if the request is frozen
*/
public void setActualWorkspaceName( String actualWorkspaceName ) {
+ checkNotFrozen();
this.actualWorkspaceName = actualWorkspaceName;
}
@@ -161,8 +163,10 @@
* Set the actual location of the root node in the new workspace.
*
* @param actualLocationOfRoot the actual location of the workspace's root node.
+ * @throws IllegalStateException if the request is frozen
*/
public void setActualRootLocation( Location actualLocationOfRoot ) {
+ checkNotFrozen();
this.actualLocationOfRoot = actualLocationOfRoot;
}
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CompositeRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CompositeRequest.java 2009-05-18 15:36:58 UTC (rev 911)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CompositeRequest.java 2009-05-19 17:40:25 UTC (rev 912)
@@ -29,6 +29,7 @@
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
+import net.jcip.annotations.Immutable;
import org.jboss.dna.common.util.CheckArg;
/**
@@ -42,6 +43,7 @@
*
* @author Randall Hauch
*/
+@Immutable
public class CompositeRequest extends Request implements Iterable<Request> {
private static final long serialVersionUID = 1L;
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CopyBranchRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CopyBranchRequest.java 2009-05-18 15:36:58 UTC (rev 911)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CopyBranchRequest.java 2009-05-19 17:40:25 UTC (rev 912)
@@ -37,7 +37,7 @@
*
* @author Randall Hauch
*/
-public class CopyBranchRequest extends Request implements ChangeRequest {
+public class CopyBranchRequest extends ChangeRequest {
private static final long serialVersionUID = 1L;
@@ -205,9 +205,11 @@
* {@link Location#isSame(Location) same location} as the {@link #from() from location}; if the new location does not
* represent the {@link Location#isSame(Location) same location} as the {@link #into() into location}; if the either
* location does not have a path
+ * @throws IllegalStateException if the request is frozen
*/
public void setActualLocations( Location fromLocation,
Location intoLocation ) {
+ checkNotFrozen();
if (!from.isSame(fromLocation)) { // not same if actual is null
throw new IllegalArgumentException(GraphI18n.actualLocationIsNotSameAsInputLocation.text(fromLocation, from));
}
@@ -251,6 +253,7 @@
*
* @see org.jboss.dna.graph.request.ChangeRequest#changes(java.lang.String, org.jboss.dna.graph.property.Path)
*/
+ @Override
public boolean changes( String workspace,
Path path ) {
return this.intoWorkspace.equals(workspace) && into.hasPath() && into.getPath().isAtOrBelow(path);
@@ -261,6 +264,7 @@
*
* @see org.jboss.dna.graph.request.ChangeRequest#changedLocation()
*/
+ @Override
public Location changedLocation() {
return into;
}
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CreateNodeRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CreateNodeRequest.java 2009-05-18 15:36:58 UTC (rev 911)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CreateNodeRequest.java 2009-05-19 17:40:25 UTC (rev 912)
@@ -43,7 +43,7 @@
*
* @author Randall Hauch
*/
-public class CreateNodeRequest extends Request implements Iterable<Property>, ChangeRequest {
+public class CreateNodeRequest extends ChangeRequest implements Iterable<Property> {
private static final long serialVersionUID = 1L;
@@ -279,8 +279,10 @@
* used
* @throws IllegalArgumentException if the actual location does not represent the {@link Location#isSame(Location) same
* location} as the {@link #under() current location}, or if the actual location does not have a path.
+ * @throws IllegalStateException if the request is frozen
*/
public void setActualLocationOfNode( Location actual ) {
+ checkNotFrozen();
CheckArg.isNotNull(actual, "actual");
if (!under.isSame(actual, false)) { // not same if actual is null
}
@@ -309,6 +311,7 @@
*
* @see org.jboss.dna.graph.request.ChangeRequest#changes(java.lang.String, org.jboss.dna.graph.property.Path)
*/
+ @Override
public boolean changes( String workspace,
Path path ) {
return this.workspaceName.equals(workspace) && under.hasPath() && under.getPath().isAtOrBelow(path);
@@ -319,6 +322,7 @@
*
* @see org.jboss.dna.graph.request.ChangeRequest#changedLocation()
*/
+ @Override
public Location changedLocation() {
return under;
}
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CreateWorkspaceRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CreateWorkspaceRequest.java 2009-05-18 15:36:58 UTC (rev 911)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CreateWorkspaceRequest.java 2009-05-19 17:40:25 UTC (rev 912)
@@ -105,8 +105,10 @@
* {@link CreateConflictBehavior#CREATE_WITH_ADJUSTED_NAME alter the name}.
*
* @param actualWorkspaceName the actual name of the workspace that was created, or null if a workspace was not created
+ * @throws IllegalStateException if the request is frozen
*/
public void setActualWorkspaceName( String actualWorkspaceName ) {
+ checkNotFrozen();
this.actualWorkspaceName = actualWorkspaceName;
}
@@ -123,8 +125,10 @@
* Set the actual location of the root node in the new workspace.
*
* @param actualLocationOfRoot the actual location of the workspace's root node.
+ * @throws IllegalStateException if the request is frozen
*/
public void setActualRootLocation( Location actualLocationOfRoot ) {
+ checkNotFrozen();
this.actualLocationOfRoot = actualLocationOfRoot;
}
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/DeleteBranchRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/DeleteBranchRequest.java 2009-05-18 15:36:58 UTC (rev 911)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/DeleteBranchRequest.java 2009-05-19 17:40:25 UTC (rev 912)
@@ -34,7 +34,7 @@
*
* @author Randall Hauch
*/
-public class DeleteBranchRequest extends Request implements ChangeRequest {
+public class DeleteBranchRequest extends ChangeRequest {
private static final long serialVersionUID = 1L;
@@ -92,8 +92,10 @@
* @param actual the actual location of the node being deleted, or null if the {@link #at() current location} should be used
* @throws IllegalArgumentException if the actual location does not represent the {@link Location#isSame(Location) same
* location} as the {@link #at() current location}, or if the actual location does not have a path.
+ * @throws IllegalStateException if the request is frozen
*/
public void setActualLocationOfNode( Location actual ) {
+ checkNotFrozen();
if (!at.isSame(actual)) { // not same if actual is null
throw new IllegalArgumentException(GraphI18n.actualLocationIsNotSameAsInputLocation.text(actual, at));
}
@@ -118,6 +120,7 @@
*
* @see org.jboss.dna.graph.request.ChangeRequest#changes(java.lang.String, org.jboss.dna.graph.property.Path)
*/
+ @Override
public boolean changes( String workspace,
Path path ) {
return this.workspaceName.equals(workspace) && at.hasPath() && at.getPath().isAtOrBelow(path);
@@ -128,6 +131,7 @@
*
* @see org.jboss.dna.graph.request.ChangeRequest#changedLocation()
*/
+ @Override
public Location changedLocation() {
return at;
}
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/DeleteChildrenRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/DeleteChildrenRequest.java 2009-05-18 15:36:58 UTC (rev 911)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/DeleteChildrenRequest.java 2009-05-19 17:40:25 UTC (rev 912)
@@ -32,7 +32,7 @@
* Instruction that all nodes below a supplied node be deleted. This is similar to {@link DeleteBranchRequest}, except that the
* parent node (top node in the branch) is not deleted.
*/
-public class DeleteChildrenRequest extends Request implements ChangeRequest {
+public class DeleteChildrenRequest extends ChangeRequest {
private static final long serialVersionUID = 1L;
@@ -90,8 +90,10 @@
* @param actual the actual location of the node being deleted, or null if the {@link #at() current location} should be used
* @throws IllegalArgumentException if the actual location does not represent the {@link Location#isSame(Location) same
* location} as the {@link #at() current location}, or if the actual location does not have a path.
+ * @throws IllegalStateException if the request is frozen
*/
public void setActualLocationOfNode( Location actual ) {
+ checkNotFrozen();
if (!at.isSame(actual)) { // not same if actual is null
throw new IllegalArgumentException(GraphI18n.actualLocationIsNotSameAsInputLocation.text(actual, at));
}
@@ -116,6 +118,7 @@
*
* @see org.jboss.dna.graph.request.ChangeRequest#changes(java.lang.String, org.jboss.dna.graph.property.Path)
*/
+ @Override
public boolean changes( String workspace,
Path path ) {
return this.workspaceName.equals(workspace) && at.hasPath() && at.getPath().isAtOrBelow(path);
@@ -126,6 +129,7 @@
*
* @see org.jboss.dna.graph.request.ChangeRequest#changedLocation()
*/
+ @Override
public Location changedLocation() {
return at;
}
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/GetWorkspacesRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/GetWorkspacesRequest.java 2009-05-18 15:36:58 UTC (rev 911)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/GetWorkspacesRequest.java 2009-05-19 17:40:25 UTC (rev 912)
@@ -63,8 +63,10 @@
* Set the names of the workspaces that are available (at least to the current user)
*
* @param availableWorkspaceNames Sets availableWorkspaceNames to the specified value.
+ * @throws IllegalStateException if the request is frozen
*/
public void setAvailableWorkspaceNames( Set<String> availableWorkspaceNames ) {
+ checkNotFrozen();
this.availableWorkspaceNames = availableWorkspaceNames;
}
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/MoveBranchRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/MoveBranchRequest.java 2009-05-18 15:36:58 UTC (rev 911)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/MoveBranchRequest.java 2009-05-19 17:40:25 UTC (rev 912)
@@ -36,7 +36,7 @@
*
* @author Randall Hauch
*/
-public class MoveBranchRequest extends Request implements ChangeRequest {
+public class MoveBranchRequest extends ChangeRequest {
private static final long serialVersionUID = 1L;
@@ -216,9 +216,11 @@
* {@link Location#isSame(Location) same location} as the {@link #from() from location}, if the new location does not
* represent the {@link Location#isSame(Location) same location} as the {@link #into() into location}, or if the
* either location does not have a path
+ * @throws IllegalStateException if the request is frozen
*/
public void setActualLocations( Location oldLocation,
Location newLocation ) {
+ checkNotFrozen();
CheckArg.isNotNull(oldLocation, "oldLocation");
CheckArg.isNotNull(newLocation, "newLocation");
if (!from.isSame(oldLocation)) { // not same if actual is null
@@ -265,6 +267,7 @@
*
* @see org.jboss.dna.graph.request.ChangeRequest#changes(java.lang.String, org.jboss.dna.graph.property.Path)
*/
+ @Override
public boolean changes( String workspace,
Path path ) {
return this.workspaceName.equals(workspace)
@@ -276,6 +279,7 @@
*
* @see org.jboss.dna.graph.request.ChangeRequest#changedLocation()
*/
+ @Override
public Location changedLocation() {
return into;
}
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadAllChildrenRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadAllChildrenRequest.java 2009-05-18 15:36:58 UTC (rev 911)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadAllChildrenRequest.java 2009-05-19 17:40:25 UTC (rev 912)
@@ -116,11 +116,13 @@
*
* @param children the locations of the children that were read
* @throws IllegalArgumentException if the parameter is null
+ * @throws IllegalStateException if the request is frozen
* @see #addChild(Location)
* @see #addChild(Path, Property)
* @see #addChild(Path, Property, Property...)
*/
public void addChildren( Iterable<Location> children ) {
+ checkNotFrozen();
CheckArg.isNotNull(children, "children");
for (Location child : children) {
if (child != null) this.children.add(child);
@@ -133,10 +135,12 @@
*
* @param child the location of the child that was read
* @throws IllegalArgumentException if the location is null
+ * @throws IllegalStateException if the request is frozen
* @see #addChild(Path, Property)
* @see #addChild(Path, Property, Property...)
*/
public void addChild( Location child ) {
+ checkNotFrozen();
CheckArg.isNotNull(child, "child");
this.children.add(child);
}
@@ -149,12 +153,14 @@
* @param firstIdProperty the first identification property of the child that was just read
* @param remainingIdProperties the remaining identification properties of the child that was just read
* @throws IllegalArgumentException if the path or identification properties are null
+ * @throws IllegalStateException if the request is frozen
* @see #addChild(Location)
* @see #addChild(Path, Property)
*/
public void addChild( Path pathToChild,
Property firstIdProperty,
Property... remainingIdProperties ) {
+ checkNotFrozen();
Location child = Location.create(pathToChild, firstIdProperty, remainingIdProperties);
this.children.add(child);
}
@@ -166,11 +172,13 @@
* @param pathToChild the path of the child that was just read
* @param idProperty the identification property of the child that was just read
* @throws IllegalArgumentException if the path or identification properties are null
+ * @throws IllegalStateException if the request is frozen
* @see #addChild(Location)
* @see #addChild(Path, Property, Property...)
*/
public void addChild( Path pathToChild,
Property idProperty ) {
+ checkNotFrozen();
Location child = Location.create(pathToChild, idProperty);
this.children.add(child);
}
@@ -184,8 +192,10 @@
* @throws IllegalArgumentException if the actual location does not represent the {@link Location#isSame(Location) same
* location} as the {@link #of() current location}; if the actual location does not have a path; or if the actual
* workspace name is null
+ * @throws IllegalStateException if the request is frozen
*/
public void setActualLocationOfNode( Location actualLocation ) {
+ checkNotFrozen();
if (!this.of.isSame(actualLocation)) { // not same if actualLocation is null
throw new IllegalArgumentException(GraphI18n.actualLocationIsNotSameAsInputLocation.text(actualLocation, of));
}
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadAllPropertiesRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadAllPropertiesRequest.java 2009-05-18 15:36:58 UTC (rev 911)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadAllPropertiesRequest.java 2009-05-19 17:40:25 UTC (rev 912)
@@ -129,8 +129,10 @@
* @return the previous property that had the same name, or null if there was no previously-recorded property with the same
* name
* @throws IllegalArgumentException if the property is null
+ * @throws IllegalStateException if the request is frozen
*/
public Property addProperty( Property property ) {
+ checkNotFrozen();
return this.properties.put(property.getName(), property);
}
@@ -139,8 +141,10 @@
*
* @param properties the properties that were read
* @throws IllegalArgumentException if the property is null
+ * @throws IllegalStateException if the request is frozen
*/
public void addProperties( Property... properties ) {
+ checkNotFrozen();
for (Property property : properties) {
this.properties.put(property.getName(), property);
}
@@ -151,8 +155,10 @@
*
* @param properties the properties that were read
* @throws IllegalArgumentException if the property is null
+ * @throws IllegalStateException if the request is frozen
*/
public void addProperties( Iterable<Property> properties ) {
+ checkNotFrozen();
for (Property property : properties) {
this.properties.put(property.getName(), property);
}
@@ -172,8 +178,10 @@
*
* @param numberOfChildren the number of children
* @throws IllegalArgumentException if the number of childre is negative
+ * @throws IllegalStateException if the request is frozen
*/
public void setNumberOfChildren( int numberOfChildren ) {
+ checkNotFrozen();
CheckArg.isNonNegative(numberOfChildren, "numberOfChildren");
this.numberOfChildren = numberOfChildren;
}
@@ -185,8 +193,10 @@
* @param actual the actual location of the node being read, or null if the {@link #at() current location} should be used
* @throws IllegalArgumentException if the actual location does not represent the {@link Location#isSame(Location) same
* location} as the {@link #at() current location}, or if the actual location does not have a path.
+ * @throws IllegalStateException if the request is frozen
*/
public void setActualLocationOfNode( Location actual ) {
+ checkNotFrozen();
if (!at.isSame(actual)) { // not same if actual is null
throw new IllegalArgumentException(GraphI18n.actualLocationIsNotSameAsInputLocation.text(actual, at));
}
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadBlockOfChildrenRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadBlockOfChildrenRequest.java 2009-05-18 15:36:58 UTC (rev 911)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadBlockOfChildrenRequest.java 2009-05-19 17:40:25 UTC (rev 912)
@@ -163,11 +163,13 @@
*
* @param children the locations of the children that were read
* @throws IllegalArgumentException if the parameter is null
+ * @throws IllegalStateException if the request is frozen
* @see #addChild(Location)
* @see #addChild(Path, Property)
* @see #addChild(Path, Property, Property...)
*/
public void addChildren( Iterable<Location> children ) {
+ checkNotFrozen();
CheckArg.isNotNull(children, "children");
for (Location child : children) {
if (child != null) this.children.add(child);
@@ -180,10 +182,12 @@
*
* @param child the location of the child that was read
* @throws IllegalArgumentException if the location is null
+ * @throws IllegalStateException if the request is frozen
* @see #addChild(Path, Property)
* @see #addChild(Path, Property, Property...)
*/
public void addChild( Location child ) {
+ checkNotFrozen();
CheckArg.isNotNull(child, "child");
this.children.add(child);
}
@@ -196,12 +200,14 @@
* @param firstIdProperty the first identification property of the child that was just read
* @param remainingIdProperties the remaining identification properties of the child that was just read
* @throws IllegalArgumentException if the path or identification properties are null
+ * @throws IllegalStateException if the request is frozen
* @see #addChild(Location)
* @see #addChild(Path, Property)
*/
public void addChild( Path pathToChild,
Property firstIdProperty,
Property... remainingIdProperties ) {
+ checkNotFrozen();
Location child = Location.create(pathToChild, firstIdProperty, remainingIdProperties);
this.children.add(child);
}
@@ -213,11 +219,13 @@
* @param pathToChild the path of the child that was just read
* @param idProperty the identification property of the child that was just read
* @throws IllegalArgumentException if the path or identification properties are null
+ * @throws IllegalStateException if the request is frozen
* @see #addChild(Location)
* @see #addChild(Path, Property, Property...)
*/
public void addChild( Path pathToChild,
Property idProperty ) {
+ checkNotFrozen();
Location child = Location.create(pathToChild, idProperty);
this.children.add(child);
}
@@ -229,8 +237,10 @@
* @param actual the actual location of the node being read, or null if the {@link #of() current location} should be used
* @throws IllegalArgumentException if the actual location does not represent the {@link Location#isSame(Location) same
* location} as the {@link #of() current location}, or if the actual location does not have a path.
+ * @throws IllegalStateException if the request is frozen
*/
public void setActualLocationOfNode( Location actual ) {
+ checkNotFrozen();
if (!of.isSame(actual)) { // not same if actual is null
throw new IllegalArgumentException(GraphI18n.actualLocationIsNotSameAsInputLocation.text(actual, of));
}
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadBranchRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadBranchRequest.java 2009-05-18 15:36:58 UTC (rev 911)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadBranchRequest.java 2009-05-19 17:40:25 UTC (rev 912)
@@ -199,9 +199,11 @@
* @param node the location of the node that appears on this branch; must {@link Location#hasPath() have a path}
* @param properties the properties on the node
* @throws IllegalArgumentException if the node is null
+ * @throws IllegalStateException if the request is frozen
*/
public void setProperties( Location node,
Property... properties ) {
+ checkNotFrozen();
CheckArg.isNotNull(node, "node");
assert node.hasPath();
Node nodeObj = nodes.get(node.getPath());
@@ -222,9 +224,11 @@
* @param node the location of the node that appears on this branch; must {@link Location#hasPath() have a path}
* @param properties the properties on the node
* @throws IllegalArgumentException if the node is null
+ * @throws IllegalStateException if the request is frozen
*/
public void setProperties( Location node,
Iterable<Property> properties ) {
+ checkNotFrozen();
CheckArg.isNotNull(node, "node");
assert node.hasPath();
Node nodeObj = nodes.get(node.getPath());
@@ -243,9 +247,11 @@
*
* @param parent the location of the parent; must {@link Location#hasPath() have a path}
* @param children the location of each child, in the order they appear in the parent
+ * @throws IllegalStateException if the request is frozen
*/
public void setChildren( Location parent,
Location... children ) {
+ checkNotFrozen();
CheckArg.isNotNull(parent, "parent");
CheckArg.isNotNull(children, "children");
assert parent.hasPath();
@@ -262,9 +268,11 @@
*
* @param parent the location of the parent; must {@link Location#hasPath() have a path}
* @param children the location of each child, in the order they appear in the parent
+ * @throws IllegalStateException if the request is frozen
*/
public void setChildren( Location parent,
List<Location> children ) {
+ checkNotFrozen();
CheckArg.isNotNull(parent, "parent");
CheckArg.isNotNull(children, "children");
assert parent.hasPath();
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadNextBlockOfChildrenRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadNextBlockOfChildrenRequest.java 2009-05-18 15:36:58 UTC (rev 911)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadNextBlockOfChildrenRequest.java 2009-05-19 17:40:25 UTC (rev 912)
@@ -131,11 +131,13 @@
*
* @param children the locations of the children that were read
* @throws IllegalArgumentException if the parameter is null
+ * @throws IllegalStateException if the request is frozen
* @see #addChild(Location)
* @see #addChild(Path, Property)
* @see #addChild(Path, Property, Property...)
*/
public void addChildren( Iterable<Location> children ) {
+ checkNotFrozen();
CheckArg.isNotNull(children, "children");
for (Location child : children) {
if (child != null) this.children.add(child);
@@ -148,10 +150,12 @@
*
* @param child the location of the child that was read
* @throws IllegalArgumentException if the location is null
+ * @throws IllegalStateException if the request is frozen
* @see #addChild(Path, Property)
* @see #addChild(Path, Property, Property...)
*/
public void addChild( Location child ) {
+ checkNotFrozen();
CheckArg.isNotNull(child, "child");
this.children.add(child);
}
@@ -164,12 +168,14 @@
* @param firstIdProperty the first identification property of the child that was just read
* @param remainingIdProperties the remaining identification properties of the child that was just read
* @throws IllegalArgumentException if the path or identification properties are null
+ * @throws IllegalStateException if the request is frozen
* @see #addChild(Location)
* @see #addChild(Path, Property)
*/
public void addChild( Path pathToChild,
Property firstIdProperty,
Property... remainingIdProperties ) {
+ checkNotFrozen();
Location child = Location.create(pathToChild, firstIdProperty, remainingIdProperties);
this.children.add(child);
}
@@ -181,11 +187,13 @@
* @param pathToChild the path of the child that was just read
* @param idProperty the identification property of the child that was just read
* @throws IllegalArgumentException if the path or identification properties are null
+ * @throws IllegalStateException if the request is frozen
* @see #addChild(Location)
* @see #addChild(Path, Property, Property...)
*/
public void addChild( Path pathToChild,
Property idProperty ) {
+ checkNotFrozen();
Location child = Location.create(pathToChild, idProperty);
this.children.add(child);
}
@@ -198,8 +206,10 @@
* should be used
* @throws IllegalArgumentException if the actual location does not represent the {@link Location#isSame(Location) same
* location} as the {@link #startingAfter() starting after location}, or if the actual location does not have a path.
+ * @throws IllegalStateException if the request is frozen
*/
public void setActualLocationOfStartingAfterNode( Location actual ) {
+ checkNotFrozen();
if (!startingAfter.isSame(actual)) { // not same if actual is null
throw new IllegalArgumentException(GraphI18n.actualLocationIsNotSameAsInputLocation.text(actual, startingAfter));
}
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadNodeRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadNodeRequest.java 2009-05-18 15:36:58 UTC (rev 911)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadNodeRequest.java 2009-05-19 17:40:25 UTC (rev 912)
@@ -121,8 +121,10 @@
* @return the previous property that had the same name, or null if there was no previously-recorded property with the same
* name
* @throws IllegalArgumentException if the property is null
+ * @throws IllegalStateException if the request is frozen
*/
public Property addProperty( Property property ) {
+ checkNotFrozen();
return this.properties.put(property.getName(), property);
}
@@ -130,9 +132,12 @@
* Add a property that was read from the {@link RepositoryConnection}
*
* @param properties the properties that were read
- * @throws IllegalArgumentException if the property is null
+ * @throws IllegalArgumentException if the properties array is null
+ * @throws IllegalStateException if the request is frozen
*/
public void addProperties( Property... properties ) {
+ checkNotFrozen();
+ CheckArg.isNotNull(properties, "properties");
for (Property property : properties) {
this.properties.put(property.getName(), property);
}
@@ -142,9 +147,12 @@
* Add a property that was read from the {@link RepositoryConnection}
*
* @param properties the properties that were read
- * @throws IllegalArgumentException if the property is null
+ * @throws IllegalArgumentException if the iterable reference is null
+ * @throws IllegalStateException if the request is frozen
*/
public void addProperties( Iterable<Property> properties ) {
+ checkNotFrozen();
+ CheckArg.isNotNull(properties, "properties");
for (Property property : properties) {
this.properties.put(property.getName(), property);
}
@@ -175,11 +183,13 @@
*
* @param children the locations of the children that were read
* @throws IllegalArgumentException if the parameter is null
+ * @throws IllegalStateException if the request is frozen
* @see #addChild(Location)
* @see #addChild(Path, Property)
* @see #addChild(Path, Property, Property...)
*/
public void addChildren( Iterable<Location> children ) {
+ checkNotFrozen();
CheckArg.isNotNull(children, "children");
for (Location child : children) {
if (child != null) this.children.add(child);
@@ -192,10 +202,12 @@
*
* @param child the location of the child that was read
* @throws IllegalArgumentException if the location is null
+ * @throws IllegalStateException if the request is frozen
* @see #addChild(Path, Property)
* @see #addChild(Path, Property, Property...)
*/
public void addChild( Location child ) {
+ checkNotFrozen();
CheckArg.isNotNull(child, "child");
this.children.add(child);
}
@@ -208,12 +220,14 @@
* @param firstIdProperty the first identification property of the child that was just read
* @param remainingIdProperties the remaining identification properties of the child that was just read
* @throws IllegalArgumentException if the path or identification properties are null
+ * @throws IllegalStateException if the request is frozen
* @see #addChild(Location)
* @see #addChild(Path, Property)
*/
public void addChild( Path pathToChild,
Property firstIdProperty,
Property... remainingIdProperties ) {
+ checkNotFrozen();
Location child = Location.create(pathToChild, firstIdProperty, remainingIdProperties);
this.children.add(child);
}
@@ -225,11 +239,13 @@
* @param pathToChild the path of the child that was just read
* @param idProperty the identification property of the child that was just read
* @throws IllegalArgumentException if the path or identification properties are null
+ * @throws IllegalStateException if the request is frozen
* @see #addChild(Location)
* @see #addChild(Path, Property, Property...)
*/
public void addChild( Path pathToChild,
Property idProperty ) {
+ checkNotFrozen();
Location child = Location.create(pathToChild, idProperty);
this.children.add(child);
}
@@ -241,8 +257,10 @@
* @param actual the actual location of the node being read, or null if the {@link #at() current location} should be used
* @throws IllegalArgumentException if the actual location does not represent the {@link Location#isSame(Location) same
* location} as the {@link #at() current location}, or if the actual location does not have a path.
+ * @throws IllegalStateException if the request is frozen
*/
public void setActualLocationOfNode( Location actual ) {
+ checkNotFrozen();
if (!at.isSame(actual)) { // not same if actual is null
throw new IllegalArgumentException(GraphI18n.actualLocationIsNotSameAsInputLocation.text(actual, at));
}
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadPropertyRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadPropertyRequest.java 2009-05-18 15:36:58 UTC (rev 911)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadPropertyRequest.java 2009-05-19 17:40:25 UTC (rev 912)
@@ -117,8 +117,10 @@
* @param property the property that was read
* @throws IllegalArgumentException if the property's name does not match the {@link #named() name of the property} that was
* to be read
+ * @throws IllegalStateException if the request is frozen
*/
public void setProperty( Property property ) {
+ checkNotFrozen();
if (property != null) CheckArg.isEquals(property.getName(), "property's name", named(), "property name");
this.property = property;
}
@@ -130,8 +132,10 @@
* @param actual the actual location of the node being read, or null if the {@link #on() current location} should be used
* @throws IllegalArgumentException if the actual location does not represent the {@link Location#isSame(Location) same
* location} as the {@link #on() current location}, or if the actual location does not have a path.
+ * @throws IllegalStateException if the request is frozen
*/
public void setActualLocationOfNode( Location actual ) {
+ checkNotFrozen();
if (!on.isSame(actual)) { // not same if actual is null
throw new IllegalArgumentException(GraphI18n.actualLocationIsNotSameAsInputLocation.text(actual, on));
}
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/RemovePropertyRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/RemovePropertyRequest.java 2009-05-18 15:36:58 UTC (rev 911)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/RemovePropertyRequest.java 2009-05-19 17:40:25 UTC (rev 912)
@@ -36,7 +36,7 @@
*
* @author Randall Hauch
*/
-public class RemovePropertyRequest extends Request implements ChangeRequest {
+public class RemovePropertyRequest extends ChangeRequest {
private static final long serialVersionUID = 1L;
@@ -108,8 +108,10 @@
* @param actual the actual location of the node being updated, or null if the {@link #from() current location} should be used
* @throws IllegalArgumentException if the actual location does represent the {@link Location#isSame(Location) same location}
* as the {@link #from() current location}, or if the actual location does not have a path.
+ * @throws IllegalStateException if the request is frozen
*/
public void setActualLocationOfNode( Location actual ) {
+ checkNotFrozen();
if (!from.isSame(actual)) { // not same if actual is null
throw new IllegalArgumentException(GraphI18n.actualLocationIsNotSameAsInputLocation.text(actual, from));
}
@@ -134,6 +136,7 @@
*
* @see org.jboss.dna.graph.request.ChangeRequest#changes(java.lang.String, org.jboss.dna.graph.property.Path)
*/
+ @Override
public boolean changes( String workspace,
Path path ) {
return this.workspaceName.equals(workspace) && from.hasPath() && from.getPath().isAtOrBelow(path);
@@ -183,6 +186,7 @@
*
* @see org.jboss.dna.graph.request.ChangeRequest#changedLocation()
*/
+ @Override
public Location changedLocation() {
return from;
}
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/RenameNodeRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/RenameNodeRequest.java 2009-05-18 15:36:58 UTC (rev 911)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/RenameNodeRequest.java 2009-05-19 17:40:25 UTC (rev 912)
@@ -36,7 +36,7 @@
*
* @author Randall Hauch
*/
-public class RenameNodeRequest extends Request implements ChangeRequest {
+public class RenameNodeRequest extends ChangeRequest {
private static final long serialVersionUID = 1L;
@@ -113,9 +113,11 @@
* location does not have the same parent as the old location, or if the new location does not have the same
* {@link Path.Segment#getName() name} on {@link Path#getLastSegment() last segment} as that {@link #toName()
* specified on the request}
+ * @throws IllegalStateException if the request is frozen
*/
public void setActualLocations( Location oldLocation,
Location newLocation ) {
+ checkNotFrozen();
if (!at.isSame(oldLocation)) { // not same if actual is null
throw new IllegalArgumentException(GraphI18n.actualLocationIsNotSameAsInputLocation.text(oldLocation, at));
}
@@ -164,6 +166,7 @@
*
* @see org.jboss.dna.graph.request.ChangeRequest#changes(java.lang.String, org.jboss.dna.graph.property.Path)
*/
+ @Override
public boolean changes( String workspace,
Path path ) {
return this.workspaceName.equals(workspace) && at.hasPath() && at.getPath().getParent().isAtOrBelow(path);
@@ -174,6 +177,7 @@
*
* @see org.jboss.dna.graph.request.ChangeRequest#changedLocation()
*/
+ @Override
public Location changedLocation() {
return at;
}
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/Request.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/Request.java 2009-05-18 15:36:58 UTC (rev 911)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/Request.java 2009-05-19 17:40:25 UTC (rev 912)
@@ -25,6 +25,7 @@
import java.io.Serializable;
import java.util.concurrent.atomic.AtomicBoolean;
+import org.jboss.dna.graph.GraphI18n;
import org.jboss.dna.graph.connector.RepositoryConnection;
/**
@@ -37,18 +38,20 @@
private static final long serialVersionUID = 1L;
private Throwable error;
- private AtomicBoolean cancelled;
+ private AtomicBoolean cancelled = new AtomicBoolean(false);
+ private final AtomicBoolean frozen = new AtomicBoolean(false);
protected Request() {
- this.cancelled = new AtomicBoolean(false);
}
/**
* Set the error for this request.
*
* @param error the error to be associated with this request, or null if this request is to have no error
+ * @throws IllegalStateException if the request is frozen
*/
public void setError( Throwable error ) {
+ checkNotFrozen();
this.error = error;
}
@@ -117,8 +120,11 @@
* <p>
* This method is safe to be called by different threads.
* </p>
+ *
+ * @throws IllegalStateException if the request is frozen
*/
public void cancel() {
+ checkNotFrozen();
this.cancelled.set(true);
}
@@ -128,4 +134,31 @@
* @return true if this request reads information, or false if it requests that the repository content be changed in some way
*/
public abstract boolean isReadOnly();
+
+ /**
+ * Determine whether this request has been frozen, preventing any further updates.
+ *
+ * @return true if the request has been frozen, or false otherwise
+ */
+ public boolean isFrozen() {
+ return frozen.get();
+ }
+
+ /**
+ * Freeze this request to prevent any further modification. This method does nothing if the request is already frozen.
+ */
+ public void freeze() {
+ frozen.set(true);
+ }
+
+ /**
+ * Utility method to check that the request is not frozen, and if it is to throw an {@link IllegalStateException}.
+ *
+ * @throws IllegalStateException if the request is frozen
+ */
+ protected void checkNotFrozen() throws IllegalStateException {
+ if (frozen.get()) {
+ throw new IllegalStateException(GraphI18n.requestIsFrozenAndMayNotBeChanged.text(this));
+ }
+ }
}
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/SetPropertyRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/SetPropertyRequest.java 2009-05-18 15:36:58 UTC (rev 911)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/SetPropertyRequest.java 2009-05-19 17:40:25 UTC (rev 912)
@@ -36,7 +36,7 @@
*
* @author Randall Hauch
*/
-public class SetPropertyRequest extends Request implements ChangeRequest {
+public class SetPropertyRequest extends ChangeRequest {
private static final long serialVersionUID = 1L;
@@ -108,8 +108,10 @@
* @param actual the actual location of the node being updated, or null if the {@link #on() current location} should be used
* @throws IllegalArgumentException if the actual location does represent the {@link Location#isSame(Location) same location}
* as the {@link #on() current location}, or if the actual location does not have a path.
+ * @throws IllegalStateException if the request is frozen
*/
public void setActualLocationOfNode( Location actual ) {
+ checkNotFrozen();
if (!on.isSame(actual)) { // not same if actual is null
throw new IllegalArgumentException(GraphI18n.actualLocationIsNotSameAsInputLocation.text(actual, on));
}
@@ -134,6 +136,7 @@
*
* @see org.jboss.dna.graph.request.ChangeRequest#changes(java.lang.String, org.jboss.dna.graph.property.Path)
*/
+ @Override
public boolean changes( String workspace,
Path path ) {
return this.workspaceName.equals(workspace) && on.hasPath() && on.getPath().isAtOrBelow(path);
@@ -183,6 +186,7 @@
*
* @see org.jboss.dna.graph.request.ChangeRequest#changedLocation()
*/
+ @Override
public Location changedLocation() {
return on;
}
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/UpdatePropertiesRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/UpdatePropertiesRequest.java 2009-05-18 15:36:58 UTC (rev 911)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/UpdatePropertiesRequest.java 2009-05-19 17:40:25 UTC (rev 912)
@@ -53,7 +53,7 @@
*
* @author Randall Hauch
*/
-public class UpdatePropertiesRequest extends Request implements ChangeRequest {
+public class UpdatePropertiesRequest extends ChangeRequest {
private static final long serialVersionUID = 1L;
@@ -126,8 +126,10 @@
* @param actual the actual location of the node being updated, or null if the {@link #on() current location} should be used
* @throws IllegalArgumentException if the actual location does represent the {@link Location#isSame(Location) same location}
* as the {@link #on() current location}, or if the actual location does not have a path.
+ * @throws IllegalStateException if the request is frozen
*/
public void setActualLocationOfNode( Location actual ) {
+ checkNotFrozen();
if (!on.isSame(actual)) { // not same if actual is null
throw new IllegalArgumentException(GraphI18n.actualLocationIsNotSameAsInputLocation.text(actual, on));
}
@@ -152,6 +154,7 @@
*
* @see org.jboss.dna.graph.request.ChangeRequest#changes(java.lang.String, org.jboss.dna.graph.property.Path)
*/
+ @Override
public boolean changes( String workspace,
Path path ) {
return this.workspaceName.equals(workspace) && on.hasPath() && on.getPath().isAtOrBelow(path);
@@ -201,6 +204,7 @@
*
* @see org.jboss.dna.graph.request.ChangeRequest#changedLocation()
*/
+ @Override
public Location changedLocation() {
return on;
}
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/VerifyNodeExistsRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/VerifyNodeExistsRequest.java 2009-05-18 15:36:58 UTC (rev 911)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/VerifyNodeExistsRequest.java 2009-05-19 17:40:25 UTC (rev 912)
@@ -91,8 +91,10 @@
* @param actual the actual location of the node being read, or null if the {@link #at() current location} should be used
* @throws IllegalArgumentException if the actual location does not represent the {@link Location#isSame(Location) same
* location} as the {@link #at() current location}, or if the actual location does not have a path.
+ * @throws IllegalStateException if the request is frozen
*/
public void setActualLocationOfNode( Location actual ) {
+ checkNotFrozen();
if (!at.isSame(actual)) { // not same if actual is null
throw new IllegalArgumentException(GraphI18n.actualLocationIsNotSameAsInputLocation.text(actual, at));
}
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/VerifyWorkspaceRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/VerifyWorkspaceRequest.java 2009-05-18 15:36:58 UTC (rev 911)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/VerifyWorkspaceRequest.java 2009-05-19 17:40:25 UTC (rev 912)
@@ -69,8 +69,10 @@
* Set the actual name of the workspace.
*
* @param actualWorkspaceName the actual name of the workspace; never null
+ * @throws IllegalStateException if the request is frozen
*/
public void setActualWorkspaceName( String actualWorkspaceName ) {
+ checkNotFrozen();
CheckArg.isNotNull(actualWorkspaceName, "actualWorkspaceName");
this.actualWorkspaceName = actualWorkspaceName;
}
@@ -88,8 +90,10 @@
* Set the actual location of the root node in the new workspace.
*
* @param actualLocationOfRoot the actual location of the workspace's root node.
+ * @throws IllegalStateException if the request is frozen
*/
public void setActualRootLocation( Location actualLocationOfRoot ) {
+ checkNotFrozen();
this.actualLocationOfRoot = actualLocationOfRoot;
}
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/processor/RequestProcessor.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/processor/RequestProcessor.java 2009-05-18 15:36:58 UTC (rev 911)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/processor/RequestProcessor.java 2009-05-19 17:40:25 UTC (rev 912)
@@ -187,58 +187,66 @@
*/
public void process( Request request ) {
if (request == null) return;
- if (request.isCancelled()) return;
- if (request instanceof CompositeRequest) {
- process((CompositeRequest)request);
- } else if (request instanceof CopyBranchRequest) {
- process((CopyBranchRequest)request);
- } else if (request instanceof CreateNodeRequest) {
- process((CreateNodeRequest)request);
- } else if (request instanceof DeleteBranchRequest) {
- process((DeleteBranchRequest)request);
- } else if (request instanceof DeleteChildrenRequest) {
- process((DeleteChildrenRequest)request);
- } else if (request instanceof MoveBranchRequest) {
- process((MoveBranchRequest)request);
- } else if (request instanceof ReadAllChildrenRequest) {
- process((ReadAllChildrenRequest)request);
- } else if (request instanceof ReadNextBlockOfChildrenRequest) {
- process((ReadNextBlockOfChildrenRequest)request);
- } else if (request instanceof ReadBlockOfChildrenRequest) {
- process((ReadBlockOfChildrenRequest)request);
- } else if (request instanceof ReadBranchRequest) {
- process((ReadBranchRequest)request);
- } else if (request instanceof ReadNodeRequest) {
- process((ReadNodeRequest)request);
- } else if (request instanceof ReadAllPropertiesRequest) {
- process((ReadAllPropertiesRequest)request);
- } else if (request instanceof ReadPropertyRequest) {
- process((ReadPropertyRequest)request);
- } else if (request instanceof RemovePropertyRequest) {
- process((RemovePropertyRequest)request);
- } else if (request instanceof SetPropertyRequest) {
- process((SetPropertyRequest)request);
- } else if (request instanceof RenameNodeRequest) {
- process((RenameNodeRequest)request);
- } else if (request instanceof UpdatePropertiesRequest) {
- process((UpdatePropertiesRequest)request);
- } else if (request instanceof VerifyNodeExistsRequest) {
- process((VerifyNodeExistsRequest)request);
- } else if (request instanceof VerifyWorkspaceRequest) {
- process((VerifyWorkspaceRequest)request);
- } else if (request instanceof GetWorkspacesRequest) {
- process((GetWorkspacesRequest)request);
- } else if (request instanceof CreateWorkspaceRequest) {
- process((CreateWorkspaceRequest)request);
- } else if (request instanceof CloneWorkspaceRequest) {
- process((CloneWorkspaceRequest)request);
- } else if (request instanceof DestroyWorkspaceRequest) {
- process((DestroyWorkspaceRequest)request);
- } else {
- processUnknownRequest(request);
+ try {
+ if (request.isCancelled()) return;
+ if (request instanceof CompositeRequest) {
+ process((CompositeRequest)request);
+ } else if (request instanceof CopyBranchRequest) {
+ process((CopyBranchRequest)request);
+ } else if (request instanceof CreateNodeRequest) {
+ process((CreateNodeRequest)request);
+ } else if (request instanceof DeleteBranchRequest) {
+ process((DeleteBranchRequest)request);
+ } else if (request instanceof DeleteChildrenRequest) {
+ process((DeleteChildrenRequest)request);
+ } else if (request instanceof MoveBranchRequest) {
+ process((MoveBranchRequest)request);
+ } else if (request instanceof ReadAllChildrenRequest) {
+ process((ReadAllChildrenRequest)request);
+ } else if (request instanceof ReadNextBlockOfChildrenRequest) {
+ process((ReadNextBlockOfChildrenRequest)request);
+ } else if (request instanceof ReadBlockOfChildrenRequest) {
+ process((ReadBlockOfChildrenRequest)request);
+ } else if (request instanceof ReadBranchRequest) {
+ process((ReadBranchRequest)request);
+ } else if (request instanceof ReadNodeRequest) {
+ process((ReadNodeRequest)request);
+ } else if (request instanceof ReadAllPropertiesRequest) {
+ process((ReadAllPropertiesRequest)request);
+ } else if (request instanceof ReadPropertyRequest) {
+ process((ReadPropertyRequest)request);
+ } else if (request instanceof RemovePropertyRequest) {
+ process((RemovePropertyRequest)request);
+ } else if (request instanceof SetPropertyRequest) {
+ process((SetPropertyRequest)request);
+ } else if (request instanceof RenameNodeRequest) {
+ process((RenameNodeRequest)request);
+ } else if (request instanceof UpdatePropertiesRequest) {
+ process((UpdatePropertiesRequest)request);
+ } else if (request instanceof VerifyNodeExistsRequest) {
+ process((VerifyNodeExistsRequest)request);
+ } else if (request instanceof VerifyWorkspaceRequest) {
+ process((VerifyWorkspaceRequest)request);
+ } else if (request instanceof GetWorkspacesRequest) {
+ process((GetWorkspacesRequest)request);
+ } else if (request instanceof CreateWorkspaceRequest) {
+ process((CreateWorkspaceRequest)request);
+ } else if (request instanceof CloneWorkspaceRequest) {
+ process((CloneWorkspaceRequest)request);
+ } else if (request instanceof DestroyWorkspaceRequest) {
+ process((DestroyWorkspaceRequest)request);
+ } else {
+ processUnknownRequest(request);
+ }
+ } finally {
+ completeRequest(request);
}
}
+ protected void completeRequest( Request request ) {
+ request.freeze();
+ }
+
/**
* Process a request that is composed of multiple other (non-composite) requests. If any of the embedded requests
* {@link Request#hasError() has an error} after it is processed, the submitted request will be marked with an error.
Modified: trunk/dna-graph/src/main/resources/org/jboss/dna/graph/GraphI18n.properties
===================================================================
--- trunk/dna-graph/src/main/resources/org/jboss/dna/graph/GraphI18n.properties 2009-05-18 15:36:58 UTC (rev 911)
+++ trunk/dna-graph/src/main/resources/org/jboss/dna/graph/GraphI18n.properties 2009-05-19 17:40:25 UTC (rev 912)
@@ -71,6 +71,7 @@
actualOldLocationMustHavePath = The actual old location of {0} must have a path
actualNewLocationMustHaveSameParentAsOldLocation = The new location of {0} must be a sibling of the old location of {1}
actualNewLocationMustHaveSameNameAsRequest = The new location of {0} must have the same name as in the request ({1})
+requestIsFrozenAndMayNotBeChanged = Request is frozen and may not be changed: {0}
errorImportingContent = Error importing {0} content from {1}
unableToFindRepositorySourceWithName = Unable to find a repository source named "{0}"
Modified: trunk/extensions/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/FederatingRequestProcessor.java
===================================================================
--- trunk/extensions/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/FederatingRequestProcessor.java 2009-05-18 15:36:58 UTC (rev 911)
+++ trunk/extensions/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/FederatingRequestProcessor.java 2009-05-19 17:40:25 UTC (rev 912)
@@ -324,8 +324,8 @@
}
// Delete in the cache ...
- DeleteBranchRequest cacheRequest = new DeleteBranchRequest(request.at(),
- workspace.getCacheProjection().getWorkspaceName());
+ DeleteBranchRequest cacheRequest = new DeleteBranchRequest(request.at(), workspace.getCacheProjection()
+ .getWorkspaceName());
executeInCache(cacheRequest, workspace);
}
@@ -379,8 +379,8 @@
}
// Delete from the cache the parent of the new location ...
- DeleteBranchRequest cacheRequest = new DeleteBranchRequest(request.into(),
- fromWorkspace.getCacheProjection().getWorkspaceName());
+ DeleteBranchRequest cacheRequest = new DeleteBranchRequest(request.into(), fromWorkspace.getCacheProjection()
+ .getWorkspaceName());
executeInCache(cacheRequest, fromWorkspace);
}
@@ -425,8 +425,8 @@
intoProjection.convertToRepository(sourceRequest.getActualLocationAfter()));
}
// Delete from the cache ...
- DeleteBranchRequest cacheRequest = new DeleteBranchRequest(request.from(),
- workspace.getCacheProjection().getWorkspaceName());
+ DeleteBranchRequest cacheRequest = new DeleteBranchRequest(request.from(), workspace.getCacheProjection()
+ .getWorkspaceName());
executeInCache(cacheRequest, workspace);
// Mark the new parent node as being expired ...
cacheRequest = new DeleteBranchRequest(request.into(), workspace.getCacheProjection().getWorkspaceName());
@@ -461,8 +461,8 @@
}
// Update the cache ...
- UpdatePropertiesRequest cacheRequest = new UpdatePropertiesRequest(request.on(),
- workspace.getCacheProjection().getWorkspaceName(),
+ UpdatePropertiesRequest cacheRequest = new UpdatePropertiesRequest(request.on(), workspace.getCacheProjection()
+ .getWorkspaceName(),
request.properties());
executeInCache(cacheRequest, workspace);
}
@@ -647,7 +647,10 @@
// that already exists in the cache.
PathNotFoundException notFound = (PathNotFoundException)fromCache.getError();
Path lowestExistingAncestor = notFound.getLowestAncestorThatDoesExist();
+
if (location.hasPath()) {
+ // Create a new instance so that we can update it ...
+ fromCache = new ReadNodeRequest(location, workspace.getCacheProjection().getWorkspaceName());
Path path = location.getPath();
Path ancestor = path.getParent();
if (!ancestor.equals(lowestExistingAncestor)) {
@@ -1144,9 +1147,9 @@
readable(registry, create.properties()));
} else if (request instanceof UpdatePropertiesRequest) {
UpdatePropertiesRequest update = (UpdatePropertiesRequest)request;
- logger.trace(" updating {0} with properties {1}",
- update.on().getString(registry),
- readable(registry, update.properties().values()));
+ logger.trace(" updating {0} with properties {1}", update.on().getString(registry), readable(registry,
+ update.properties()
+ .values()));
} else {
logger.trace(" " + request.toString());
}
@@ -1165,9 +1168,9 @@
readable(registry, create.properties()));
} else if (request instanceof UpdatePropertiesRequest) {
UpdatePropertiesRequest update = (UpdatePropertiesRequest)request;
- logger.trace(" updating {0} with properties {1}",
- update.on().getString(registry),
- readable(registry, update.properties().values()));
+ logger.trace(" updating {0} with properties {1}", update.on().getString(registry), readable(registry,
+ update.properties()
+ .values()));
} else {
logger.trace(" " + request.toString());
}
16 years, 11 months
DNA SVN: r911 - in trunk: dna-graph/src/main/java/org/jboss/dna/graph and 10 other directories.
by dna-commits@lists.jboss.org
Author: rhauch
Date: 2009-05-18 11:36:58 -0400 (Mon, 18 May 2009)
New Revision: 911
Added:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/BranchedMirrorProjector.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/FederatedLexicon.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/FederatedRepository.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/FederatedRepositoryConnection.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/FederatedRepositorySource.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/FederatedRequest.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/FederatedWorkspace.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/ForkRequestProcessor.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/GeneralProjector.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/JoinMirrorRequestProcessor.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/JoinRequestProcessor.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/MirrorProjector.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/NoMoreFederatedRequests.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/OffsetMirrorProjector.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/ProjectedNode.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/Projection.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/ProjectionParser.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/Projector.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/ProjectorWithPlaceholders.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/package-info.java
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/MockRepositoryConnection.java
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/MockRepositoryRequestProcessor.java
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/AbstractFederatedRepositorySourceIntegrationTest.java
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/AbstractProjectorTest.java
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/BranchedMirrorProjectorTest.java
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/FederatedRepositoryConnectionTest.java
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/FederatedRepositorySourceTest.java
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/FederatedRepositorySourceUsingMirrorAndBranchProjectionsTest.java
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/FederatedRepositorySourceUsingMirrorProjectionTest.java
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/FederatedRepositorySourceUsingOffsetProjectionTest.java
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/FederatedRepositoryTest.java
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/FederatedRequestTest.java
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/FederatedWorkspaceTest.java
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/ForkRequestProcessorChannelTest.java
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/ForkRequestProcessorTest.java
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/GeneralProjectorTest.java
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/JoinRequestProcessorTest.java
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/MirrorProjectorTest.java
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/OffsetMirrorProjectorTest.java
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/ProjectionParserTest.java
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/ProjectionPathRuleTest.java
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/ProjectionTest.java
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/ProjectorWithPlaceholdersTest.java
Modified:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/DnaLexicon.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/ExecutionContext.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/Graph.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/GraphI18n.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/Location.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/inmemory/InMemoryRequestProcessor.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CloneWorkspaceRequest.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CompositeRequest.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CopyBranchRequest.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CreateNodeRequest.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CreateWorkspaceRequest.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/DeleteBranchRequest.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/GetWorkspacesRequest.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/MoveBranchRequest.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadAllChildrenRequest.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadAllPropertiesRequest.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadBlockOfChildrenRequest.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadBranchRequest.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadNextBlockOfChildrenRequest.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadNodeRequest.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadPropertyRequest.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/RemovePropertyRequest.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/RenameNodeRequest.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/SetPropertyRequest.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/UpdatePropertiesRequest.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/VerifyNodeExistsRequest.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/VerifyWorkspaceRequest.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/processor/RequestProcessor.java
trunk/dna-graph/src/main/resources/org/jboss/dna/graph/GraphI18n.properties
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/inmemory/InMemoryConnectorReadableTest.java
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/test/AbstractConnectorTest.java
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/test/ReadableConnectorTest.java
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/test/WorkspaceConnectorTest.java
trunk/pom.xml
Log:
DNA-403 Federation connector does not behave properly in many situations
Implemented a new federation connector (in 'dna-graph') that addresses most if not all of the concerns listed in the issue. Some changes were made to the requests (e.g., addition of DeleteChildrenRequest) and ExecutionContext and Location. However, most of the changes were in the addition of the 'org.jboss.dna.graph.connector.federation' package and its mostly package-level classes.
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/DnaLexicon.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/DnaLexicon.java 2009-05-15 19:28:48 UTC (rev 910)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/DnaLexicon.java 2009-05-18 15:36:58 UTC (rev 911)
@@ -42,6 +42,15 @@
public static final Name CLASSPATH = new BasicName(Namespace.URI, "classpath");
public static final Name NAMESPACES = new BasicName(Namespace.URI, "namespaces");
public static final Name PROJECTION_RULES = new BasicName(Namespace.URI, "projectionRules");
+ public static final Name READ_ONLY = new BasicName(Namespace.URI, "readOnly");
public static final Name TIME_TO_EXPIRE = new BasicName(Namespace.URI, "timeToExpire");
public static final Name NAMESPACE_URI = new BasicName(Namespace.URI, "uri");
+
+ public static final Name WORKSPACES = new BasicName(Namespace.URI, "workspaces");
+ public static final Name SOURCE_NAME = new BasicName(Namespace.URI, "sourceName");
+ public static final Name WORKSPACE_NAME = new BasicName(Namespace.URI, "workspaceName");
+ public static final Name DEFAULT_WORKSPACE_NAME = new BasicName(Namespace.URI, "defaultWorkspaceName");
+ public static final Name PROJECTION = new BasicName(Namespace.URI, "projection");
+ public static final Name PROJECTIONS = new BasicName(Namespace.URI, "projections");
+
}
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/ExecutionContext.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/ExecutionContext.java 2009-05-15 19:28:48 UTC (rev 910)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/ExecutionContext.java 2009-05-18 15:36:58 UTC (rev 911)
@@ -42,6 +42,7 @@
import org.jboss.dna.common.component.StandardClassLoaderFactory;
import org.jboss.dna.common.util.CheckArg;
import org.jboss.dna.common.util.Logger;
+import org.jboss.dna.graph.connector.federation.FederatedLexicon;
import org.jboss.dna.graph.mimetype.ExtensionBasedMimeTypeDetector;
import org.jboss.dna.graph.mimetype.MimeTypeDetector;
import org.jboss.dna.graph.property.NamespaceRegistry;
@@ -421,7 +422,7 @@
CallbackHandler callbackHandler ) throws LoginException {
LoginContext loginContext = new LoginContext(name, callbackHandler);
loginContext.login();
-
+
return new ExecutionContext(this, loginContext);
}
@@ -464,7 +465,7 @@
CallbackHandler callbackHandler ) throws LoginException {
LoginContext loginContext = new LoginContext(name, subject, callbackHandler);
loginContext.login();
-
+
return new ExecutionContext(this, loginContext);
}
@@ -499,6 +500,7 @@
namespaceRegistry.register(JcrMixLexicon.Namespace.PREFIX, JcrMixLexicon.Namespace.URI);
namespaceRegistry.register(JcrNtLexicon.Namespace.PREFIX, JcrNtLexicon.Namespace.URI);
namespaceRegistry.register(DnaLexicon.Namespace.PREFIX, DnaLexicon.Namespace.URI);
+ namespaceRegistry.register(FederatedLexicon.Namespace.PREFIX, DnaLexicon.Namespace.URI);
// namespaceRegistry.register("dnadtd", "http://www.jboss.org/dna/dtd/1.0");
// namespaceRegistry.register("dnaxml", "http://www.jboss.org/dna/xml/1.0");
}
@@ -521,9 +523,8 @@
}
/**
+ * {@inheritDoc}
*
- * {@inheritDoc}
- *
* @see javax.security.auth.callback.CallbackHandler#handle(javax.security.auth.callback.Callback[])
*/
public void handle( Callback[] callbacks ) throws UnsupportedCallbackException, IOException {
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/Graph.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/Graph.java 2009-05-15 19:28:48 UTC (rev 910)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/Graph.java 2009-05-18 15:36:58 UTC (rev 911)
@@ -986,7 +986,7 @@
getCurrentWorkspaceName(),
child,
EMPTY_PROPERTIES,
- NodeConflictBehavior.DO_NOT_REPLACE).getActualLocationOfNode();
+ NodeConflictBehavior.UPDATE).getActualLocationOfNode();
return new GetNodeOrReturnGraph(location);
}
@@ -1009,7 +1009,7 @@
getCurrentWorkspaceName(),
child,
EMPTY_PROPERTIES,
- NodeConflictBehavior.DO_NOT_REPLACE).getActualLocationOfNode();
+ NodeConflictBehavior.UPDATE).getActualLocationOfNode();
return new GetNodeOrReturnGraph(location);
}
@@ -1716,15 +1716,13 @@
}
public List<Location> under( Location at ) {
- return requests.readBlockOfChildren(at, getCurrentWorkspaceName(), startingIndex, blockSize)
- .getChildren();
+ return requests.readBlockOfChildren(at, getCurrentWorkspaceName(), startingIndex, blockSize).getChildren();
}
};
}
public List<Location> startingAfter( final Location previousSibling ) {
- return requests.readNextBlockOfChildren(previousSibling, getCurrentWorkspaceName(), blockSize)
- .getChildren();
+ return requests.readNextBlockOfChildren(previousSibling, getCurrentWorkspaceName(), blockSize).getChildren();
}
public List<Location> startingAfter( String pathOfPreviousSibling ) {
@@ -5248,10 +5246,7 @@
}
public SubgraphNode getNode( Name relativePath ) {
- Path path = getGraph().getContext()
- .getValueFactories()
- .getPathFactory()
- .create(getLocation().getPath(), relativePath);
+ Path path = getGraph().getContext().getValueFactories().getPathFactory().create(getLocation().getPath(), relativePath);
path = path.getNormalizedPath();
return getNode(path);
}
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/GraphI18n.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/GraphI18n.java 2009-05-15 19:28:48 UTC (rev 910)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/GraphI18n.java 2009-05-18 15:36:58 UTC (rev 911)
@@ -68,6 +68,7 @@
public static I18n executedRequest;
public static I18n closingRequestProcessor;
public static I18n closedRequestProcessor;
+ public static I18n multipleErrorsWhileExecutingManyRequests;
public static I18n multipleErrorsWhileExecutingRequests;
public static I18n unsupportedRequestType;
public static I18n unableToAddMoreRequestsToAlreadyExecutedBatch;
@@ -88,13 +89,33 @@
public static I18n unableToFindRepositorySourceWithName;
/* In-Memory Connector */
- public static I18n inMemoryConnectorName;
public static I18n inMemoryNodeDoesNotExist;
public static I18n errorSerializingInMemoryCachePolicyInSource;
public static I18n inMemoryConnectorRequestsMustHavePathOrUuid;
public static I18n workspaceDoesNotExistInRepository;
public static I18n workspaceAlreadyExistsInRepository;
+ /* Federation Connection */
+ public static I18n namePropertyIsRequiredForFederatedRepositorySource;
+ public static I18n propertyIsRequiredForFederatedRepositorySource;
+ public static I18n federatedRepositorySourceMustBeInitialized;
+ public static I18n errorReadingConfigurationForFederatedRepositorySource;
+ public static I18n errorAddingProjectionRuleParseMethod;
+ public static I18n requiredNodeDoesNotExistRelativeToNode;
+ public static I18n unableToObtainConnectionToFederatedSource;
+ public static I18n workspaceDoesNotExistInFederatedRepository;
+ public static I18n locationCannotBeProjectedIntoWorkspaceAndSource;
+ public static I18n unableToAddRequestToChannelThatIsDone;
+ public static I18n federatedSourceDoesNotSupportCreatingWorkspaces;
+ public static I18n federatedSourceDoesNotSupportCloningWorkspaces;
+ public static I18n federatedSourceDoesNotSupportDestroyingWorkspaces;
+ public static I18n unableToProjectSourceInformationIntoWorkspace;
+ public static I18n unableToCreateNodeUnderPlaceholder;
+ public static I18n unableToUpdatePlaceholder;
+ public static I18n unableToDeletePlaceholder;
+ public static I18n copyLimitedToBeWithinSingleSource;
+ public static I18n moveLimitedToBeWithinSingleSource;
+
static {
try {
I18n.initialize(GraphI18n.class);
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/Location.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/Location.java 2009-05-15 19:28:48 UTC (rev 910)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/Location.java 2009-05-18 15:36:58 UTC (rev 911)
@@ -45,7 +45,7 @@
* @author Randall Hauch
*/
@Immutable
-public abstract class Location implements Iterable<Property> {
+public abstract class Location implements Iterable<Property>, Comparable<Location> {
/**
* Simple shared iterator instance that is used when there are no properties.
@@ -432,6 +432,24 @@
}
/**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Comparable#compareTo(java.lang.Object)
+ */
+ public int compareTo( Location that ) {
+ if (this == that) return 0;
+ if (this.hasPath() && that.hasPath()) {
+ return this.getPath().compareTo(that.getPath());
+ }
+ UUID thisUuid = this.getUuid();
+ UUID thatUuid = that.getUuid();
+ if (thisUuid != null && thatUuid != null) {
+ return thisUuid.compareTo(thatUuid);
+ }
+ return this.hashCode() - that.hashCode();
+ }
+
+ /**
* Get the string form of the location.
*
* @return the string
Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/BranchedMirrorProjector.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/BranchedMirrorProjector.java (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/BranchedMirrorProjector.java 2009-05-18 15:36:58 UTC (rev 911)
@@ -0,0 +1,193 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ *
+ * JBoss DNA 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.graph.connector.federation;
+
+import java.util.Iterator;
+import java.util.List;
+import net.jcip.annotations.Immutable;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.Location;
+import org.jboss.dna.graph.connector.federation.Projection.Rule;
+import org.jboss.dna.graph.property.Path;
+import org.jboss.dna.graph.property.PathFactory;
+import org.jboss.dna.graph.property.Path.Segment;
+
+/**
+ * A Projector for federated repository configurations that have a single mirrored projection (a projection that is a one-for-one
+ * mapping to a single source) and a single branch projection that maps a single path in the source to a single path in the
+ * federated repository.
+ * <p>
+ * For example, consider a federated repository that maps the "/" path in source1 into "/" in the federated repository, and the
+ * "/path/in/source" path into the "/federated/path" branch in the federated repository. Thus, anything at or below
+ * "/federated/path" goes into the (If this were the only projection, then a {@link MirrorProjector} could be used.)
+ */
+@Immutable
+final class BranchedMirrorProjector extends ProjectorWithPlaceholders {
+
+ /**
+ * Attempt to create an instance of the {@link BranchedMirrorProjector} with the supplied projections using the supplied
+ * context.
+ *
+ * @param context the context; may not be null
+ * @param projections the projections in the federated repository; may not be null
+ * @return the branched mirror projector, or null if the projections didn't match the criteria for such a projector
+ */
+ static BranchedMirrorProjector with( ExecutionContext context,
+ List<Projection> projections ) {
+ assert projections != null;
+ assert context != null;
+ if (projections.size() != 2) return null;
+ Projection first = projections.get(0);
+ Projection second = projections.get(1);
+ if (first.getRules().size() != 1) return null;
+ if (second.getRules().size() != 1) return null;
+ Rule firstRule = first.getRules().get(0);
+ Rule secondRule = second.getRules().get(0);
+ assert firstRule != null;
+ assert secondRule != null;
+ PathFactory pathFactory = context.getValueFactories().getPathFactory();
+ List<Path> firstTopLevelPaths = first.getRules().get(0).getTopLevelPathsInRepository(pathFactory);
+ if (firstTopLevelPaths.size() != 1) return null;
+ List<Path> secondTopLevelPaths = second.getRules().get(0).getTopLevelPathsInRepository(pathFactory);
+ if (secondTopLevelPaths.size() != 1) return null;
+ // We're good, so create the instance ...
+ Path firstTopLevelPath = firstTopLevelPaths.get(0);
+ Path secondTopLevelPath = secondTopLevelPaths.get(0);
+ if (firstTopLevelPath.isRoot()) {
+ return new BranchedMirrorProjector(context, projections, first, second, secondTopLevelPath,
+ secondRule.getPathInSource(secondTopLevelPath, pathFactory));
+ }
+ return new BranchedMirrorProjector(context, projections, second, first, firstTopLevelPath,
+ firstRule.getPathInSource(firstTopLevelPath, pathFactory));
+ }
+
+ private final Projection mirrorProjection;
+ private final Projection branchProjection;
+ private final Path branchFederatedPath;
+ private final Path branchSourcePath;
+ private final boolean branchSourceUsesSamePath;
+
+ BranchedMirrorProjector( ExecutionContext context,
+ List<Projection> projections,
+ Projection mirrorProjection,
+ Projection branchProjection,
+ Path branchFederatedPath,
+ Path branchSourcePath ) {
+ super(context, projections);
+ assert mirrorProjection != null;
+ assert branchProjection != null;
+ assert branchFederatedPath != null;
+ assert branchSourcePath != null;
+ this.mirrorProjection = mirrorProjection;
+ this.branchProjection = branchProjection;
+ this.branchFederatedPath = branchFederatedPath;
+ this.branchSourcePath = branchSourcePath;
+ this.branchSourceUsesSamePath = branchSourcePath.equals(branchFederatedPath);
+ }
+
+ /**
+ * {@inheritDoc}
+ * <p>
+ * This implementation <i>always<i> returns a single {@link ProxyNode} for the location in the single projection.
+ * </p>
+ *
+ * @see org.jboss.dna.graph.connector.federation.Projector#project(ExecutionContext, Location, boolean)
+ */
+ public ProjectedNode project( ExecutionContext context,
+ Location location,
+ boolean requiresUpdate ) {
+ if (location.hasPath()) {
+ Path path = location.getPath();
+ if (path.isRoot()) {
+ // It is a projection of the mirror's root and the placeholder for the branch root ...
+ if (requiresUpdate && mirrorProjection.isReadOnly()) return null;
+ ProxyNode result = new ProxyNode(mirrorProjection, location, location.with(branchSourcePath),
+ branchSourceUsesSamePath);
+ result.add(isPlaceholder(location));
+ return result;
+ }
+ ProjectedNode onBranch = isOnBranch(path, location, context);
+ if (onBranch != null) {
+ if (requiresUpdate && branchProjection.isReadOnly()) return null;
+ return onBranch;
+ }
+ // Otherwise it is a proxy to the mirror only ...
+ if (requiresUpdate && mirrorProjection.isReadOnly()) return null;
+ return new ProxyNode(mirrorProjection, location, location, true);
+ }
+
+ // The location has no path and only identifier properties ...
+ if (requiresUpdate) {
+ if (branchProjection.isReadOnly()) {
+ // Can't update branch ...
+ if (mirrorProjection.isReadOnly()) return null;
+ return new ProxyNode(mirrorProjection, location, location, true); // no paths
+ }
+ if (mirrorProjection.isReadOnly()) {
+ // Can't update mirror ...
+ return new ProxyNode(branchProjection, location, location, branchSourceUsesSamePath); // no paths
+ }
+ }
+
+ // This is just a read, so create one projection for the mirror, and a second one for the branch.
+ ProjectedNode result = new ProxyNode(mirrorProjection, location, location, true);
+ result.add(new ProxyNode(branchProjection, location, location, branchSourceUsesSamePath)); // no paths
+ return result;
+ }
+
+ protected final ProjectedNode isOnBranch( Path federatedPath,
+ Location location,
+ ExecutionContext context ) {
+ Iterator<Segment> branchIter = branchFederatedPath.iterator();
+ Iterator<Segment> federIter = federatedPath.iterator();
+ // Look at the first path ...
+ if (branchIter.hasNext() && federIter.hasNext()) {
+ if (!branchIter.next().equals(federIter.next())) return null; // not on branch
+ }
+ // Otherwise, the federated path is on the branch, but how far ...
+ while (branchIter.hasNext() && federIter.hasNext()) {
+ if (!branchIter.next().equals(federIter.next())) {
+ // Didn't make it all the way along the branch ...
+ return null;
+ }
+ }
+ if (branchIter.hasNext()) {
+ // Didn't make it all the way down to the top of the branch, but it is a placeholder ...
+ return isPlaceholder(location);
+ }
+ // Otherwise it is within the brach ...
+ Location locationInSource = location;
+ if (!branchSourceUsesSamePath) {
+ // The source uses a different path ...
+ if (federIter.hasNext()) {
+ Path subpath = federatedPath.subpath(branchFederatedPath.size());
+ Path sourcePath = context.getValueFactories().getPathFactory().create(branchSourcePath, subpath);
+ locationInSource = location.with(sourcePath);
+ } else {
+ locationInSource = location.with(branchSourcePath);
+ }
+ }
+ return new ProxyNode(branchProjection, locationInSource, location, branchSourceUsesSamePath);
+ }
+}
Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/BranchedMirrorProjector.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Copied: trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/FederatedLexicon.java (from rev 910, trunk/dna-graph/src/main/java/org/jboss/dna/graph/DnaLexicon.java)
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/FederatedLexicon.java (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/FederatedLexicon.java 2009-05-18 15:36:58 UTC (rev 911)
@@ -0,0 +1,42 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ *
+ * JBoss DNA 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.graph.connector.federation;
+
+import org.jboss.dna.graph.DnaLexicon;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.basic.BasicName;
+
+/**
+ * @author Randall Hauch
+ */
+public class FederatedLexicon extends DnaLexicon {
+
+ public static final Name WORKSPACES = new BasicName(Namespace.URI, "workspaces");
+ public static final Name CACHE = new BasicName(Namespace.URI, "cache");
+ public static final Name PROJECTION = new BasicName(Namespace.URI, "projection");
+ public static final Name PROJECTIONS = new BasicName(Namespace.URI, "projections");
+ public static final Name SOURCE_NAME = new BasicName(Namespace.URI, "sourceName");
+ public static final Name WORKSPACE_NAME = new BasicName(Namespace.URI, "workspaceName");
+ public static final Name DEFAULT_WORKSPACE_NAME = new BasicName(Namespace.URI, "defaultWorkspaceName");
+}
Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/FederatedRepository.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/FederatedRepository.java (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/FederatedRepository.java 2009-05-18 15:36:58 UTC (rev 911)
@@ -0,0 +1,166 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * Unless otherwise indicated, all code in JBoss DNA is licensed
+ * to you 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.
+ *
+ * JBoss DNA 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.graph.connector.federation;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ExecutorService;
+import net.jcip.annotations.Immutable;
+import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.graph.GraphI18n;
+import org.jboss.dna.graph.cache.CachePolicy;
+import org.jboss.dna.graph.connector.RepositoryConnectionFactory;
+import org.jboss.dna.graph.connector.RepositorySource;
+import org.jboss.dna.graph.request.InvalidWorkspaceException;
+
+/**
+ * The configuration of a {@link FederatedRepositorySource}.
+ */
+@Immutable
+class FederatedRepository {
+
+ private final String sourceName;
+ private final CachePolicy defaultCachePolicy;
+ private final RepositoryConnectionFactory connectionFactory;
+ private final Map<String, FederatedWorkspace> workspacesByName;
+ private final FederatedWorkspace defaultWorkspace;
+ private final ExecutorService executor;
+
+ /**
+ * Construct a new instance of a configuration defining how the workspaces in a federated repository should project the
+ * content from one or more sources.
+ *
+ * @param sourceName the name of the federated repository source; may not be null
+ * @param connectionFactory the factory for connections to the sources being federated; may not be null
+ * @param workspaces the workspaces that make up this federated repository; may not be null or empty
+ * @param defaultCachePolicy the default cache policy for the source, or null if there is no default caching policy for the
+ * whole federated repository (each workspace may have its own)
+ * @param executor the {@link ExecutorService} that can be used to parallelize actions within this repository;may not be null
+ */
+ public FederatedRepository( String sourceName,
+ RepositoryConnectionFactory connectionFactory,
+ Iterable<FederatedWorkspace> workspaces,
+ CachePolicy defaultCachePolicy,
+ ExecutorService executor ) {
+ CheckArg.isNotNull(sourceName, "sourceName");
+ CheckArg.isNotNull(connectionFactory, "connectionFactory");
+ CheckArg.isNotNull(workspaces, "workspaces");
+ CheckArg.isNotNull(executor, "executor");
+ this.sourceName = sourceName;
+ this.connectionFactory = connectionFactory;
+ this.executor = executor;
+ this.defaultCachePolicy = defaultCachePolicy;
+ this.workspacesByName = new HashMap<String, FederatedWorkspace>();
+ FederatedWorkspace defaultWorkspace = null;
+ for (FederatedWorkspace workspace : workspaces) {
+ if (defaultWorkspace == null) defaultWorkspace = workspace;
+ this.workspacesByName.put(workspace.getName(), workspace);
+ }
+ this.defaultWorkspace = defaultWorkspace;
+ assert this.defaultWorkspace != null;
+ assert this.workspacesByName.size() > 0;
+ }
+
+ /**
+ * Get the name of the {@link RepositorySource} that owns this configuration.
+ *
+ * @return the source's name; never null
+ */
+ public String getSourceName() {
+ return sourceName;
+ }
+
+ /**
+ * Get the {@link RepositoryConnectionFactory factory for connections} to the sources that are being federated by this
+ * repository.
+ *
+ * @return the connection factory for the sources; never null
+ */
+ public RepositoryConnectionFactory getConnectionFactory() {
+ return connectionFactory;
+ }
+
+ /**
+ * Get the {@link FederatedWorkspace workspace} information, given its name.
+ *
+ * @param name the name of the workspace, or null if the default workspace should be returned
+ * @return the workspace
+ * @throws InvalidWorkspaceException if the specified workspace does not exist
+ */
+ public FederatedWorkspace getWorkspace( String name ) {
+ if (name == null) {
+ assert defaultWorkspace != null;
+ return defaultWorkspace;
+ }
+ FederatedWorkspace workspace = workspacesByName.get(name);
+ if (workspace == null) {
+ String msg = GraphI18n.workspaceDoesNotExistInFederatedRepository.text(name, getSourceName());
+ throw new InvalidWorkspaceException(msg);
+ }
+ return workspace;
+ }
+
+ /**
+ * Get the names of the available workspaces.
+ *
+ * @return the unmodifiable copy of the workspace names; never null
+ */
+ Set<String> getWorkspaceNames() {
+ return Collections.unmodifiableSet(new HashSet<String>(workspacesByName.keySet()));
+ }
+
+ /**
+ * Get the default cache policy that used for the whole repository. Note that the repository may or may not have a default
+ * caching policy, and each {@link FederatedWorkspace workspace} may have its own {@link FederatedWorkspace#getCachePolicy()
+ * caching policy}.
+ *
+ * @return the default cache policy for the repository, used if/when the workspace(s) don't have their own caching policy or
+ * when the source content does not specify the caching policy; may be null
+ */
+ public CachePolicy getDefaultCachePolicy() {
+ return defaultCachePolicy;
+ }
+
+ /**
+ * Get the executor for this repository. This executor can be used to process tasks.
+ *
+ * @return the executor; may not be null
+ */
+ public ExecutorService getExecutor() {
+ return executor;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return "Federated repository \"" + getSourceName() + "\" with workspaces " + workspacesByName.keySet();
+ }
+}
Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/FederatedRepository.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/FederatedRepositoryConnection.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/FederatedRepositoryConnection.java (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/FederatedRepositoryConnection.java 2009-05-18 15:36:58 UTC (rev 911)
@@ -0,0 +1,265 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * Unless otherwise indicated, all code in JBoss DNA is licensed
+ * to you 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.
+ *
+ * JBoss DNA 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.graph.connector.federation;
+
+import java.util.LinkedList;
+import java.util.Queue;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+import javax.transaction.xa.XAResource;
+import org.jboss.dna.common.statistic.Stopwatch;
+import org.jboss.dna.common.util.Logger;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.cache.CachePolicy;
+import org.jboss.dna.graph.connector.RepositoryConnection;
+import org.jboss.dna.graph.connector.RepositorySourceException;
+import org.jboss.dna.graph.connector.RepositorySourceListener;
+import org.jboss.dna.graph.property.DateTime;
+import org.jboss.dna.graph.request.CompositeRequest;
+import org.jboss.dna.graph.request.Request;
+import org.jboss.dna.graph.request.processor.RequestProcessor;
+
+/**
+ * This {@link RepositoryConnection} implementation executes {@link Request requests} against the federated repository by
+ * projecting them into requests against the underlying sources that are being federated.
+ * <p>
+ * One important design of the connector framework is that requests can be submitted in {@link CompositeRequest batch} to a
+ * {@link RepositoryConnection connection}, which may perform them more efficiently than if each request was submitted one at a
+ * time. This connector design maintains this feature by projecting the incoming requests into requests against each source, then
+ * submitting the batch of projected requests to each source, and then transforming the results of the projected requests back
+ * into original requests.
+ * </p>
+ * <p>
+ * This is accomplished using a three-step process:
+ * <ol>
+ * <li><strong>Step 1:</strong> Process the incoming requests and for each generate the appropriate request(s) against the sources
+ * (dictated by the {@link FederatedWorkspace workspace's} {@link FederatedWorkspace#getProjections() projections}). These
+ * "projected requests" are then enqueued for each source.</li>
+ * <li><strong>Step 2:</strong> Submit each batch of projected requests to the appropriate source, in parallel where possible.
+ * Note that the requests are still ordered correctly for each source.</li>
+ * <li><strong>Step 3:</strong> Accumulate the results for the incoming requests by post-processing the projected requests and
+ * transforming the source-specific results back into the federated workspace (again, using the workspace's projections).</li>
+ * </ol>
+ * </p>
+ * <p>
+ * This process is a form of the <i>fork-join</i> divide-and-conquer algorithm, which involves splitting a problem into smaller
+ * parts, forking new subtasks to execute each smaller part, joining on the subtasks (waiting until all have finished), and then
+ * composing the results. Technically, Step 2 performs the fork and join operations, but this class uses {@link RequestProcessor}
+ * implementations to do Step 1 and 3 (called {@link ForkRequestProcessor} and {@link JoinRequestProcessor}, respectively).
+ * </p>
+ */
+class FederatedRepositoryConnection implements RepositoryConnection {
+
+ private final FederatedRepository repository;
+ private final Stopwatch stopwatch;
+ private final Logger logger;
+
+ FederatedRepositoryConnection( FederatedRepository repository ) {
+ this.repository = repository;
+ this.logger = Logger.getLogger(getClass());
+ this.stopwatch = logger.isTraceEnabled() ? new Stopwatch() : null;
+ }
+
+ /**
+ * The federated repository that created this connection.
+ *
+ * @return repository
+ */
+ FederatedRepository getRepository() {
+ return repository;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.connector.RepositoryConnection#getDefaultCachePolicy()
+ */
+ public CachePolicy getDefaultCachePolicy() {
+ return repository.getDefaultCachePolicy(); // may be null
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.connector.RepositoryConnection#getSourceName()
+ */
+ public String getSourceName() {
+ return repository.getSourceName();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.connector.RepositoryConnection#getXAResource()
+ */
+ public XAResource getXAResource() {
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.connector.RepositoryConnection#ping(long, java.util.concurrent.TimeUnit)
+ */
+ public boolean ping( long time,
+ TimeUnit unit ) {
+ return true;
+ }
+
+ protected boolean shouldProcessSynchronously( Request request ) {
+ if (request instanceof CompositeRequest) {
+ CompositeRequest composite = (CompositeRequest)request;
+ if (composite.size() == 1) return true;
+ return false;
+ }
+ // Otherwise, its just a single request ...
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.connector.RepositoryConnection#execute(org.jboss.dna.graph.ExecutionContext,
+ * org.jboss.dna.graph.request.Request)
+ */
+ public void execute( ExecutionContext context,
+ final Request request ) throws RepositorySourceException {
+ // Compute the current time ...
+ DateTime nowInUtc = context.getValueFactories().getDateFactory().createUtc();
+
+ // Figure out whether we should asynchronously do the forking ...
+ boolean synchronousStep1 = shouldProcessSynchronously(request);
+ final boolean awaitAllSubtasks = false;
+
+ // Prepare for trace-level logging ...
+ if (stopwatch != null) stopwatch.start();
+
+ boolean abort = false;
+ try {
+ // ----------------------------------------------------------------------------------------------------
+ // Step 1: Fork the submitted requests into source-specific requests...
+ // ----------------------------------------------------------------------------------------------------
+ // This forks a subtask for each source, as soon as the first source-specific request for a source
+ // is generated. Each source's "execute(ExecutionContext,Request)" is called only once (a queue is
+ // used so that the source can begin processing the requests before all the requests have been
+ // computed and submitted to the subtask). Thus, it's possible (and likely) that this thread
+ // and subtask threads are executed in parallel.
+ final Queue<FederatedRequest> requests = awaitAllSubtasks ? new LinkedList<FederatedRequest>() : new LinkedBlockingQueue<FederatedRequest>();
+ final ForkRequestProcessor fork = new ForkRequestProcessor(repository, context, nowInUtc, requests);
+ if (synchronousStep1) {
+ // Execute the forking process in this thread ...
+ try {
+ fork.process(request);
+ } finally {
+ fork.close();
+ }
+ if (!awaitAllSubtasks) requests.add(new NoMoreFederatedRequests());
+ // At this point, all submitted requests have been processed/forked, so we can continue with
+ // the join process, starting with the first submitted request. Note that the subtasks may
+ // still be executing, but as the join process operates on a forked request, it will wait
+ // until all forked requests have completed. Hopefully, in most situations, the subtasks
+ // have enough of a lead that the join process never has to wait.
+ } else {
+ // Submit the forking process for execution in a separate thread ...
+ repository.getExecutor().submit(new Runnable() {
+ public void run() {
+ try {
+ fork.process(request);
+ } finally {
+ fork.close();
+ }
+ if (!awaitAllSubtasks) requests.add(new NoMoreFederatedRequests());
+ }
+ });
+
+ // At this point, the forking process is being run by a thread owned by the Executor. We'll still
+ // continue with the join process, starting with the first submitted request. Note that it is
+ // likely that the subtasks are still running in threads owned by the Executor.
+ }
+
+ if (awaitAllSubtasks) {
+ // Await until all subtasks have completed ...
+ fork.await();
+ }
+
+ // ----------------------------------------------------------------------------------------------------
+ // Step 2: Join the results of the source-specific (forked) requests back into the submitted requests
+ // ----------------------------------------------------------------------------------------------------
+ JoinRequestProcessor join = new JoinRequestProcessor(repository, context, nowInUtc);
+ try {
+ if (awaitAllSubtasks) {
+ join.process(requests);
+ } else {
+ join.process((BlockingQueue<FederatedRequest>)requests);
+ }
+ } catch (RuntimeException e) {
+ abort = true;
+ throw e;
+ } finally {
+ join.close();
+ }
+ } catch (InterruptedException e) {
+ abort = true;
+ request.setError(e);
+ } catch (ExecutionException e) {
+ abort = true;
+ request.setError(e);
+ } catch (CancellationException e) {
+ abort = true;
+ request.cancel();
+ // do nothing else
+ } catch (RuntimeException e) {
+ abort = true;
+ throw e;
+ } finally {
+ if (stopwatch != null) stopwatch.stop();
+ if (abort) {
+ // Rollback the transaction (if there is one) ...
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.connector.RepositoryConnection#close()
+ */
+ public void close() {
+ if (stopwatch != null) {
+ logger.trace("Processing federated requests:\n" + stopwatch.getDetailedStatistics());
+ }
+ // do nothing else, since we don't currently hold any state
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.connector.RepositoryConnection#setListener(org.jboss.dna.graph.connector.RepositorySourceListener)
+ */
+ public void setListener( RepositorySourceListener listener ) {
+ }
+}
Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/FederatedRepositoryConnection.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/FederatedRepositorySource.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/FederatedRepositorySource.java (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/FederatedRepositorySource.java 2009-05-18 15:36:58 UTC (rev 911)
@@ -0,0 +1,588 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * Unless otherwise indicated, all code in JBoss DNA is licensed
+ * to you 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.
+ *
+ * JBoss DNA 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.graph.connector.federation;
+
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import javax.naming.Context;
+import javax.naming.Name;
+import javax.naming.RefAddr;
+import javax.naming.Reference;
+import javax.naming.StringRefAddr;
+import javax.naming.spi.ObjectFactory;
+import net.jcip.annotations.GuardedBy;
+import org.jboss.dna.common.i18n.I18n;
+import org.jboss.dna.common.util.HashCode;
+import org.jboss.dna.graph.DnaLexicon;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.Graph;
+import org.jboss.dna.graph.GraphI18n;
+import org.jboss.dna.graph.Location;
+import org.jboss.dna.graph.Node;
+import org.jboss.dna.graph.Subgraph;
+import org.jboss.dna.graph.SubgraphNode;
+import org.jboss.dna.graph.cache.BasicCachePolicy;
+import org.jboss.dna.graph.cache.CachePolicy;
+import org.jboss.dna.graph.connector.RepositoryConnection;
+import org.jboss.dna.graph.connector.RepositoryConnectionFactory;
+import org.jboss.dna.graph.connector.RepositoryContext;
+import org.jboss.dna.graph.connector.RepositorySource;
+import org.jboss.dna.graph.connector.RepositorySourceCapabilities;
+import org.jboss.dna.graph.connector.RepositorySourceException;
+import org.jboss.dna.graph.property.NamespaceRegistry;
+import org.jboss.dna.graph.property.Path;
+import org.jboss.dna.graph.property.Property;
+import org.jboss.dna.graph.property.ValueFactories;
+import org.jboss.dna.graph.property.ValueFactory;
+
+/**
+ *
+ */
+public class FederatedRepositorySource implements RepositorySource, ObjectFactory {
+
+ /**
+ * The default limit is {@value} for retrying {@link RepositoryConnection connection} calls to the underlying source.
+ */
+ public static final int DEFAULT_RETRY_LIMIT = 0;
+
+ /**
+ * The default path to the node defining the configuration for a source.
+ */
+ public static final String DEFAULT_CONFIGURATION_PATH = "/";
+
+ protected static final String SOURCE_NAME = "sourceName";
+ protected static final String RETRY_LIMIT = "retryLimit";
+ protected static final String CONFIGURATION_SOURCE_NAME = "configurationSourceName";
+ protected static final String CONFIGURATION_PATH = "configurationPath";
+ protected static final String CONFIGURATION_WORKSPACE_NAME = "configurationWorkspaceName";
+
+ private static final long serialVersionUID = 1L;
+
+ private volatile String name;
+ private volatile int retryLimit;
+ private volatile String configurationSourceName;
+ private volatile String configurationWorkspaceName;
+ private volatile String configurationPath = DEFAULT_CONFIGURATION_PATH;
+ private volatile RepositorySourceCapabilities capabilities = new RepositorySourceCapabilities(true, true, false, false, true);
+ private volatile transient FederatedRepository configuration;
+ private volatile transient RepositoryContext context;
+
+ /**
+ * Construct a new instance of a {@link RepositorySource} for a federated repository.
+ */
+ public FederatedRepositorySource() {
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.connector.RepositorySource#getName()
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * @param name Sets name to the specified value.
+ */
+ public synchronized void setName( String name ) {
+ if (this.name == name || this.name != null && this.name.equals(name)) return; // unchanged
+ this.name = name;
+ changeConfiguration();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.connector.RepositorySource#getRetryLimit()
+ */
+ public int getRetryLimit() {
+ return retryLimit;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.connector.RepositorySource#setRetryLimit(int)
+ */
+ public synchronized void setRetryLimit( int limit ) {
+ retryLimit = limit < 0 ? 0 : limit;
+ changeConfiguration();
+ }
+
+ /**
+ * Get the name of the {@link RepositorySource} that should be used to create the {@link FederatedRepository federated
+ * repository configuration} as the configuration repository.
+ * <p>
+ * This is a required property.
+ * </p>
+ *
+ * @return the name of the {@link RepositorySource} instance that should be used for the configuration
+ * @see #setConfigurationSourceName(String)
+ * @see #getConfigurationWorkspaceName()
+ * @see #getConfigurationPath()
+ */
+ public String getConfigurationSourceName() {
+ return configurationSourceName;
+ }
+
+ /**
+ * Set the name of the {@link RepositorySource} that should be used to create the {@link FederatedRepository federated
+ * repository configuration} as the configuration repository. The instance will be retrieved from the
+ * {@link RepositoryConnectionFactory} instance inside the {@link RepositoryContext#getRepositoryConnectionFactory()
+ * repository context} supplied during {@link RepositorySource#initialize(RepositoryContext) initialization}.
+ * <p>
+ * This is a required property.
+ * </p>
+ *
+ * @param sourceName the name of the {@link RepositorySource} instance that should be used for the configuration, or null if
+ * the federated repository instance is to be found in JNDI
+ * @see #getConfigurationSourceName()
+ * @see #setConfigurationPath(String)
+ * @see #setConfigurationWorkspaceName(String)
+ */
+ public synchronized void setConfigurationSourceName( String sourceName ) {
+ if (this.configurationSourceName == sourceName || this.configurationSourceName != null
+ && this.configurationSourceName.equals(sourceName)) return; // unchanged
+ this.configurationSourceName = sourceName;
+ changeConfiguration();
+ }
+
+ /**
+ * Get the name of the workspace in the {@link #getConfigurationSourceName() source} containing the configuration content for
+ * this source. If this workspace name is null, the default workspace as defined by that source will be used.
+ *
+ * @return the name of the configuration workspace, or null if the default workspace for the
+ * {@link #getConfigurationSourceName() configuration source} should be used
+ * @see #getConfigurationSourceName()
+ * @see #setConfigurationWorkspaceName(String)
+ * @see #getConfigurationPath()
+ */
+ public String getConfigurationWorkspaceName() {
+ return configurationWorkspaceName;
+ }
+
+ /**
+ * Set the name of the workspace in the {@link #getConfigurationSourceName() source} containing the configuration content for
+ * this source. If this workspace name is null, the default workspace as defined by that source will be used.
+ *
+ * @param workspaceName the name of the configuration workspace, or null if the default workspace for the
+ * {@link #getConfigurationSourceName() configuration source} should be used
+ * @see #setConfigurationSourceName(String)
+ * @see #setConfigurationPath(String)
+ * @see #getConfigurationWorkspaceName()
+ */
+ public synchronized void setConfigurationWorkspaceName( String workspaceName ) {
+ if (this.configurationWorkspaceName == workspaceName || this.configurationWorkspaceName != null
+ && this.configurationWorkspaceName.equals(workspaceName)) return; // unchanged
+ this.configurationWorkspaceName = workspaceName;
+ changeConfiguration();
+ }
+
+ /**
+ * Get the path in the {@link #getConfigurationWorkspaceName() workspace} of the {@link #getConfigurationSourceName()
+ * configuration source} where this source can find the content defining its configuration.
+ * <p>
+ * This is a required property.
+ * </p>
+ *
+ * @return the string array of projection rules, or null if the projection rules haven't yet been set or if the federated
+ * repository instance is to be found in JNDI
+ * @see #getConfigurationSourceName()
+ * @see #getConfigurationWorkspaceName()
+ * @see #setConfigurationPath(String)
+ */
+ public String getConfigurationPath() {
+ return configurationPath;
+ }
+
+ /**
+ * Set the path in the {@link #getConfigurationWorkspaceName() workspace} of the {@link #getConfigurationSourceName()
+ * configuration source} where this source can find the content defining its configuration.
+ * <p>
+ * This is a required property.
+ * </p>
+ *
+ * @param pathInSourceToConfigurationRoot the path within the configuration source to the node that should be the root of the
+ * configuration information, or null if the {@link #DEFAULT_CONFIGURATION_PATH default path} should be used
+ * @see #getConfigurationPath()
+ * @see #setConfigurationSourceName(String)
+ * @see #setConfigurationWorkspaceName(String)
+ */
+ public void setConfigurationPath( String pathInSourceToConfigurationRoot ) {
+ if (this.configurationPath == pathInSourceToConfigurationRoot || this.configurationPath != null
+ && this.configurationPath.equals(pathInSourceToConfigurationRoot)) return;
+ String path = pathInSourceToConfigurationRoot != null ? pathInSourceToConfigurationRoot : DEFAULT_CONFIGURATION_PATH;
+ // Ensure one leading slash and one trailing slashes ...
+ this.configurationPath = path = ("/" + path).replaceAll("^/+", "/").replaceAll("/+$", "") + "/";
+ changeConfiguration();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.connector.RepositorySource#getCapabilities()
+ */
+ public RepositorySourceCapabilities getCapabilities() {
+ return capabilities;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.connector.RepositorySource#initialize(org.jboss.dna.graph.connector.RepositoryContext)
+ */
+ public synchronized void initialize( RepositoryContext context ) throws RepositorySourceException {
+ this.context = context;
+ changeConfiguration();
+ }
+
+ /**
+ * Get the repository context that was used to {@link #initialize(RepositoryContext) initialize} this source.
+ *
+ * @return the context, or null if the source was not yet {@link #initialize(RepositoryContext) initialized}
+ */
+ /*package*/RepositoryContext getRepositoryContext() {
+ return context;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.connector.RepositorySource#getConnection()
+ */
+ public RepositoryConnection getConnection() throws RepositorySourceException {
+ FederatedRepository config = this.configuration;
+ if (config == null) {
+ synchronized (this) {
+ if (this.configuration == null) {
+ // Check all the properties of this source ...
+ String name = getName();
+ if (name == null) {
+ I18n msg = GraphI18n.namePropertyIsRequiredForFederatedRepositorySource;
+ throw new RepositorySourceException(getName(), msg.text("name"));
+ }
+ RepositoryContext repositoryContext = getRepositoryContext();
+ if (repositoryContext == null) {
+ I18n msg = GraphI18n.federatedRepositorySourceMustBeInitialized;
+ throw new RepositorySourceException(getName(), msg.text("name", name));
+ }
+ String configSource = getConfigurationSourceName();
+ String configWorkspace = getConfigurationWorkspaceName();
+ String configPath = getConfigurationPath();
+ if (configSource == null) {
+ I18n msg = GraphI18n.propertyIsRequiredForFederatedRepositorySource;
+ throw new RepositorySourceException(getName(), msg.text("configuration source name", name));
+ }
+
+ // Load the configuration ...
+ this.configuration = loadRepository(name, repositoryContext, configSource, configWorkspace, configPath);
+ }
+ config = this.configuration;
+ }
+ }
+ return new FederatedRepositoryConnection(config);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see javax.naming.Referenceable#getReference()
+ */
+ public Reference getReference() {
+ String className = getClass().getName();
+ String factoryClassName = this.getClass().getName();
+ Reference ref = new Reference(className, factoryClassName, null);
+
+ ref.add(new StringRefAddr(SOURCE_NAME, getName()));
+ ref.add(new StringRefAddr(RETRY_LIMIT, Integer.toString(getRetryLimit())));
+ ref.add(new StringRefAddr(CONFIGURATION_SOURCE_NAME, getConfigurationSourceName()));
+ ref.add(new StringRefAddr(CONFIGURATION_WORKSPACE_NAME, getConfigurationWorkspaceName()));
+ ref.add(new StringRefAddr(CONFIGURATION_PATH, getConfigurationPath()));
+ return ref;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see javax.naming.spi.ObjectFactory#getObjectInstance(java.lang.Object, javax.naming.Name, javax.naming.Context,
+ * java.util.Hashtable)
+ */
+ public Object getObjectInstance( Object obj,
+ Name name,
+ Context nameCtx,
+ Hashtable<?, ?> environment ) throws Exception {
+ if (obj instanceof Reference) {
+ Map<String, String> values = new HashMap<String, String>();
+ Reference ref = (Reference)obj;
+ Enumeration<?> en = ref.getAll();
+ while (en.hasMoreElements()) {
+ RefAddr subref = (RefAddr)en.nextElement();
+ if (subref instanceof StringRefAddr) {
+ String key = subref.getType();
+ Object value = subref.getContent();
+ if (value != null) values.put(key, value.toString());
+ }
+ }
+ String sourceName = values.get(SOURCE_NAME);
+ String retryLimit = values.get(RETRY_LIMIT);
+ String configSourceName = values.get(CONFIGURATION_SOURCE_NAME);
+ String configSourceWorkspace = values.get(CONFIGURATION_WORKSPACE_NAME);
+ String configSourcePath = values.get(CONFIGURATION_PATH);
+
+ // Create the source instance ...
+ FederatedRepositorySource source = new FederatedRepositorySource();
+ if (sourceName != null) source.setName(sourceName);
+ if (configSourceName != null) source.setConfigurationSourceName(configSourceName);
+ if (configSourceWorkspace != null) source.setConfigurationWorkspaceName(configSourceWorkspace);
+ if (configSourcePath != null) source.setConfigurationPath(configSourcePath);
+ if (retryLimit != null) source.setRetryLimit(Integer.parseInt(retryLimit));
+ return source;
+ }
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int hashCode() {
+ return HashCode.compute(getName());
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean equals( Object obj ) {
+ if (obj == this) return true;
+ if (obj instanceof FederatedRepositorySource) {
+ FederatedRepositorySource that = (FederatedRepositorySource)obj;
+ // The source name must match
+ if (this.getName() == null) {
+ if (that.getName() != null) return false;
+ } else {
+ if (!this.getName().equals(that.getName())) return false;
+ }
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Mark the current configuration (if there is one) as being invalid.
+ */
+ @GuardedBy( "this" )
+ protected void changeConfiguration() {
+ this.configuration = null;
+ }
+
+ /**
+ * Utility to load the current configuration for this source from the {@link #getConfigurationSourceName() configuration
+ * repository}. This method may only be called after the source is {@link #initialize(RepositoryContext) initialized}.
+ *
+ * @param name the name of the source; may not be null
+ * @param repositoryContext the repository context; may not be null
+ * @param configSource the name of the configuration source; may not be null
+ * @param configWorkspace the name of the workspace in the configuration source, or null if the configuration source's default
+ * workspace should be used
+ * @param configPath the path to the node in the configuration workspace that defines the source; may not be null
+ * @return the configuration; never null
+ * @throws RepositorySourceException if there is a problem with the configuration
+ */
+ protected FederatedRepository loadRepository( String name,
+ RepositoryContext repositoryContext,
+ String configSource,
+ String configWorkspace,
+ String configPath ) throws RepositorySourceException {
+ // All the required properties have been set ...
+ ExecutionContext executionContext = repositoryContext.getExecutionContext();
+ RepositoryConnectionFactory connectionFactory = repositoryContext.getRepositoryConnectionFactory();
+ ValueFactories valueFactories = executionContext.getValueFactories();
+ ValueFactory<String> strings = valueFactories.getStringFactory();
+ ValueFactory<Long> longs = valueFactories.getLongFactory();
+ ProjectionParser projectionParser = ProjectionParser.getInstance();
+ NamespaceRegistry registry = executionContext.getNamespaceRegistry();
+
+ try {
+ Graph config = Graph.create(configSource, connectionFactory, executionContext);
+ if (configWorkspace != null) {
+ configWorkspace = config.useWorkspace(configWorkspace).getName();
+ } else {
+ configWorkspace = config.getCurrentWorkspaceName();
+ }
+
+ // Read the configuration for the federated repository:
+ // Level 1: the node representing the federated repository
+ // Level 2: the "dna:workspaces" node
+ // Level 3: a node for each workspace in the federated repository
+ // Level 4: the "dna:projections" nodes
+ // Level 5: a node below "dna:projections" for each projection, with properties for the source name,
+ // workspace name, cache expiration time, and projection rules
+ Subgraph repositories = config.getSubgraphOfDepth(5).at(configPath);
+
+ // Get the name of the default workspace ...
+ String defaultWorkspaceName = null;
+ Property defaultWorkspaceNameProperty = repositories.getRoot().getProperty(DnaLexicon.DEFAULT_WORKSPACE_NAME);
+ if (defaultWorkspaceNameProperty != null) {
+ // Set the name using the property if there is one ...
+ defaultWorkspaceName = strings.create(defaultWorkspaceNameProperty.getFirstValue());
+ }
+
+ // Get the default expiration time for the repository ...
+ CachePolicy defaultCachePolicy = null;
+ Property timeToExpire = repositories.getRoot().getProperty(DnaLexicon.TIME_TO_EXPIRE);
+ if (timeToExpire != null && !timeToExpire.isEmpty()) {
+ long timeToCacheInMillis = longs.create(timeToExpire.getFirstValue());
+ defaultCachePolicy = new BasicCachePolicy(timeToCacheInMillis, TimeUnit.MILLISECONDS).getUnmodifiable();
+ }
+
+ // Level 2: The "dna:workspaces" node ...
+ Node workspacesNode = repositories.getNode(DnaLexicon.WORKSPACES);
+ if (workspacesNode == null) {
+ I18n msg = GraphI18n.requiredNodeDoesNotExistRelativeToNode;
+ throw new RepositorySourceException(msg.text(DnaLexicon.WORKSPACES.getString(registry),
+ repositories.getLocation().getPath().getString(registry),
+ configWorkspace,
+ configSource));
+ }
+
+ // Level 3: The workspace nodes ...
+ LinkedList<FederatedWorkspace> workspaces = new LinkedList<FederatedWorkspace>();
+ for (Location workspace : workspacesNode) {
+
+ // Get the name of the workspace ...
+ String workspaceName = null;
+ SubgraphNode workspaceNode = repositories.getNode(workspace);
+ Property workspaceNameProperty = workspaceNode.getProperty(DnaLexicon.WORKSPACE_NAME);
+ if (workspaceNameProperty != null) {
+ // Set the name using the property if there is one ...
+ workspaceName = strings.create(workspaceNameProperty.getFirstValue());
+ }
+ if (workspaceName == null) {
+ // Otherwise, set the name using the local name of the workspace node ...
+ workspaceName = workspace.getPath().getLastSegment().getName().getLocalName();
+ }
+
+ // Level 4: the "dna:projections" node ...
+ Node projectionsNode = workspaceNode.getNode(DnaLexicon.PROJECTIONS);
+ if (projectionsNode == null) {
+ I18n msg = GraphI18n.requiredNodeDoesNotExistRelativeToNode;
+ throw new RepositorySourceException(getName(), msg.text(DnaLexicon.PROJECTIONS.getString(registry),
+ workspaceNode.getLocation()
+ .getPath()
+ .getString(registry),
+ configWorkspace,
+ configSource));
+ }
+
+ // Level 5: the projection nodes ...
+ List<Projection> sourceProjections = new LinkedList<Projection>();
+ for (Location projection : projectionsNode) {
+ Node projectionNode = repositories.getNode(projection);
+ sourceProjections.add(createProjection(executionContext, projectionParser, projectionNode));
+ }
+
+ // Create the federated workspace configuration ...
+ FederatedWorkspace space = new FederatedWorkspace(repositoryContext, name, workspaceName, sourceProjections,
+ defaultCachePolicy);
+ if (workspaceName.equals(defaultWorkspaceName)) {
+ workspaces.addFirst(space);
+ } else {
+ workspaces.add(space);
+ }
+ }
+
+ // Create the ExecutorService ...
+ ExecutorService executor = Executors.newCachedThreadPool();
+
+ return new FederatedRepository(name, connectionFactory, workspaces, defaultCachePolicy, executor);
+ } catch (RepositorySourceException t) {
+ throw t; // rethrow
+ } catch (Throwable t) {
+ I18n msg = GraphI18n.errorReadingConfigurationForFederatedRepositorySource;
+ throw new RepositorySourceException(getName(), msg.text(name, configSource, configWorkspace, configPath), t);
+ }
+ }
+
+ /**
+ * Instantiate the {@link Projection} described by the supplied properties.
+ *
+ * @param context the execution context that should be used to read the configuration; may not be null
+ * @param projectionParser the projection rule parser that should be used; may not be null
+ * @param node the node where these properties were found; never null
+ * @return the region instance, or null if it could not be created
+ */
+ protected Projection createProjection( ExecutionContext context,
+ ProjectionParser projectionParser,
+ Node node ) {
+ ValueFactory<String> strings = context.getValueFactories().getStringFactory();
+
+ Path path = node.getLocation().getPath();
+
+ // Get the source name from the local name of the node ...
+ String sourceName = path.getLastSegment().getName().getLocalName();
+ Property sourceNameProperty = node.getProperty(DnaLexicon.SOURCE_NAME);
+ if (sourceNameProperty != null && !sourceNameProperty.isEmpty()) {
+ // There is a "dna:sourceName" property, so use this instead ...
+ sourceName = strings.create(sourceNameProperty.getFirstValue());
+ }
+ assert sourceName != null;
+
+ // Get the workspace name ...
+ String workspaceName = null;
+ Property workspaceNameProperty = node.getProperty(DnaLexicon.WORKSPACE_NAME);
+ if (workspaceNameProperty != null && !workspaceNameProperty.isEmpty()) {
+ // There is a "dna:workspaceName" property, so use this instead ...
+ workspaceName = strings.create(workspaceNameProperty.getFirstValue());
+ }
+
+ // Get the projection rules ...
+ Projection.Rule[] projectionRules = null;
+ Property projectionRulesProperty = node.getProperty(DnaLexicon.PROJECTION_RULES);
+ if (projectionRulesProperty != null && !projectionRulesProperty.isEmpty()) {
+ String[] projectionRuleStrs = strings.create(projectionRulesProperty.getValuesAsArray());
+ if (projectionRuleStrs != null && projectionRuleStrs.length != 0) {
+ projectionRules = projectionParser.rulesFromStrings(context, projectionRuleStrs);
+ }
+ }
+
+ // Is this projection read-only?
+ boolean readOnly = false;
+ Property readOnlyProperty = node.getProperty(DnaLexicon.READ_ONLY);
+ if (readOnlyProperty != null && !readOnlyProperty.isEmpty()) {
+ readOnly = context.getValueFactories().getBooleanFactory().create(readOnlyProperty.getFirstValue());
+ }
+
+ return new Projection(sourceName, workspaceName, readOnly, projectionRules);
+ }
+
+}
Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/FederatedRepositorySource.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/FederatedRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/FederatedRequest.java (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/FederatedRequest.java 2009-05-18 15:36:58 UTC (rev 911)
@@ -0,0 +1,174 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * Unless otherwise indicated, all code in JBoss DNA is licensed
+ * to you 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.
+ *
+ * JBoss DNA 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.graph.connector.federation;
+
+import java.util.concurrent.CountDownLatch;
+import net.jcip.annotations.NotThreadSafe;
+import org.jboss.dna.graph.request.Request;
+
+/**
+ * A wrapper for a request submitted to the federated repository, and the corresponding source-specific {@link ProjectedRequest
+ * projected requests}.
+ */
+@NotThreadSafe
+class FederatedRequest {
+ private final Request original;
+ private CountDownLatch forkLatch;
+ private int incompleteCount;
+ private ProjectedRequest first;
+
+ FederatedRequest( Request original ) {
+ this.original = original;
+ }
+
+ public Request original() {
+ return original;
+ }
+
+ public final FederatedRequest add( Request request,
+ boolean isSameLocationAsOriginal,
+ boolean isComplete,
+ Projection projection,
+ Projection secondProjection ) {
+ if (!isComplete) ++incompleteCount;
+ if (first == null) {
+ if (isSameLocationAsOriginal) {
+ first = new MirrorRequest(request, isComplete, projection, secondProjection);
+ } else {
+ first = new ProjectedRequest(request, isComplete, projection, secondProjection);
+ }
+ } else {
+ first.addNext(request, isComplete, projection);
+ }
+ return this;
+ }
+
+ public final FederatedRequest add( Request request,
+ boolean isSameLocationAsOriginal,
+ boolean isComplete,
+ Projection projection ) {
+ return add(request, isSameLocationAsOriginal, isComplete, projection, null);
+ }
+
+ public void freeze() {
+ if (incompleteCount > 0 && forkLatch == null) {
+ forkLatch = new CountDownLatch(incompleteCount);
+ }
+ }
+
+ public ProjectedRequest getFirstProjectedRequest() {
+ return first;
+ }
+
+ public boolean hasIncompleteRequests() {
+ return incompleteCount != 0;
+ }
+
+ public CountDownLatch getLatch() {
+ return forkLatch;
+ }
+
+ public void await() throws InterruptedException {
+ if (forkLatch != null) forkLatch.await();
+ }
+
+ class ProjectedRequest {
+ private final Projection projection;
+ private final Projection projection2;
+ private final Request request;
+ private final boolean isComplete;
+ private ProjectedRequest next;
+
+ protected ProjectedRequest( Request request,
+ boolean isComplete,
+ Projection projection,
+ Projection secondProjection ) {
+ this.projection = projection;
+ this.request = request;
+ this.isComplete = isComplete;
+ this.projection2 = secondProjection;
+ }
+
+ public Projection getProjection() {
+ return projection;
+ }
+
+ public Projection getSecondProjection() {
+ return projection2;
+ }
+
+ public Request getRequest() {
+ return request;
+ }
+
+ public boolean isComplete() {
+ return isComplete;
+ }
+
+ public boolean isSameLocation() {
+ return false;
+ }
+
+ public ProjectedRequest next() {
+ return next;
+ }
+
+ public boolean hasNext() {
+ return next != null;
+ }
+
+ protected final ProjectedRequest addNext( Request request,
+ boolean isComplete,
+ Projection projection,
+ Projection secondProjection ) {
+ ProjectedRequest last = this;
+ while (last.next != null) {
+ last = last.next;
+ }
+ last.next = new ProjectedRequest(request, isComplete, projection, secondProjection);
+ return last.next;
+ }
+
+ protected final ProjectedRequest addNext( Request request,
+ boolean isComplete,
+ Projection projection ) {
+ return addNext(request, isComplete, projection, null);
+ }
+ }
+
+ class MirrorRequest extends ProjectedRequest {
+ protected MirrorRequest( Request request,
+ boolean isComplete,
+ Projection projection,
+ Projection secondProjection ) {
+ super(request, isComplete, projection, secondProjection);
+ }
+
+ @Override
+ public boolean isSameLocation() {
+ return true;
+ }
+ }
+
+}
Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/FederatedRequest.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/FederatedWorkspace.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/FederatedWorkspace.java (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/FederatedWorkspace.java 2009-05-18 15:36:58 UTC (rev 911)
@@ -0,0 +1,235 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * Unless otherwise indicated, all code in JBoss DNA is licensed
+ * to you 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.
+ *
+ * JBoss DNA 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.graph.connector.federation;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+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.CheckArg;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.Location;
+import org.jboss.dna.graph.cache.CachePolicy;
+import org.jboss.dna.graph.connector.RepositoryContext;
+
+/**
+ * The configuration of a single workspace within a {@link FederatedRepositorySource federated repository}.
+ */
+@Immutable
+class FederatedWorkspace {
+
+ private final RepositoryContext repositoryContext;
+ private final String sourceName;
+ private final String workspaceName;
+ private final List<Projection> projections;
+ private final Map<String, List<Projection>> projectionsBySourceName;
+ private final CachePolicy cachePolicy;
+ private final Problems problems;
+ private final Projector projector;
+
+ /**
+ * Create a configuration for a federated workspace.
+ *
+ * @param repositoryContext the repository context; may not be null
+ * @param sourceName the name of the federated repository; may not be null
+ * @param workspaceName the name of the federated workspace; may not be null
+ * @param projections the source projections; may not be null
+ * @param cachePolicy the cache policy for this workspace; may be null if there is no policy
+ * @throws IllegalArgumentException if the name is null or is blank
+ */
+ public FederatedWorkspace( RepositoryContext repositoryContext,
+ String sourceName,
+ String workspaceName,
+ Iterable<Projection> projections,
+ CachePolicy cachePolicy ) {
+ CheckArg.isNotNull(repositoryContext, "repositoryContext");
+ CheckArg.isNotNull(sourceName, "sourceName");
+ CheckArg.isNotNull(workspaceName, "workspaceName");
+ this.repositoryContext = repositoryContext;
+ this.workspaceName = workspaceName;
+ this.sourceName = sourceName;
+ this.cachePolicy = cachePolicy;
+ this.problems = new ThreadSafeProblems();
+ List<Projection> projectionList = new ArrayList<Projection>();
+ for (Projection projection : projections) {
+ if (projection == null) continue;
+ if (!projectionList.contains(projection)) {
+ projectionList.add(projection);
+ }
+ }
+ this.projections = Collections.unmodifiableList(projectionList);
+ CheckArg.isNotEmpty(this.projections, "sourceProjections");
+ this.projectionsBySourceName = new HashMap<String, List<Projection>>();
+ for (Projection projection : this.projections) {
+ String nameOfSource = projection.getSourceName();
+ List<Projection> projectionsForSource = projectionsBySourceName.get(nameOfSource);
+ if (projectionsForSource == null) {
+ projectionsForSource = new LinkedList<Projection>();
+ projectionsBySourceName.put(nameOfSource, projectionsForSource);
+ }
+ projectionsForSource.add(projection);
+ }
+
+ // Create the (most) appropriate projector ...
+ ExecutionContext context = this.repositoryContext.getExecutionContext();
+ Projector projector = MirrorProjector.with(context, projectionList);
+ if (projector == null) projector = BranchedMirrorProjector.with(context, projectionList);
+ if (projector == null) projector = OffsetMirrorProjector.with(context, projectionList);
+ if (projector == null) projector = GeneralProjector.with(context, projectionList);
+ assert projector != null;
+ this.projector = projector;
+ }
+
+ /**
+ * Get the repository context in which this workspace exists and has been initialized.
+ *
+ * @return the repository context
+ */
+ public RepositoryContext getRepositoryContext() {
+ return repositoryContext;
+ }
+
+ /**
+ * Get the name of the federated repository.
+ *
+ * @return sourceName
+ */
+ public String getSourceName() {
+ return sourceName;
+ }
+
+ /**
+ * Get the name of this repository
+ *
+ * @return name
+ */
+ public String getName() {
+ return this.workspaceName;
+ }
+
+ /**
+ * Get the cache policy for this workspace
+ *
+ * @return the workspace's cache policy; may be null
+ */
+ public CachePolicy getCachePolicy() {
+ return cachePolicy;
+ }
+
+ /**
+ * 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 source projections.
+ *
+ * @return the source projections; never null and never empty
+ */
+ public List<Projection> getProjections() {
+ return projections;
+ }
+
+ /**
+ * Determine whether this workspace has a projection supplied contribution
+ *
+ * @param sourceName the name of the source
+ * @param workspaceName the name of the workspace
+ * @return true if this workspace contains a projection that uses the supplied source and workspace
+ */
+ public boolean contains( String sourceName,
+ String workspaceName ) {
+ List<Projection> projections = this.projectionsBySourceName.get(sourceName);
+ if (projections != null) {
+ for (Projection projection : projections) {
+ if (projection.getWorkspaceName().equals(workspaceName)) return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Get the map of projections by their source name. This method provides direct access to the map used by this instance, and
+ * is mutable. This is meant to be called only by subclasses and tests.
+ *
+ * @return the list of projections for each source
+ */
+ Map<String, List<Projection>> getProjectionsBySourceName() {
+ return projectionsBySourceName;
+ }
+
+ /**
+ * Project the supplied location in the federated repository into the equivalent projected node(s).
+ *
+ * @param context the execution context in which the content is being accessed; may not be null
+ * @param location the location in the federated repository; may not be null
+ * @param requiresUpdate true if the operation for which this projection is needed will update the content in some way, or
+ * false if read-only operations will be performed
+ * @return the projected node, or null if the node does not exist in any projection
+ */
+ public ProjectedNode project( ExecutionContext context,
+ Location location,
+ boolean requiresUpdate ) {
+ return projector.project(context, location, requiresUpdate);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ return this.workspaceName.hashCode();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals( Object obj ) {
+ if (obj == this) return true;
+ if (obj instanceof FederatedWorkspace) {
+ FederatedWorkspace that = (FederatedWorkspace)obj;
+ if (!this.getSourceName().equals(that.getSourceName())) return false;
+ if (!this.getName().equals(that.getName())) return false;
+ if (!this.getProjections().equals(that.getProjections())) return false;
+ return true;
+ }
+ return false;
+ }
+
+}
Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/FederatedWorkspace.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/ForkRequestProcessor.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/ForkRequestProcessor.java (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/ForkRequestProcessor.java 2009-05-18 15:36:58 UTC (rev 911)
@@ -0,0 +1,1267 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * Unless otherwise indicated, all code in JBoss DNA is licensed
+ * to you 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.
+ *
+ * JBoss DNA 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.graph.connector.federation;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Queue;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.atomic.AtomicBoolean;
+import net.jcip.annotations.NotThreadSafe;
+import org.jboss.dna.common.i18n.I18n;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.GraphI18n;
+import org.jboss.dna.graph.Location;
+import org.jboss.dna.graph.connector.RepositoryConnection;
+import org.jboss.dna.graph.connector.RepositoryConnectionFactory;
+import org.jboss.dna.graph.connector.federation.FederatedRequest.ProjectedRequest;
+import org.jboss.dna.graph.property.DateTime;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.Path;
+import org.jboss.dna.graph.property.PathNotFoundException;
+import org.jboss.dna.graph.property.Property;
+import org.jboss.dna.graph.request.CloneWorkspaceRequest;
+import org.jboss.dna.graph.request.CompositeRequest;
+import org.jboss.dna.graph.request.CopyBranchRequest;
+import org.jboss.dna.graph.request.CreateNodeRequest;
+import org.jboss.dna.graph.request.CreateWorkspaceRequest;
+import org.jboss.dna.graph.request.DeleteBranchRequest;
+import org.jboss.dna.graph.request.DeleteChildrenRequest;
+import org.jboss.dna.graph.request.DestroyWorkspaceRequest;
+import org.jboss.dna.graph.request.GetWorkspacesRequest;
+import org.jboss.dna.graph.request.InvalidRequestException;
+import org.jboss.dna.graph.request.InvalidWorkspaceException;
+import org.jboss.dna.graph.request.MoveBranchRequest;
+import org.jboss.dna.graph.request.ReadAllChildrenRequest;
+import org.jboss.dna.graph.request.ReadAllPropertiesRequest;
+import org.jboss.dna.graph.request.ReadBranchRequest;
+import org.jboss.dna.graph.request.ReadNodeRequest;
+import org.jboss.dna.graph.request.ReadPropertyRequest;
+import org.jboss.dna.graph.request.Request;
+import org.jboss.dna.graph.request.UnsupportedRequestException;
+import org.jboss.dna.graph.request.UpdatePropertiesRequest;
+import org.jboss.dna.graph.request.VerifyNodeExistsRequest;
+import org.jboss.dna.graph.request.VerifyWorkspaceRequest;
+import org.jboss.dna.graph.request.processor.RequestProcessor;
+
+/**
+ * This is a {@link RequestProcessor} implementation that is responsible for forking each incoming request into the
+ * source-specific requests. This processor uses an {@link ExecutorService} to begin processing the forked requests immediately.
+ * As a result, while this processor processes the incoming requests on the federated content, the sources may already be
+ * processing previously forked (and source-specific) requests. Thus, it's quite possible that the sources finish processing their
+ * requests very shortly after this processor finishes its work.
+ * <p>
+ * This processor creates a separate channels for each source to which a request needs to be submitted. This channel submits all
+ * requests to that source via a single {@link RepositoryConnection#execute(ExecutionContext, Request) execute} call, meaning that
+ * all requests will be processed by the source within a single atomic operation. The channels also generally remain open until
+ * this processor completes processing of all incoming requests (that is, until all requests have been forked), so that any
+ * cancellation of this processor results in cancellation of each channel (and consequently the cancellation of the
+ * {@link CompositeRequest} that the channel submitted to its source).
+ * </p>
+ *
+ * @see FederatedRepositoryConnection#execute(ExecutionContext, Request)
+ * @see JoinRequestProcessor
+ */
+@NotThreadSafe
+class ForkRequestProcessor extends RequestProcessor {
+
+ /**
+ * A psuedo Request that is used by {@link Channel} to insert into a request queue so that the queue's iterator knows when
+ * there are no more requests to process.
+ */
+ protected static class LastRequest extends Request {
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ public boolean isReadOnly() {
+ return false;
+ }
+ }
+
+ /**
+ * A pseudo Request that allows addition of a request that, when processed, will decrement a latch. Since the latch is
+ * supplied by the submitter, this is useful when the submitter wishes to block until a particular request is processed. The
+ * submitter merely creates a {@link CountDownLatch}, submits their real request wrapped by a BlockedRequest, and calls
+ * {@link CountDownLatch#await()} on the latch. When <code>await()</code> returns, the first request has been completed.
+ *
+ * @see ForkRequestProcessor#submit(Request, String, CountDownLatch)
+ * @see ForkRequestProcessor#submitAndAwait(Request, String)
+ */
+ protected static class BlockedRequest extends Request {
+ private static final long serialVersionUID = 1L;
+ protected final Request original;
+ protected final transient CountDownLatch latch;
+
+ protected BlockedRequest( Request original,
+ CountDownLatch latch ) {
+ this.original = original;
+ this.latch = latch;
+ }
+
+ @Override
+ public boolean isReadOnly() {
+ return original.isReadOnly();
+ }
+
+ @Override
+ public boolean isCancelled() {
+ return original.isCancelled();
+ }
+ }
+
+ /**
+ * Represents the channel for a specific source into which this processor submits the requests for that source. To use, create
+ * a Channel, {@link Channel#start(ExecutorService, ExecutionContext, RepositoryConnectionFactory) start it}, and then
+ * {@link Channel#add(Request) add} requests (optionally with a {@link Channel#add(Request, CountDownLatch) latch} or via a
+ * {@link Channel#addAndAwait(Request) add and await}). Finally, call {@link Channel#done()} when there are no more requests.
+ * <p>
+ * When the channel is {@link Channel#start(ExecutorService, ExecutionContext, RepositoryConnectionFactory) started}, it
+ * creates a {@link Callable} and submits it to the supplied {@link ExecutorService}. (The resulting {@link Future} is then
+ * captured so that the channel can be {@link Channel#cancel(boolean) cancelled}.) The Callable obtains a
+ * {@link RepositoryConnection connection} to the channel's source, and then has the connection process a single
+ * {@link CompositeRequest} that fronts the queue of Request instances added to this channel. Because a blocking queue is
+ * used, the CompositeRequest's {@link CompositeRequest#iterator() iterator} blocks (on {@link Iterator#hasNext()}) until the
+ * next request is available. When {@link Channel#done()} is called, the iterator stops blocking and completes.
+ * </p>
+ */
+ protected static class Channel {
+ protected final String sourceName;
+ /** The list of all requests that are or have been processed as part of this channel */
+ protected final LinkedList<Request> allRequests = new LinkedList<Request>();
+ /** The queue of requests that remain unprocessed */
+ private final BlockingQueue<Request> queue = new LinkedBlockingQueue<Request>();
+ /** The CompositeRequest that is submitted to the underlying processor */
+ protected final CompositeRequest composite;
+ /** The Future that is submitted to the ExecutorService to do the processing */
+ protected Future<String> future;
+ /** Flag that defines whether the channel has processed all requests */
+ protected final AtomicBoolean done = new AtomicBoolean(false);
+
+ /**
+ * Create a new channel that operates against the supplied source.
+ *
+ * @param sourceName the name of the repository source used to execute this channel's {@link #allRequests() requests}; may
+ * not be null or empty
+ */
+ protected Channel( final String sourceName ) {
+ assert sourceName != null;
+ this.sourceName = sourceName;
+ this.composite = new CompositeRequest(false) {
+ private static final long serialVersionUID = 1L;
+ private final LinkedList<Request> allRequests = Channel.this.allRequests;
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.request.CompositeRequest#iterator()
+ */
+ @Override
+ public Iterator<Request> iterator() {
+ return createIterator();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.request.CompositeRequest#getRequests()
+ */
+ @Override
+ public List<Request> getRequests() {
+ return allRequests;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.request.CompositeRequest#size()
+ */
+ @Override
+ public int size() {
+ return done.get() ? allRequests.size() : CompositeRequest.UNKNOWN_NUMBER_OF_REQUESTS;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.request.Request#cancel()
+ */
+ @Override
+ public void cancel() {
+ done.set(true);
+ }
+ };
+ }
+
+ /**
+ * Utility method to create an iterator over the requests in this channel. This really should be called once
+ *
+ * @return the iterator over the channels
+ */
+ protected Iterator<Request> createIterator() {
+ final BlockingQueue<Request> queue = this.queue;
+ return new Iterator<Request>() {
+ private BlockedRequest previousBlocked;
+ private Request next;
+
+ public boolean hasNext() {
+ // If the previous request was a blocked request, then we need to decrement its latch ...
+ if (previousBlocked != null && previousBlocked.latch != null) {
+ previousBlocked.latch.countDown();
+ }
+
+ // If next still has a request, then 'hasNext()' has been called multiple times in a row
+ if (next != null) return true;
+
+ // Now, block for a next item (this blocks) ...
+ try {
+ next = queue.take();
+ } catch (InterruptedException e) {
+ // This happens when the federated connector has been told to shutdown now, and it shuts down
+ // its executor (the worker pool) immediately by interrupting each in-use thread.
+ // In this case, we should consider there to be more more requests ...
+ try {
+ return false;
+ } finally {
+ // reset the interrupted status ...
+ Thread.interrupted();
+ }
+ }
+ if (next instanceof LastRequest) {
+ return false;
+ }
+ return next != null;
+ }
+
+ public Request next() {
+ if (next == null) {
+ // Must have been called without first calling 'hasNext()' ...
+ try {
+ next = queue.take();
+ } catch (InterruptedException e) {
+ // This happens when the federated connector has been told to shutdown now, and it shuts down
+ // its executor (the worker pool) immediately by interrupting each in-use thread.
+ // In this case, we should consider there to be more more requests (again, this case
+ // is when 'next()' has been called without calling 'hasNext()') ...
+ try {
+ throw new NoSuchElementException();
+ } finally {
+ // reset the interrupted status ...
+ Thread.interrupted();
+ }
+ }
+ }
+ // If this request is a blocked request, we need to return the wrapped request and record the blocked request
+ if (next instanceof BlockedRequest) {
+ previousBlocked = (BlockedRequest)next;
+ next = null;
+ return previousBlocked.original;
+ }
+ previousBlocked = null;
+ if (next != null) {
+ Request result = next;
+ next = null;
+ return result;
+ }
+ throw new NoSuchElementException();
+ }
+
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+ };
+ }
+
+ /**
+ * Begins processing any requests that have been {@link #add(Request) added} to this channel. Processing is done by
+ * submitting the channel to the supplied executor.
+ *
+ * @param executor the executor that is to do the work; may not be null
+ * @param context the execution context in which the work is to be performed; may not be null
+ * @param connectionFactory the connection factory that should be used to create connections; may not be null
+ * @throws IllegalStateException if this channel has already been started
+ */
+ protected void start( final ExecutorService executor,
+ final ExecutionContext context,
+ final RepositoryConnectionFactory connectionFactory ) {
+ assert executor != null;
+ assert context != null;
+ assert connectionFactory != null;
+ if (this.future != null) {
+ throw new IllegalStateException();
+ }
+ this.future = executor.submit(new Callable<String>() {
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.util.concurrent.Callable#call()
+ */
+ public String call() throws Exception {
+ final RepositoryConnection connection = connectionFactory.createConnection(sourceName);
+ try {
+ connection.execute(context, composite);
+ } finally {
+ connection.close();
+ }
+ return sourceName;
+ }
+ });
+ }
+
+ /**
+ * Add the request to this channel for asynchronous processing. This method is called by the
+ * {@link ForkRequestProcessor#submit(Request, String)} method.
+ *
+ * @param request the request to be submitted; may not be null
+ * @throws IllegalStateException if this channel has already been marked as {@link #done()}
+ */
+ protected void add( Request request ) {
+ if (done.get()) {
+ throw new IllegalStateException(GraphI18n.unableToAddRequestToChannelThatIsDone.text(sourceName, request));
+ }
+ assert request != null;
+ this.allRequests.add(request);
+ this.queue.add(request);
+ }
+
+ /**
+ * Add the request to this channel for asynchronous processing, and supply a {@link CountDownLatch count-down latch} that
+ * should be {@link CountDownLatch#countDown() decremented} when this request is completed.
+ *
+ * @param request the request to be submitted; may not be null
+ * @param latch the count-down latch that should be decremented when <code>request</code> has been completed; may not be
+ * null
+ * @return the same latch that was supplied, for method chaining purposes; never null
+ * @throws IllegalStateException if this channel has already been marked as {@link #done()}
+ */
+ protected CountDownLatch add( Request request,
+ CountDownLatch latch ) {
+ if (done.get()) {
+ throw new IllegalStateException(GraphI18n.unableToAddRequestToChannelThatIsDone.text(sourceName, request));
+ }
+ assert request != null;
+ assert latch != null;
+ // Submit the request for processing ...
+ this.allRequests.add(request);
+ this.queue.add(new BlockedRequest(request, latch));
+ return latch;
+ }
+
+ /**
+ * Add the request to this channel for asynchronous processing, and supply a {@link CountDownLatch count-down latch} that
+ * should be {@link CountDownLatch#countDown() decremented} when this request is completed. This method is called by the
+ * {@link ForkRequestProcessor#submitAndAwait(Request, String)} method.
+ *
+ * @param request the request to be submitted; may not be null
+ * @throws InterruptedException if the current thread is interrupted while waiting
+ */
+ protected void addAndAwait( Request request ) throws InterruptedException {
+ // Add the request with a latch, then block until the request has completed ...
+ add(request, new CountDownLatch(1)).await();
+ }
+
+ /**
+ * Mark this source as having no more requests to process.
+ */
+ protected void done() {
+ this.done.set(true);
+ this.queue.add(new LastRequest());
+ }
+
+ /**
+ * Return whether this channel has been {@link #done() marked as done}.
+ *
+ * @return true if the channel was marked as done, or false otherwise
+ */
+ protected boolean isDone() {
+ return done.get();
+ }
+
+ /**
+ * Cancel this forked channel, stopping work as soon as possible. If the channel has not yet been started, this method
+ *
+ * @param mayInterruptIfRunning true if the channel is still being worked on, and the thread on which its being worked on
+ * may be interrupted, or false if the channel should be allowed to finish if it is already in work.
+ */
+ public void cancel( boolean mayInterruptIfRunning ) {
+ if (this.future == null || this.future.isDone() || this.future.isCancelled()) return;
+
+ // Mark the composite as cancelled first, so that the next composed request will be marked as
+ // cancelled.
+ this.composite.cancel();
+
+ // Now mark the channel as being done ...
+ done();
+
+ // Now, mark the channel as being cancelled (do allow interrupting the worker thread) ...
+ this.future.cancel(mayInterruptIfRunning);
+ }
+
+ /**
+ * Return whether this channel has been {@link #start(ExecutorService, ExecutionContext, RepositoryConnectionFactory)
+ * started}.
+ *
+ * @return true if this channel was started, or false otherwise
+ */
+ public boolean isStarted() {
+ return this.future != null;
+ }
+
+ /**
+ * Return whether this channel has completed all of its work.
+ *
+ * @return true if the channel was started and is complete, or false otherwise
+ */
+ public boolean isComplete() {
+ return this.future != null && this.future.isDone();
+ }
+
+ /**
+ * Await until this channel has completed.
+ *
+ * @throws CancellationException if the channel was cancelled
+ * @throws ExecutionException if the channel execution threw an exception
+ * @throws InterruptedException if the current thread is interrupted while waiting
+ */
+ protected void await() throws ExecutionException, InterruptedException, CancellationException {
+ this.future.get();
+ }
+
+ /**
+ * Get all the requests that were submitted to this queue. The resulting list is the actual list that is appended when
+ * requests are added, and may change until the channel is marked as {@link #done() done}.
+ *
+ * @return all of the requests that were submitted to this channel; never null
+ */
+ protected List<Request> allRequests() {
+ return allRequests;
+ }
+
+ /**
+ * Get the name of the source that this channel uses.
+ *
+ * @return the source name; never null
+ */
+ protected String sourceName() {
+ return sourceName;
+ }
+ }
+
+ private final FederatedRepository repository;
+ private final ExecutorService executor;
+ private final RepositoryConnectionFactory connectionFactory;
+ private final Map<String, Channel> channelBySourceName = new HashMap<String, Channel>();
+ private final Queue<FederatedRequest> federatedRequestQueue;
+
+ /**
+ * Create a new fork processor
+ *
+ * @param repository the federated repository configuration; never null
+ * @param context the execution context in which this processor is executing; may not be null
+ * @param now the timestamp representing the current time in UTC; may not be null
+ * @param federatedRequestQueue the queue into which should be placed the {@link FederatedRequest} objects created by this
+ * processor that still must be post-processed
+ */
+ public ForkRequestProcessor( FederatedRepository repository,
+ ExecutionContext context,
+ DateTime now,
+ Queue<FederatedRequest> federatedRequestQueue ) {
+ super(repository.getSourceName(), context, now);
+ this.repository = repository;
+ this.executor = this.repository.getExecutor();
+ this.connectionFactory = this.repository.getConnectionFactory();
+ this.federatedRequestQueue = federatedRequestQueue;
+ assert this.executor != null;
+ assert this.connectionFactory != null;
+ assert this.federatedRequestQueue != null;
+ }
+
+ /**
+ * Asynchronously submit a request to the supplied source. This is typically called when the forked requests are not needed
+ * before continuing.
+ *
+ * @param request the request to be submitted; may not be null
+ * @param sourceName the name of the source; may not be null
+ * @see #submitAndAwait(Request, String)
+ * @see #submit(Request, String, CountDownLatch)
+ */
+ protected void submit( Request request,
+ String sourceName ) {
+ assert request != null;
+ Channel channel = channelBySourceName.get(sourceName);
+ if (channel == null) {
+ channel = new Channel(sourceName);
+ channelBySourceName.put(sourceName, channel);
+ channel.start(executor, getExecutionContext(), connectionFactory);
+ }
+ channel.add(request);
+ }
+
+ /**
+ * Submit a request to the supplied source, and block until the request has been processed. This method is typically used when
+ * a federated request is forked into multiple source-specific requests, but the output of a source-specific request is
+ * required before forking other source-specific requests. This pattern is common in requests that update one source and any
+ * information not stored by that source needs to be stored in another source.
+ *
+ * @param request the request to be submitted; may not be null
+ * @param sourceName the name of the source; may not be null
+ * @throws InterruptedException if the current thread is interrupted while waiting
+ * @see #submit(Request, String)
+ * @see #submit(Request, String, CountDownLatch)
+ */
+ protected void submitAndAwait( Request request,
+ String sourceName ) throws InterruptedException {
+ assert request != null;
+ Channel channel = channelBySourceName.get(sourceName);
+ if (channel == null) {
+ channel = new Channel(sourceName);
+ channelBySourceName.put(sourceName, channel);
+ channel.start(executor, getExecutionContext(), connectionFactory);
+ }
+ channel.addAndAwait(request);
+ }
+
+ /**
+ * Submit a request to the supplied source, and have the supplied {@link CountDownLatch latch} be
+ * {@link CountDownLatch#countDown() decremented} when the request has been completed. Note that the same latch can be used
+ * multiple times.
+ * <p>
+ * This method is typically used when a federated request is forked into multiple source-specific requests, but the output of
+ * a source-specific request is required before forking other source-specific requests. This pattern is common in requests
+ * that update one source and any information not stored by that source needs to be stored in another source.
+ * </p>
+ *
+ * @param request the request to be submitted; may not be null
+ * @param sourceName the name of the source; may not be null
+ * @param latch the count-down latch; may not be null
+ * @see #submit(Request, String)
+ * @see #submitAndAwait(Request, String)
+ */
+ protected void submit( Request request,
+ String sourceName,
+ CountDownLatch latch ) {
+ assert request != null;
+ Channel channel = channelBySourceName.get(sourceName);
+ if (channel == null) {
+ channel = new Channel(sourceName);
+ channelBySourceName.put(sourceName, channel);
+ channel.start(executor, getExecutionContext(), connectionFactory);
+ }
+ channel.add(request, latch);
+ }
+
+ /**
+ * Await until all of this processor's channels have completed.
+ *
+ * @throws CancellationException if the channel was cancelled
+ * @throws ExecutionException if the channel execution threw an exception
+ * @throws InterruptedException if the current thread is interrupted while waiting
+ */
+ public void await() throws ExecutionException, InterruptedException, CancellationException {
+ for (Channel channel : channelBySourceName.values()) {
+ channel.await();
+ }
+ }
+
+ /**
+ * Utility to obtain the federated workspace referenced by the request. This method supports using the default workspace if
+ * the workspace name is null. If no such workspace, the request is marked with an appropriate error.
+ *
+ * @param request the request; may not be null
+ * @param workspaceName the name of the workspace; may be null if the default workspace should be used
+ * @return the federated workspace, or null if none was found
+ */
+ protected final FederatedWorkspace getWorkspace( Request request,
+ String workspaceName ) {
+ try {
+ return repository.getWorkspace(workspaceName);
+ } catch (InvalidWorkspaceException e) {
+ request.setError(e);
+ } catch (Throwable e) {
+ request.setError(e);
+ }
+ return null;
+ }
+
+ protected final String readable( Location location ) {
+ return location.getString(getExecutionContext().getNamespaceRegistry());
+ }
+
+ protected final String readable( Name name ) {
+ return name.getString(getExecutionContext().getNamespaceRegistry());
+ }
+
+ /**
+ * Utility method to project the specified location into the federated repository's sources.
+ *
+ * @param location the location that should be projected; may not be null
+ * @param workspaceName the name of the workspace, or null if the default workspace should be used
+ * @param request the request; may not be null
+ * @param requiresUpdate true if the operation for which this projection is needed will update the content in some way, or
+ * false if read-only operations will be performed
+ * @return the projected node, or null if there was no projected node (and an error was set on the request)
+ */
+ protected final ProjectedNode project( Location location,
+ String workspaceName,
+ Request request,
+ boolean requiresUpdate ) {
+ FederatedWorkspace workspace = getWorkspace(request, workspaceName);
+ if (workspace == null) return null;
+
+ ProjectedNode projectedNode = workspace.project(getExecutionContext(), location, requiresUpdate);
+ if (projectedNode == null) {
+ I18n msg = GraphI18n.locationCannotBeProjectedIntoWorkspaceAndSource;
+ Path root = getExecutionContext().getValueFactories().getPathFactory().createRootPath();
+ request.setError(new PathNotFoundException(location, root, msg.text(readable(location),
+ workspace.getName(),
+ repository.getSourceName())));
+ }
+ return projectedNode;
+ }
+
+ protected void submit( FederatedRequest request ) {
+ request.freeze();
+ if (request.hasIncompleteRequests()) {
+ // Submit the projected requests ...
+ ProjectedRequest projected = request.getFirstProjectedRequest();
+ while (projected != null) {
+ if (!projected.isComplete()) {
+ submit(projected.getRequest(), projected.getProjection().getSourceName(), request.getLatch());
+ }
+ projected = projected.next();
+ }
+ }
+ // Record this federated request ...
+ this.federatedRequestQueue.add(request);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.VerifyNodeExistsRequest)
+ */
+ @Override
+ public void process( VerifyNodeExistsRequest request ) {
+ // Figure out where this request is projected ...
+ ProjectedNode projectedNode = project(request.at(), request.inWorkspace(), request, false);
+ if (projectedNode == null) return;
+
+ // Create the federated request ...
+ FederatedRequest federatedRequest = new FederatedRequest(request);
+ while (projectedNode != null) {
+ if (projectedNode.isPlaceholder()) {
+ PlaceholderNode placeholder = projectedNode.asPlaceholder();
+ // Create a request and set the results ...
+ ReadNodeRequest placeholderRequest = new ReadNodeRequest(request.at(), request.inWorkspace());
+ placeholderRequest.addChildren(placeholder.children());
+ placeholderRequest.addProperties(placeholder.properties().values());
+ placeholderRequest.setActualLocationOfNode(placeholder.location());
+ federatedRequest.add(placeholderRequest, true, true, null);
+ } else if (projectedNode.isProxy()) {
+ ProxyNode proxy = projectedNode.asProxy();
+ // Create and submit a request for the projection ...
+ VerifyNodeExistsRequest pushDownRequest = new VerifyNodeExistsRequest(proxy.location(), proxy.workspaceName());
+ federatedRequest.add(pushDownRequest, proxy.isSameLocationAsOriginal(), false, proxy.projection());
+ }
+ projectedNode = projectedNode.next();
+ }
+ // Submit for processing ...
+ submit(federatedRequest);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.ReadNodeRequest)
+ */
+ @Override
+ public void process( ReadNodeRequest request ) {
+ // Figure out where this request is projected ...
+ ProjectedNode projectedNode = project(request.at(), request.inWorkspace(), request, false);
+ if (projectedNode == null) return;
+
+ // Create the federated request ...
+ FederatedRequest federatedRequest = new FederatedRequest(request);
+ while (projectedNode != null) {
+ if (projectedNode.isPlaceholder()) {
+ PlaceholderNode placeholder = projectedNode.asPlaceholder();
+ // Create a request and set the results ...
+ ReadNodeRequest placeholderRequest = new ReadNodeRequest(placeholder.location(), request.inWorkspace());
+ placeholderRequest.addChildren(placeholder.children());
+ placeholderRequest.addProperties(placeholder.properties().values());
+ placeholderRequest.setActualLocationOfNode(placeholder.location());
+ federatedRequest.add(placeholderRequest, true, true, null);
+ } else if (projectedNode.isProxy()) {
+ ProxyNode proxy = projectedNode.asProxy();
+ // Create and submit a request for the projection ...
+ ReadNodeRequest pushDownRequest = new ReadNodeRequest(proxy.location(), proxy.workspaceName());
+ federatedRequest.add(pushDownRequest, proxy.isSameLocationAsOriginal(), false, proxy.projection());
+ }
+ projectedNode = projectedNode.next();
+ }
+ // Submit for processing ...
+ submit(federatedRequest);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.ReadAllChildrenRequest)
+ */
+ @Override
+ public void process( ReadAllChildrenRequest request ) {
+ // Figure out where this request is projected ...
+ ProjectedNode projectedNode = project(request.of(), request.inWorkspace(), request, false);
+ if (projectedNode == null) return;
+
+ // Create the federated request ...
+ FederatedRequest federatedRequest = new FederatedRequest(request);
+ while (projectedNode != null) {
+ if (projectedNode.isPlaceholder()) {
+ PlaceholderNode placeholder = projectedNode.asPlaceholder();
+ // Create a request and set the results ...
+ ReadAllChildrenRequest placeholderRequest = new ReadAllChildrenRequest(placeholder.location(),
+ request.inWorkspace());
+ placeholderRequest.addChildren(placeholder.children());
+ placeholderRequest.setActualLocationOfNode(placeholder.location());
+ federatedRequest.add(placeholderRequest, true, true, null);
+ } else if (projectedNode.isProxy()) {
+ ProxyNode proxy = projectedNode.asProxy();
+ // Create and submit a request for the projection ...
+ ReadAllChildrenRequest pushDownRequest = new ReadAllChildrenRequest(proxy.location(), proxy.workspaceName());
+ federatedRequest.add(pushDownRequest, proxy.isSameLocationAsOriginal(), false, proxy.projection());
+ }
+ projectedNode = projectedNode.next();
+ }
+ // Submit for processing ...
+ submit(federatedRequest);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.ReadAllPropertiesRequest)
+ */
+ @Override
+ public void process( ReadAllPropertiesRequest request ) {
+ // Figure out where this request is projected ...
+ ProjectedNode projectedNode = project(request.at(), request.inWorkspace(), request, false);
+ if (projectedNode == null) return;
+
+ // Create the federated request ...
+ FederatedRequest federatedRequest = new FederatedRequest(request);
+ while (projectedNode != null) {
+ if (projectedNode.isPlaceholder()) {
+ PlaceholderNode placeholder = projectedNode.asPlaceholder();
+ // Create a request and set the results ...
+ ReadAllPropertiesRequest placeholderRequest = new ReadAllPropertiesRequest(placeholder.location(),
+ request.inWorkspace());
+ placeholderRequest.addProperties(placeholder.properties().values());
+ placeholderRequest.setActualLocationOfNode(placeholder.location());
+ federatedRequest.add(placeholderRequest, true, true, null);
+ } else if (projectedNode.isProxy()) {
+ ProxyNode proxy = projectedNode.asProxy();
+ // Create and submit a request for the projection ...
+ ReadAllPropertiesRequest pushDownRequest = new ReadAllPropertiesRequest(proxy.location(), proxy.workspaceName());
+ federatedRequest.add(pushDownRequest, proxy.isSameLocationAsOriginal(), false, proxy.projection());
+ }
+ projectedNode = projectedNode.next();
+ }
+ // Submit for processing ...
+ submit(federatedRequest);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.ReadPropertyRequest)
+ */
+ @Override
+ public void process( ReadPropertyRequest request ) {
+ // Figure out where this request is projected ...
+ ProjectedNode projectedNode = project(request.on(), request.inWorkspace(), request, false);
+ if (projectedNode == null) return;
+
+ // Create the federated request ...
+ FederatedRequest federatedRequest = new FederatedRequest(request);
+ while (projectedNode != null) {
+ if (projectedNode.isPlaceholder()) {
+ PlaceholderNode placeholder = projectedNode.asPlaceholder();
+ // Create a request and set the results ...
+ ReadPropertyRequest placeholderRequest = new ReadPropertyRequest(placeholder.location(), request.inWorkspace(),
+ request.named());
+ Property property = placeholder.properties().get(request.named());
+ placeholderRequest.setProperty(property);
+ placeholderRequest.setActualLocationOfNode(placeholder.location());
+ federatedRequest.add(placeholderRequest, true, true, null);
+ } else if (projectedNode.isProxy()) {
+ ProxyNode proxy = projectedNode.asProxy();
+ // Create and submit a request for the projection ...
+ ReadPropertyRequest pushDownRequest = new ReadPropertyRequest(proxy.location(), proxy.workspaceName(),
+ request.named());
+ federatedRequest.add(pushDownRequest, proxy.isSameLocationAsOriginal(), false, proxy.projection());
+ }
+ projectedNode = projectedNode.next();
+ }
+ // Submit for processing ...
+ submit(federatedRequest);
+ }
+
+ /**
+ * {@inheritDoc}
+ * <p>
+ * This implementation creates a single {@link FederatedRequest} and a {@link ReadBranchRequest} for each {@link ProxyNode}
+ * and one {@link ReadNodeRequest} for each {@link PlaceholderNode}.
+ * </p>
+ *
+ * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.ReadBranchRequest)
+ */
+ @Override
+ public void process( ReadBranchRequest request ) {
+ // Figure out where this request is projected ...
+ ProjectedNode projectedNode = project(request.at(), request.inWorkspace(), request, false);
+ if (projectedNode == null) return;
+
+ // Create the federated request ...
+ FederatedRequest federatedRequest = new FederatedRequest(request);
+ FederatedWorkspace workspace = getWorkspace(request, request.inWorkspace());
+
+ // And process the branch, creating ReadNodeRequests for each placeholder, and ReadBranchRequests for each proxy node...
+ if (request.maximumDepth() > 0) {
+ processBranch(federatedRequest, projectedNode, workspace, request.maximumDepth());
+ }
+
+ // Submit the requests for processing ...
+ submit(federatedRequest);
+ }
+
+ /**
+ * A method used recursively to add {@link ReadNodeRequest}s and {@link ReadBranchRequest}s for each
+ *
+ * @param federatedRequest
+ * @param projectedNode
+ * @param workspace
+ * @param maxDepth
+ */
+ protected void processBranch( FederatedRequest federatedRequest,
+ ProjectedNode projectedNode,
+ FederatedWorkspace workspace,
+ int maxDepth ) {
+ assert maxDepth > 0;
+ while (projectedNode != null) {
+ if (projectedNode.isPlaceholder()) {
+ PlaceholderNode placeholder = projectedNode.asPlaceholder();
+ // Create a request and set the results ...
+ ReadNodeRequest placeholderRequest = new ReadNodeRequest(placeholder.location(), workspace.getName());
+ placeholderRequest.addChildren(placeholder.children());
+ placeholderRequest.addProperties(placeholder.properties().values());
+ placeholderRequest.setActualLocationOfNode(placeholder.location());
+ federatedRequest.add(placeholderRequest, true, true, null);
+ if (maxDepth > 1) {
+ ExecutionContext context = getExecutionContext();
+ // For each child of the placeholder node ...
+ for (Location child : placeholder.children()) {
+ ProjectedNode projectedSubnode = workspace.project(context, child, false);
+ // Call recursively, but reduce the max depth
+ processBranch(federatedRequest, projectedSubnode, workspace, maxDepth - 1);
+ }
+ }
+ } else if (projectedNode.isProxy()) {
+ ProxyNode proxy = projectedNode.asProxy();
+ // Create and submit a request for the projection ...
+ ReadBranchRequest pushDownRequest = new ReadBranchRequest(proxy.location(), proxy.workspaceName(), maxDepth);
+ federatedRequest.add(pushDownRequest, proxy.isSameLocationAsOriginal(), false, proxy.projection());
+ }
+ projectedNode = projectedNode.next();
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.CreateNodeRequest)
+ */
+ @Override
+ public void process( CreateNodeRequest request ) {
+ // Figure out where this request is projected ...
+ ProjectedNode projectedNode = project(request.under(), request.inWorkspace(), request, true);
+ if (projectedNode == null) return;
+
+ // Create the federated request ...
+ FederatedRequest federatedRequest = new FederatedRequest(request);
+
+ // Any non-read request should be submitted to the first ProxyNode ...
+ while (projectedNode != null) {
+ if (projectedNode.isProxy()) {
+ ProxyNode proxy = projectedNode.asProxy();
+ // Create and submit a request for the projection ...
+ CreateNodeRequest pushDownRequest = new CreateNodeRequest(proxy.location(), proxy.workspaceName(),
+ request.named(), request.conflictBehavior(),
+ request.properties());
+ federatedRequest.add(pushDownRequest, proxy.isSameLocationAsOriginal(), false, proxy.projection());
+
+ // Submit the requests for processing and then STOP ...
+ submit(federatedRequest);
+ return;
+ }
+ assert projectedNode.isPlaceholder();
+ projectedNode = projectedNode.next();
+ }
+ // Unable to perform this create ...
+ String msg = GraphI18n.unableToCreateNodeUnderPlaceholder.text(readable(request.named()),
+ readable(request.under()),
+ request.inWorkspace(),
+ getSourceName());
+ request.setError(new UnsupportedRequestException(msg));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.UpdatePropertiesRequest)
+ */
+ @Override
+ public void process( UpdatePropertiesRequest request ) {
+ // Figure out where this request is projected ...
+ ProjectedNode projectedNode = project(request.on(), request.inWorkspace(), request, true);
+ if (projectedNode == null) return;
+
+ // Create the federated request ...
+ FederatedRequest federatedRequest = new FederatedRequest(request);
+
+ // Any non-read request should be submitted to the first ProxyNode ...
+ while (projectedNode != null) {
+ if (projectedNode.isProxy()) {
+ ProxyNode proxy = projectedNode.asProxy();
+ // Create and submit a request for the projection ...
+ UpdatePropertiesRequest pushDownRequest = new UpdatePropertiesRequest(proxy.location(), proxy.workspaceName(),
+ request.properties());
+ federatedRequest.add(pushDownRequest, proxy.isSameLocationAsOriginal(), false, proxy.projection());
+
+ // Submit the requests for processing and then STOP ...
+ submit(federatedRequest);
+ return;
+ }
+ assert projectedNode.isPlaceholder();
+ projectedNode = projectedNode.next();
+ }
+ // Unable to perform this update ...
+ String msg = GraphI18n.unableToUpdatePlaceholder.text(readable(request.on()), request.inWorkspace(), getSourceName());
+ request.setError(new UnsupportedRequestException(msg));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.DeleteBranchRequest)
+ */
+ @Override
+ public void process( DeleteBranchRequest request ) {
+ // Figure out where this request is projected ...
+ ProjectedNode projectedNode = project(request.at(), request.inWorkspace(), request, true);
+ if (projectedNode == null) return;
+
+ // Create the federated request ...
+ FederatedRequest federatedRequest = new FederatedRequest(request);
+
+ // A delete should be executed against any ProxyNode that applies ...
+ FederatedWorkspace workspace = getWorkspace(request, request.inWorkspace());
+ boolean submit = deleteBranch(federatedRequest, projectedNode, workspace, getExecutionContext());
+ if (submit) {
+ // Submit the requests for processing and then STOP ...
+ submit(federatedRequest);
+ } else {
+ // Unable to perform this delete ...
+ String msg = GraphI18n.unableToDeletePlaceholder.text(readable(request.at()), request.inWorkspace(), getSourceName());
+ request.setError(new UnsupportedRequestException(msg));
+ }
+ }
+
+ protected boolean deleteBranch( FederatedRequest federatedRequest,
+ ProjectedNode projectedNode,
+ FederatedWorkspace workspace,
+ ExecutionContext context ) {
+ boolean submit = false;
+ while (projectedNode != null) {
+ if (projectedNode.isProxy()) {
+ ProxyNode proxy = projectedNode.asProxy();
+ // Is this proxy represent the top level node of the projection?
+ if (proxy.isTopLevelNode()) {
+ // Then we want to delete everything *underneath* the node, but we don't want to delete
+ // the node itself since it is the node being projected and must exist in order for the
+ // projection to work.
+ DeleteChildrenRequest pushDownRequest = new DeleteChildrenRequest(proxy.location(), proxy.workspaceName());
+ federatedRequest.add(pushDownRequest, proxy.isSameLocationAsOriginal(), false, proxy.projection());
+ } else {
+ // Create and submit a request for the projection ...
+ DeleteBranchRequest pushDownRequest = new DeleteBranchRequest(proxy.location(), proxy.workspaceName());
+ federatedRequest.add(pushDownRequest, proxy.isSameLocationAsOriginal(), false, proxy.projection());
+ }
+ submit = true;
+ } else if (projectedNode.isPlaceholder()) {
+ PlaceholderNode placeholder = projectedNode.asPlaceholder();
+ // Create a delete for this placeholder, but mark it completed. This is needed to know
+ // which placeholders were being deleted.
+ DeleteBranchRequest delete = new DeleteBranchRequest(placeholder.location(), workspace.getName());
+ delete.setActualLocationOfNode(placeholder.location());
+ federatedRequest.add(delete, true, true, null);
+ // Create and submit a request for each proxy below this placeholder ...
+ // For each child of the placeholder node ...
+ for (Location child : placeholder.children()) {
+ ProjectedNode projectedSubnode = workspace.project(context, child, true);
+ // Call recursively ...
+ submit = deleteBranch(federatedRequest, projectedSubnode, workspace, context);
+ }
+ }
+ projectedNode = projectedNode.next();
+ }
+ return submit;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.CopyBranchRequest)
+ */
+ @Override
+ public void process( CopyBranchRequest request ) {
+ // Figure out where the 'from' is projected ...
+ ProjectedNode projectedFromNode = project(request.from(), request.fromWorkspace(), request, false);
+ if (projectedFromNode == null) return;
+ ProjectedNode projectedIntoNode = project(request.into(), request.intoWorkspace(), request, true);
+ if (projectedIntoNode == null) return;
+
+ // Limitation: only able to project the copy if the 'from' and 'into' are in the same source & projection ...
+ while (projectedFromNode != null) {
+ if (projectedFromNode.isProxy()) {
+ ProxyNode fromProxy = projectedFromNode.asProxy();
+ // Look for a projectedIntoNode that has the same source/projection ...
+ while (projectedIntoNode != null) {
+ if (projectedIntoNode.isProxy()) {
+ // Both are proxies, so compare the projection ...
+ ProxyNode intoProxy = projectedIntoNode.asProxy();
+ if (fromProxy.projection().getSourceName().equals(intoProxy.projection().getSourceName())) break;
+ }
+ projectedIntoNode = projectedIntoNode.next();
+ }
+ if (projectedIntoNode != null) break;
+ }
+ projectedFromNode = projectedFromNode.next();
+ }
+ if (projectedFromNode == null || projectedIntoNode == null) {
+ // The copy is not done within a single source ...
+ String msg = GraphI18n.copyLimitedToBeWithinSingleSource.text(readable(request.from()),
+ request.fromWorkspace(),
+ readable(request.into()),
+ request.intoWorkspace(),
+ getSourceName());
+ request.setError(new UnsupportedRequestException(msg));
+ return;
+ }
+
+ ProxyNode fromProxy = projectedFromNode.asProxy();
+ ProxyNode intoProxy = projectedIntoNode.asProxy();
+ assert fromProxy.projection().getSourceName().equals(intoProxy.projection().getSourceName());
+ boolean sameLocation = fromProxy.isSameLocationAsOriginal() && intoProxy.isSameLocationAsOriginal();
+
+ // Create the pushed-down request ...
+ CopyBranchRequest pushDown = new CopyBranchRequest(fromProxy.location(), fromProxy.workspaceName(), intoProxy.location(),
+ intoProxy.workspaceName(), request.desiredName(),
+ request.conflictBehavior());
+ // Create the federated request ...
+ FederatedRequest federatedRequest = new FederatedRequest(request);
+ federatedRequest.add(pushDown, sameLocation, false, fromProxy.projection(), intoProxy.projection());
+
+ // Submit the requests for processing and then STOP ...
+ submit(federatedRequest);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.MoveBranchRequest)
+ */
+ @Override
+ public void process( MoveBranchRequest request ) {
+ // Figure out where the 'from' is projected ...
+ ProjectedNode projectedFromNode = project(request.from(), request.inWorkspace(), request, true);
+ if (projectedFromNode == null) return;
+ ProjectedNode projectedIntoNode = project(request.into(), request.inWorkspace(), request, true);
+ if (projectedIntoNode == null) return;
+
+ // Limitation: only able to project the move if the 'from' and 'into' are in the same source & projection ...
+ while (projectedFromNode != null) {
+ if (projectedFromNode.isProxy()) {
+ ProxyNode fromProxy = projectedFromNode.asProxy();
+ // Look for a projectedIntoNode that has the same source/projection ...
+ while (projectedIntoNode != null) {
+ if (projectedIntoNode.isProxy()) {
+ // Both are proxies, so compare the projection ...
+ ProxyNode intoProxy = projectedIntoNode.asProxy();
+ if (fromProxy.projection().getSourceName().equals(intoProxy.projection().getSourceName())) break;
+ }
+ projectedIntoNode = projectedIntoNode.next();
+ }
+ if (projectedIntoNode != null) break;
+ }
+ projectedFromNode = projectedFromNode.next();
+ }
+ if (projectedFromNode == null || projectedIntoNode == null) {
+ // The move is not done within a single source ...
+ String msg = GraphI18n.moveLimitedToBeWithinSingleSource.text(readable(request.from()),
+ request.inWorkspace(),
+ readable(request.into()),
+ request.inWorkspace(),
+ getSourceName());
+ request.setError(new UnsupportedRequestException(msg));
+ return;
+ }
+
+ ProxyNode fromProxy = projectedFromNode.asProxy();
+ ProxyNode intoProxy = projectedIntoNode.asProxy();
+ assert fromProxy.projection().getSourceName().equals(intoProxy.projection().getSourceName());
+ boolean sameLocation = fromProxy.isSameLocationAsOriginal() && intoProxy.isSameLocationAsOriginal();
+
+ // Create the pushed-down request ...
+ MoveBranchRequest pushDown = new MoveBranchRequest(fromProxy.location(), intoProxy.location(), intoProxy.workspaceName(),
+ request.desiredName(), request.conflictBehavior());
+ // Create the federated request ...
+ FederatedRequest federatedRequest = new FederatedRequest(request);
+ federatedRequest.add(pushDown, sameLocation, false, fromProxy.projection(), intoProxy.projection());
+
+ // Submit the requests for processing and then STOP ...
+ submit(federatedRequest);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.VerifyWorkspaceRequest)
+ */
+ @Override
+ public void process( VerifyWorkspaceRequest request ) {
+ FederatedWorkspace workspace = getWorkspace(request, request.workspaceName());
+ if (workspace != null) {
+ request.setActualWorkspaceName(workspace.getName());
+
+ // Get the root location ...
+ Location root = Location.create(getExecutionContext().getValueFactories().getPathFactory().createRootPath());
+ ProjectedNode projectedNode = project(root, workspace.getName(), request, false);
+ if (projectedNode == null) return;
+
+ // Create the federated request ...
+ FederatedRequest federatedRequest = new FederatedRequest(request);
+ while (projectedNode != null) {
+ if (projectedNode.isPlaceholder()) {
+ PlaceholderNode placeholder = projectedNode.asPlaceholder();
+ // Create a request and set the results ...
+ VerifyNodeExistsRequest placeholderRequest = new VerifyNodeExistsRequest(root, workspace.getName());
+ placeholderRequest.setActualLocationOfNode(placeholder.location());
+ federatedRequest.add(placeholderRequest, true, true, null);
+ } else if (projectedNode.isProxy()) {
+ ProxyNode proxy = projectedNode.asProxy();
+ // Create and submit a request for the projection ...
+ VerifyNodeExistsRequest pushDownRequest = new VerifyNodeExistsRequest(proxy.location(), proxy.workspaceName());
+ federatedRequest.add(pushDownRequest, proxy.isSameLocationAsOriginal(), false, proxy.projection());
+ }
+ projectedNode = projectedNode.next();
+ }
+ // Submit for processing ...
+ submit(federatedRequest);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.GetWorkspacesRequest)
+ */
+ @Override
+ public void process( GetWorkspacesRequest request ) {
+ request.setAvailableWorkspaceNames(repository.getWorkspaceNames());
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.CreateWorkspaceRequest)
+ */
+ @Override
+ public void process( CreateWorkspaceRequest request ) {
+ String msg = GraphI18n.federatedSourceDoesNotSupportCreatingWorkspaces.text(getSourceName());
+ request.setError(new InvalidRequestException(msg));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.CloneWorkspaceRequest)
+ */
+ @Override
+ public void process( CloneWorkspaceRequest request ) {
+ String msg = GraphI18n.federatedSourceDoesNotSupportCloningWorkspaces.text(getSourceName());
+ request.setError(new InvalidRequestException(msg));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.DestroyWorkspaceRequest)
+ */
+ @Override
+ public void process( DestroyWorkspaceRequest request ) {
+ String msg = GraphI18n.federatedSourceDoesNotSupportDestroyingWorkspaces.text(getSourceName());
+ request.setError(new InvalidRequestException(msg));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.request.processor.RequestProcessor#close()
+ */
+ @Override
+ public void close() {
+ super.close();
+ for (Channel channel : channelBySourceName.values()) {
+ channel.done();
+ }
+ }
+
+ protected void cancel( boolean mayInterruptIfRunning ) {
+ for (Channel channel : channelBySourceName.values()) {
+ channel.cancel(mayInterruptIfRunning);
+ }
+ }
+
+}
Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/ForkRequestProcessor.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/GeneralProjector.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/GeneralProjector.java (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/GeneralProjector.java 2009-05-18 15:36:58 UTC (rev 911)
@@ -0,0 +1,121 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ *
+ * JBoss DNA 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.graph.connector.federation;
+
+import java.util.List;
+import java.util.Set;
+import net.jcip.annotations.Immutable;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.Location;
+import org.jboss.dna.graph.property.Path;
+import org.jboss.dna.graph.property.PathFactory;
+
+/**
+ * A Projector for federated repository configurations that are an offset, direct one-for-one mirror against a single source
+ * repository that is projected below the federated root. In other words, the federated repository has a single projection with a
+ * single "/something/below/root => /" rule.
+ */
+@Immutable
+final class GeneralProjector extends ProjectorWithPlaceholders {
+
+ /**
+ * Attempt to create an instance of the {@link GeneralProjector} with the supplied projections using the supplied context.
+ *
+ * @param context the context; may not be null
+ * @param projections the projections in the federated repository; may not be null
+ * @return the offset mirror projector, or null if the projections didn't match the criteria for such a projector
+ */
+ static GeneralProjector with( ExecutionContext context,
+ List<Projection> projections ) {
+ assert projections != null;
+ assert context != null;
+ return new GeneralProjector(context, projections);
+ }
+
+ private final List<Projection> projections;
+
+ private GeneralProjector( ExecutionContext context,
+ List<Projection> projections ) {
+ super(context, projections);
+ this.projections = projections;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.connector.federation.Projector#project(ExecutionContext, Location, boolean)
+ */
+ public ProjectedNode project( ExecutionContext context,
+ Location location,
+ boolean requiresUpdate ) {
+ // Find the location of the desired node ...
+ Path path = location.getPath();
+ if (path == null) {
+ // There are only identification properties, so return a ProxyNode for each source/workspace ...
+ ProjectedNode result = null;
+ for (Projection projection : projections) {
+ if (requiresUpdate && projection.isReadOnly()) continue;
+ ProxyNode proxy = new ProxyNode(projection, location, location, false);
+ if (result == null) {
+ result = proxy;
+ } else {
+ result.add(proxy);
+ }
+ }
+ return result;
+ }
+ // We have a path, so see if the path is one of the existing placeholder nodes ...
+ ProjectedNode result = isPlaceholder(location);
+ if (result != null) return result;
+
+ // Otherwise this node lives below one (or more) of the projections, and we figure this out each time ...
+ final PathFactory pathFactory = context.getValueFactories().getPathFactory();
+ for (Projection projection : projections) {
+ if (requiresUpdate && projection.isReadOnly()) continue;
+ // Project the federated repository path into the paths as they would exist in the source ...
+ Set<Path> pathsInSource = projection.getPathsInSource(path, pathFactory);
+ for (Path pathInSource : pathsInSource) {
+ // Create a ProxyNode for this projection ...
+ Location locationInSource = Location.create(pathInSource);
+ boolean samePath = pathInSource.equals(path);
+ ProxyNode proxy = new ProxyNode(projection, locationInSource, location, samePath);
+ if (result == null) {
+ result = proxy;
+ } else {
+ result.add(proxy);
+ }
+ }
+ }
+
+ // If there is only one projection ...
+ if (result != null && !result.hasNext() && result.isProxy()) {
+ // Then we want to include the supplied location's ID properties down to the source ...
+ ProxyNode proxy = result.asProxy();
+ result = new ProxyNode(proxy.projection(), location.with(result.location().getPath()), location,
+ proxy.isSameLocationAsOriginal());
+ }
+ return result;
+ }
+
+}
Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/GeneralProjector.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/JoinMirrorRequestProcessor.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/JoinMirrorRequestProcessor.java (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/JoinMirrorRequestProcessor.java 2009-05-18 15:36:58 UTC (rev 911)
@@ -0,0 +1,391 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ *
+ * JBoss DNA 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.graph.connector.federation;
+
+import java.util.List;
+import java.util.Map;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.Location;
+import org.jboss.dna.graph.cache.CachePolicy;
+import org.jboss.dna.graph.property.DateTime;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.Property;
+import org.jboss.dna.graph.request.CacheableRequest;
+import org.jboss.dna.graph.request.CloneWorkspaceRequest;
+import org.jboss.dna.graph.request.CopyBranchRequest;
+import org.jboss.dna.graph.request.CreateNodeRequest;
+import org.jboss.dna.graph.request.CreateWorkspaceRequest;
+import org.jboss.dna.graph.request.DeleteBranchRequest;
+import org.jboss.dna.graph.request.DestroyWorkspaceRequest;
+import org.jboss.dna.graph.request.GetWorkspacesRequest;
+import org.jboss.dna.graph.request.MoveBranchRequest;
+import org.jboss.dna.graph.request.ReadAllChildrenRequest;
+import org.jboss.dna.graph.request.ReadAllPropertiesRequest;
+import org.jboss.dna.graph.request.ReadBlockOfChildrenRequest;
+import org.jboss.dna.graph.request.ReadBranchRequest;
+import org.jboss.dna.graph.request.ReadNextBlockOfChildrenRequest;
+import org.jboss.dna.graph.request.ReadNodeRequest;
+import org.jboss.dna.graph.request.ReadPropertyRequest;
+import org.jboss.dna.graph.request.RemovePropertyRequest;
+import org.jboss.dna.graph.request.RenameNodeRequest;
+import org.jboss.dna.graph.request.Request;
+import org.jboss.dna.graph.request.SetPropertyRequest;
+import org.jboss.dna.graph.request.UpdatePropertiesRequest;
+import org.jboss.dna.graph.request.VerifyNodeExistsRequest;
+import org.jboss.dna.graph.request.VerifyWorkspaceRequest;
+import org.jboss.dna.graph.request.processor.RequestProcessor;
+
+/**
+ * A RequestProcessor used by the {@link JoinRequestProcessor} in cases where there is a single source-request with the same
+ * location(s). This processor merely copies the results from each source request for the
+ * {@link #setFederatedRequest(FederatedRequest) current federated request} into the request passed as a parameter.
+ */
+class JoinMirrorRequestProcessor extends RequestProcessor {
+
+ private FederatedRequest federatedRequest;
+
+ JoinMirrorRequestProcessor( String sourceName,
+ ExecutionContext context,
+ DateTime now,
+ CachePolicy defaultCachePolicy ) {
+ super(sourceName, context, now, defaultCachePolicy);
+
+ }
+
+ /**
+ * @param federatedRequest Sets federatedRequest to the specified value.
+ */
+ void setFederatedRequest( FederatedRequest federatedRequest ) {
+ this.federatedRequest = federatedRequest;
+ }
+
+ protected boolean checkErrorOrCancel( Request request,
+ Request sourceRequest ) {
+ if (sourceRequest.hasError()) {
+ request.setError(sourceRequest.getError());
+ return true;
+ }
+ if (sourceRequest.isCancelled()) {
+ request.cancel();
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.ReadNodeRequest)
+ */
+ @Override
+ public void process( ReadNodeRequest request ) {
+ ReadNodeRequest source = (ReadNodeRequest)federatedRequest.getFirstProjectedRequest().getRequest();
+ if (checkErrorOrCancel(request, source)) return;
+ request.setActualLocationOfNode(source.getActualLocationOfNode());
+ for (Location childInSource : source.getChildren()) {
+ request.addChild(childInSource);
+ }
+ for (Property propertyInSource : source.getProperties()) {
+ request.addProperties(propertyInSource);
+ }
+ request.setCachePolicy(getDefaultCachePolicy());
+ setCacheableInfo(request, source.getCachePolicy());
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.ReadAllChildrenRequest)
+ */
+ @Override
+ public void process( ReadAllChildrenRequest request ) {
+ ReadAllChildrenRequest source = (ReadAllChildrenRequest)federatedRequest.getFirstProjectedRequest().getRequest();
+ if (checkErrorOrCancel(request, source)) return;
+ request.setActualLocationOfNode(source.getActualLocationOfNode());
+ for (Location childInSource : source.getChildren()) {
+ request.addChild(childInSource);
+ }
+ request.setCachePolicy(getDefaultCachePolicy());
+ setCacheableInfo(request, source.getCachePolicy());
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.ReadAllPropertiesRequest)
+ */
+ @Override
+ public void process( ReadAllPropertiesRequest request ) {
+ ReadAllPropertiesRequest source = (ReadAllPropertiesRequest)federatedRequest.getFirstProjectedRequest().getRequest();
+ if (checkErrorOrCancel(request, source)) return;
+ request.setActualLocationOfNode(source.getActualLocationOfNode());
+ for (Property propertyInSource : source.getProperties()) {
+ request.addProperties(propertyInSource);
+ }
+ request.setCachePolicy(getDefaultCachePolicy());
+ setCacheableInfo(request, source.getCachePolicy());
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.ReadBlockOfChildrenRequest)
+ */
+ @Override
+ public void process( ReadBlockOfChildrenRequest request ) {
+ ReadBlockOfChildrenRequest source = (ReadBlockOfChildrenRequest)federatedRequest.getFirstProjectedRequest().getRequest();
+ if (checkErrorOrCancel(request, source)) return;
+ request.setActualLocationOfNode(source.getActualLocationOfNode());
+ for (Location childInSource : source.getChildren()) {
+ request.addChild(childInSource);
+ }
+ request.setCachePolicy(getDefaultCachePolicy());
+ setCacheableInfo(request, source.getCachePolicy());
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.ReadNextBlockOfChildrenRequest)
+ */
+ @Override
+ public void process( ReadNextBlockOfChildrenRequest request ) {
+ ReadNextBlockOfChildrenRequest source = (ReadNextBlockOfChildrenRequest)federatedRequest.getFirstProjectedRequest().getRequest();
+ if (checkErrorOrCancel(request, source)) return;
+ request.setActualLocationOfStartingAfterNode(source.getActualLocationOfStartingAfterNode());
+ for (Location childInSource : source.getChildren()) {
+ request.addChild(childInSource);
+ }
+ request.setCachePolicy(getDefaultCachePolicy());
+ setCacheableInfo(request, source.getCachePolicy());
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.ReadPropertyRequest)
+ */
+ @Override
+ public void process( ReadPropertyRequest request ) {
+ ReadPropertyRequest source = (ReadPropertyRequest)federatedRequest.getFirstProjectedRequest().getRequest();
+ if (checkErrorOrCancel(request, source)) return;
+ request.setActualLocationOfNode(source.getActualLocationOfNode());
+ request.setProperty(source.getProperty());
+ request.setCachePolicy(getDefaultCachePolicy());
+ setCacheableInfo(request, source.getCachePolicy());
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.SetPropertyRequest)
+ */
+ @Override
+ public void process( SetPropertyRequest request ) {
+ SetPropertyRequest source = (SetPropertyRequest)federatedRequest.getFirstProjectedRequest().getRequest();
+ if (checkErrorOrCancel(request, source)) return;
+ request.setActualLocationOfNode(source.getActualLocationOfNode());
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.VerifyNodeExistsRequest)
+ */
+ @Override
+ public void process( VerifyNodeExistsRequest request ) {
+ VerifyNodeExistsRequest source = (VerifyNodeExistsRequest)federatedRequest.getFirstProjectedRequest().getRequest();
+ if (checkErrorOrCancel(request, source)) return;
+ request.setActualLocationOfNode(source.getActualLocationOfNode());
+ request.setCachePolicy(getDefaultCachePolicy());
+ setCacheableInfo(request, source.getCachePolicy());
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.RemovePropertyRequest)
+ */
+ @Override
+ public void process( RemovePropertyRequest request ) {
+ RemovePropertyRequest source = (RemovePropertyRequest)federatedRequest.getFirstProjectedRequest().getRequest();
+ if (checkErrorOrCancel(request, source)) return;
+ request.setActualLocationOfNode(source.getActualLocationOfNode());
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.RenameNodeRequest)
+ */
+ @Override
+ public void process( RenameNodeRequest request ) {
+ RenameNodeRequest source = (RenameNodeRequest)federatedRequest.getFirstProjectedRequest().getRequest();
+ if (checkErrorOrCancel(request, source)) return;
+ request.setActualLocations(source.getActualLocationBefore(), source.getActualLocationAfter());
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.ReadBranchRequest)
+ */
+ @Override
+ public void process( ReadBranchRequest request ) {
+ CacheableRequest source = (CacheableRequest)federatedRequest.getFirstProjectedRequest().getRequest();
+ if (checkErrorOrCancel(request, source)) return;
+ if (source instanceof ReadBranchRequest) {
+ ReadBranchRequest readSource = (ReadBranchRequest)source;
+ request.setActualLocationOfNode(readSource.getActualLocationOfNode());
+ for (Location node : readSource) {
+ List<Location> children = readSource.getChildren(node);
+ if (children != null) request.setChildren(node, children);
+ Map<Name, Property> props = readSource.getPropertiesFor(node);
+ if (props != null) request.setProperties(node, props.values());
+ }
+ } else if (source instanceof ReadNodeRequest) {
+ ReadNodeRequest readSource = (ReadNodeRequest)source;
+ request.setActualLocationOfNode(readSource.getActualLocationOfNode());
+ Location parent = readSource.getActualLocationOfNode();
+ request.setChildren(parent, readSource.getChildren());
+ request.setProperties(parent, readSource.getPropertiesByName().values());
+ }
+ request.setCachePolicy(getDefaultCachePolicy());
+ setCacheableInfo(request, source.getCachePolicy());
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.CreateNodeRequest)
+ */
+ @Override
+ public void process( CreateNodeRequest request ) {
+ CreateNodeRequest source = (CreateNodeRequest)federatedRequest.getFirstProjectedRequest().getRequest();
+ if (checkErrorOrCancel(request, source)) return;
+ request.setActualLocationOfNode(source.getActualLocationOfNode());
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.UpdatePropertiesRequest)
+ */
+ @Override
+ public void process( UpdatePropertiesRequest request ) {
+ UpdatePropertiesRequest source = (UpdatePropertiesRequest)federatedRequest.getFirstProjectedRequest().getRequest();
+ if (checkErrorOrCancel(request, source)) return;
+ request.setActualLocationOfNode(source.getActualLocationOfNode());
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.CopyBranchRequest)
+ */
+ @Override
+ public void process( CopyBranchRequest request ) {
+ CopyBranchRequest source = (CopyBranchRequest)federatedRequest.getFirstProjectedRequest().getRequest();
+ if (checkErrorOrCancel(request, source)) return;
+ request.setActualLocations(source.getActualLocationBefore(), source.getActualLocationAfter());
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.DeleteBranchRequest)
+ */
+ @Override
+ public void process( DeleteBranchRequest request ) {
+ DeleteBranchRequest source = (DeleteBranchRequest)federatedRequest.getFirstProjectedRequest().getRequest();
+ if (checkErrorOrCancel(request, source)) return;
+ request.setActualLocationOfNode(source.getActualLocationOfNode());
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.MoveBranchRequest)
+ */
+ @Override
+ public void process( MoveBranchRequest request ) {
+ MoveBranchRequest source = (MoveBranchRequest)federatedRequest.getFirstProjectedRequest().getRequest();
+ if (checkErrorOrCancel(request, source)) return;
+ request.setActualLocations(source.getActualLocationBefore(), source.getActualLocationAfter());
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.VerifyWorkspaceRequest)
+ */
+ @Override
+ public void process( VerifyWorkspaceRequest request ) {
+ VerifyNodeExistsRequest source = (VerifyNodeExistsRequest)federatedRequest.getFirstProjectedRequest().getRequest();
+ if (checkErrorOrCancel(request, source)) return;
+ assert source.getActualLocationOfNode().getPath().isRoot();
+ request.setActualRootLocation(source.getActualLocationOfNode());
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.GetWorkspacesRequest)
+ */
+ @Override
+ public void process( GetWorkspacesRequest request ) {
+ throw new UnsupportedOperationException(); // should never be called
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.CreateWorkspaceRequest)
+ */
+ @Override
+ public void process( CreateWorkspaceRequest request ) {
+ throw new UnsupportedOperationException(); // should never be called
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.CloneWorkspaceRequest)
+ */
+ @Override
+ public void process( CloneWorkspaceRequest request ) {
+ throw new UnsupportedOperationException(); // should never be called
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.DestroyWorkspaceRequest)
+ */
+ @Override
+ public void process( DestroyWorkspaceRequest request ) {
+ throw new UnsupportedOperationException(); // should never be called
+ }
+
+}
Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/JoinMirrorRequestProcessor.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/JoinRequestProcessor.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/JoinRequestProcessor.java (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/JoinRequestProcessor.java 2009-05-18 15:36:58 UTC (rev 911)
@@ -0,0 +1,981 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * Unless otherwise indicated, all code in JBoss DNA is licensed
+ * to you 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.
+ *
+ * JBoss DNA 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.graph.connector.federation;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.concurrent.BlockingQueue;
+import net.jcip.annotations.NotThreadSafe;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.GraphI18n;
+import org.jboss.dna.graph.Location;
+import org.jboss.dna.graph.connector.federation.FederatedRequest.ProjectedRequest;
+import org.jboss.dna.graph.property.DateTime;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.Path;
+import org.jboss.dna.graph.property.PathFactory;
+import org.jboss.dna.graph.property.PathNotFoundException;
+import org.jboss.dna.graph.property.Property;
+import org.jboss.dna.graph.property.PropertyFactory;
+import org.jboss.dna.graph.property.ValueComparators;
+import org.jboss.dna.graph.request.CacheableRequest;
+import org.jboss.dna.graph.request.CloneWorkspaceRequest;
+import org.jboss.dna.graph.request.CopyBranchRequest;
+import org.jboss.dna.graph.request.CreateNodeRequest;
+import org.jboss.dna.graph.request.CreateWorkspaceRequest;
+import org.jboss.dna.graph.request.DeleteBranchRequest;
+import org.jboss.dna.graph.request.DeleteChildrenRequest;
+import org.jboss.dna.graph.request.DestroyWorkspaceRequest;
+import org.jboss.dna.graph.request.GetWorkspacesRequest;
+import org.jboss.dna.graph.request.InvalidRequestException;
+import org.jboss.dna.graph.request.MoveBranchRequest;
+import org.jboss.dna.graph.request.ReadAllChildrenRequest;
+import org.jboss.dna.graph.request.ReadAllPropertiesRequest;
+import org.jboss.dna.graph.request.ReadBranchRequest;
+import org.jboss.dna.graph.request.ReadNodeRequest;
+import org.jboss.dna.graph.request.ReadPropertyRequest;
+import org.jboss.dna.graph.request.Request;
+import org.jboss.dna.graph.request.UpdatePropertiesRequest;
+import org.jboss.dna.graph.request.VerifyNodeExistsRequest;
+import org.jboss.dna.graph.request.VerifyWorkspaceRequest;
+import org.jboss.dna.graph.request.processor.RequestProcessor;
+
+/**
+ *
+ */
+@NotThreadSafe
+class JoinRequestProcessor extends RequestProcessor {
+
+ private final PathFactory pathFactory;
+ private final PropertyFactory propertyFactory;
+ private final JoinMirrorRequestProcessor mirrorProcessor;
+ protected FederatedRequest federatedRequest;
+
+ /**
+ * Create a new join processor
+ *
+ * @param repository the federated repository configuration; never null
+ * @param context the execution context in which this processor is executing; may not be null
+ * @param now the timestamp representing the current time in UTC; may not be null
+ */
+ public JoinRequestProcessor( FederatedRepository repository,
+ ExecutionContext context,
+ DateTime now ) {
+ super(repository.getSourceName(), context, now, repository.getDefaultCachePolicy());
+ // this.repository = repository;
+ this.propertyFactory = context.getPropertyFactory();
+ this.pathFactory = context.getValueFactories().getPathFactory();
+ this.mirrorProcessor = new JoinMirrorRequestProcessor(repository.getSourceName(), context, now,
+ repository.getDefaultCachePolicy());
+ }
+
+ /**
+ * Process all of the {@link FederatedRequest} objects that are in the supplied collection.
+ *
+ * @param completedFederatedRequests the collection of {@link FederatedRequest} whose projected requests have already been
+ * processed; may not be null
+ * @see FederatedRepositoryConnection#execute(ExecutionContext, org.jboss.dna.graph.request.Request)
+ */
+ public void process( final Iterable<FederatedRequest> completedFederatedRequests ) {
+ for (FederatedRequest federatedRequest : completedFederatedRequests) {
+ // No need to await for the forked request, since it will be done
+ this.federatedRequest = federatedRequest;
+ process(federatedRequest.original());
+ }
+ }
+
+ /**
+ * Process the {@link FederatedRequest} objects that are in the supplied queue. The queue contains {@link FederatedRequest}
+ * that may have projected requests that have not yet been processed by the respective source, so this method
+ * {@link FederatedRequest#await() waits} until all source requests have been processed. This method returns only when it
+ * obtains from the queue a {@link NoMoreFederatedRequests} instance.
+ *
+ * @param federatedRequestQueue the queue containing the federated requests; may not be null
+ * @see FederatedRepositoryConnection#execute(ExecutionContext, org.jboss.dna.graph.request.Request)
+ */
+ public void process( final BlockingQueue<FederatedRequest> federatedRequestQueue ) {
+ FederatedRequest forked = null;
+ try {
+ for (;;) {
+ forked = federatedRequestQueue.take();
+ if (forked instanceof NoMoreFederatedRequests) return;
+ forked.await();
+
+ // Determine whether this is a single mirror request ...
+ Request original = forked.original();
+ ProjectedRequest projectedRequest = forked.getFirstProjectedRequest();
+ boolean sameLocation = projectedRequest != null && !projectedRequest.hasNext()
+ && projectedRequest.isSameLocation();
+
+ // Set the cachable information ...
+ if (original instanceof CacheableRequest) {
+ CacheableRequest cacheableOriginal = (CacheableRequest)original;
+ cacheableOriginal.setCachePolicy(getDefaultCachePolicy());
+ while (projectedRequest != null) {
+ Request requestToSource = projectedRequest.getRequest();
+ if (cacheableOriginal != null) {
+ setCacheableInfo(cacheableOriginal, ((CacheableRequest)requestToSource).getCachePolicy());
+ }
+ projectedRequest = projectedRequest.next();
+ }
+ }
+
+ // Now do the join on this request ...
+ if (sameLocation) {
+ Request sourceRequest = forked.getFirstProjectedRequest().getRequest();
+ if (sourceRequest.hasError()) {
+ original.setError(sourceRequest.getError());
+ } else if (sourceRequest.isCancelled()) {
+ original.cancel();
+ }
+ mirrorProcessor.setFederatedRequest(forked);
+ mirrorProcessor.process(original);
+ } else {
+ this.federatedRequest = forked;
+ process(original);
+ }
+ }
+ } catch (InterruptedException e) {
+ // This happens when the federated connector has been told to shutdown now, and it shuts down
+ // its executor (the worker pool) immediately by interrupting each in-use thread.
+ // In this case, we should cancel the current request but should NOT iterate over any remaining requests.
+ try {
+ if (forked != null) {
+ forked.original().cancel();
+ }
+ } finally {
+ // Clear the interrupted status of the thread ...
+ Thread.interrupted();
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.VerifyNodeExistsRequest)
+ */
+ @Override
+ public void process( VerifyNodeExistsRequest request ) {
+ ProjectedRequest projectedRequest = federatedRequest.getFirstProjectedRequest();
+ assert projectedRequest != null;
+ request.setCachePolicy(getDefaultCachePolicy());
+ Location actualLocation = request.at();
+ int numMerged = 0;
+ while (projectedRequest != null) {
+ VerifyNodeExistsRequest readFromSource = (VerifyNodeExistsRequest)projectedRequest.getRequest();
+ if (readFromSource.hasError()) {
+ projectedRequest = projectedRequest.next();
+ continue;
+ }
+ if (readFromSource.isCancelled()) {
+ request.cancel();
+ return;
+ }
+
+ // Accumulate the identification properties ...
+ for (Property propertyInSource : readFromSource.getActualLocationOfNode().getIdProperties()) {
+ Name name = propertyInSource.getName();
+ Property existing = actualLocation.getIdProperty(name);
+ if (existing != null) {
+ // Merge the property values ...
+ propertyInSource = merge(existing, propertyInSource, propertyFactory, true);
+ }
+ actualLocation = actualLocation.with(propertyInSource);
+ }
+ setCacheableInfo(request, readFromSource.getCachePolicy());
+ projectedRequest = projectedRequest.next();
+ ++numMerged;
+ }
+ if (numMerged == 0) {
+ // No source requests had results ...
+ setPathNotFound(request, request.at(), federatedRequest.getFirstProjectedRequest());
+ } else {
+ request.setActualLocationOfNode(actualLocation);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.ReadNodeRequest)
+ */
+ @Override
+ public void process( ReadNodeRequest request ) {
+ Path federatedPath = request.at().getPath();
+ Map<Name, Property> properties = request.getPropertiesByName();
+ Map<Name, Integer> childSnsIndexes = new HashMap<Name, Integer>();
+ ProjectedRequest projectedRequest = federatedRequest.getFirstProjectedRequest();
+ assert projectedRequest != null;
+ request.setCachePolicy(getDefaultCachePolicy());
+ Location actualLocation = request.at();
+ int numMerged = 0;
+ while (projectedRequest != null) {
+ ReadNodeRequest readFromSource = (ReadNodeRequest)projectedRequest.getRequest();
+ if (readFromSource.hasError()) {
+ projectedRequest = projectedRequest.next();
+ continue;
+ }
+ if (readFromSource.isCancelled()) {
+ request.cancel();
+ return;
+ }
+
+ // Accumulate the identification properties ...
+ for (Property propertyInSource : readFromSource.getActualLocationOfNode().getIdProperties()) {
+ Name name = propertyInSource.getName();
+ Property existing = actualLocation.getIdProperty(name);
+ if (existing != null) {
+ // Merge the property values ...
+ propertyInSource = merge(existing, propertyInSource, propertyFactory, true);
+ }
+ actualLocation = actualLocation.with(propertyInSource);
+ }
+
+ // Add all the children from the source ...
+ for (Location childInSource : readFromSource.getChildren()) {
+ // Project back into the federated repository ...
+ Path childPath = childInSource.getPath();
+ // Correct the same-name-sibling index for the child ...
+ Name childName = childPath.getLastSegment().getName();
+ Integer snsIndex = childSnsIndexes.get(childName);
+ if (snsIndex == null) {
+ snsIndex = new Integer(1);
+ childSnsIndexes.put(childName, snsIndex);
+ } else {
+ snsIndex = new Integer(snsIndex.intValue() + 1);
+ childSnsIndexes.put(childName, snsIndex);
+ }
+ Path newPath = pathFactory.create(federatedPath, childName, snsIndex.intValue());
+ request.addChild(childInSource.with(newPath));
+ }
+
+ // Add all the properties ...
+ for (Property propertyInSource : readFromSource.getProperties()) {
+ Name name = propertyInSource.getName();
+ Property existing = properties.get(name);
+ if (existing != null) {
+ // Merge the property values ...
+ propertyInSource = merge(existing, propertyInSource, propertyFactory, true);
+ }
+ properties.put(name, propertyInSource);
+ }
+ setCacheableInfo(request, readFromSource.getCachePolicy());
+ projectedRequest = projectedRequest.next();
+ ++numMerged;
+ }
+ if (numMerged == 0) {
+ // No source requests had results ...
+ setPathNotFound(request, request.at(), federatedRequest.getFirstProjectedRequest());
+ } else {
+ request.setActualLocationOfNode(actualLocation);
+ }
+ }
+
+ /**
+ * Sets the request {@link Request#setError(Throwable) error} to a {@link PathNotFoundException} that has the lowest existing
+ * ancestor computed from the {@link PathNotFoundException}s in the projected requests.
+ *
+ * @param original
+ * @param originalLocation
+ * @param projected
+ */
+ protected void setPathNotFound( Request original,
+ Location originalLocation,
+ ProjectedRequest projected ) {
+ Path lowestExistingInFederated = pathFactory.createRootPath();
+ while (projected != null) {
+ Request projectedRequest = projected.getRequest();
+ Throwable error = projectedRequest.getError();
+ if (error instanceof PathNotFoundException) {
+ PathNotFoundException notFound = (PathNotFoundException)error;
+ Path lowestExisting = notFound.getLowestAncestorThatDoesExist();
+ // Project back to the repository level ...
+ for (Path federatedPath : projected.getProjection().getPathsInRepository(lowestExisting, pathFactory)) {
+ if (federatedPath.isAtOrBelow(lowestExistingInFederated)) {
+ lowestExistingInFederated = federatedPath;
+ }
+ }
+ }
+ projected = projected.next();
+ }
+ original.setError(new PathNotFoundException(originalLocation, lowestExistingInFederated));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.ReadAllChildrenRequest)
+ */
+ @Override
+ public void process( ReadAllChildrenRequest request ) {
+ Path federatedPath = request.of().getPath();
+ Map<Name, Integer> childSnsIndexes = new HashMap<Name, Integer>();
+ ProjectedRequest projectedRequest = federatedRequest.getFirstProjectedRequest();
+ assert projectedRequest != null;
+ request.setCachePolicy(getDefaultCachePolicy());
+ Location actualLocation = request.of();
+ int numMerged = 0;
+ while (projectedRequest != null) {
+ ReadAllChildrenRequest readFromSource = (ReadAllChildrenRequest)projectedRequest.getRequest();
+ if (readFromSource.hasError()) {
+ projectedRequest = projectedRequest.next();
+ continue;
+ }
+ if (readFromSource.isCancelled()) {
+ request.cancel();
+ return;
+ }
+
+ // Accumulate the identification properties ...
+ for (Property propertyInSource : readFromSource.getActualLocationOfNode().getIdProperties()) {
+ Name name = propertyInSource.getName();
+ Property existing = actualLocation.getIdProperty(name);
+ if (existing != null) {
+ // Merge the property values ...
+ propertyInSource = merge(existing, propertyInSource, propertyFactory, true);
+ }
+ actualLocation = actualLocation.with(propertyInSource);
+ }
+
+ // Add all the children from the source ...
+ for (Location childInSource : readFromSource.getChildren()) {
+ // Project back into the federated repository ...
+ Path childPath = childInSource.getPath();
+ // Correct the same-name-sibling index for the child ...
+ Name childName = childPath.getLastSegment().getName();
+ Integer snsIndex = childSnsIndexes.get(childName);
+ if (snsIndex == null) {
+ snsIndex = new Integer(1);
+ childSnsIndexes.put(childName, snsIndex);
+ } else {
+ snsIndex = new Integer(snsIndex.intValue() + 1);
+ childSnsIndexes.put(childName, snsIndex);
+ }
+ Path newPath = pathFactory.create(federatedPath, childName, snsIndex.intValue());
+ request.addChild(childInSource.with(newPath));
+ }
+
+ setCacheableInfo(request, readFromSource.getCachePolicy());
+ projectedRequest = projectedRequest.next();
+ ++numMerged;
+ }
+ if (numMerged == 0) {
+ // No source requests had results ...
+ setPathNotFound(request, request.of(), federatedRequest.getFirstProjectedRequest());
+ } else {
+ request.setActualLocationOfNode(actualLocation);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.ReadAllPropertiesRequest)
+ */
+ @Override
+ public void process( ReadAllPropertiesRequest request ) {
+ Map<Name, Property> properties = request.getPropertiesByName();
+ ProjectedRequest projectedRequest = federatedRequest.getFirstProjectedRequest();
+ assert projectedRequest != null;
+ request.setCachePolicy(getDefaultCachePolicy());
+ Location actualLocation = request.at();
+ int numMerged = 0;
+ while (projectedRequest != null) {
+ ReadAllPropertiesRequest readFromSource = (ReadAllPropertiesRequest)projectedRequest.getRequest();
+ if (readFromSource.hasError()) {
+ projectedRequest = projectedRequest.next();
+ continue;
+ }
+ if (readFromSource.isCancelled()) {
+ request.cancel();
+ return;
+ }
+
+ // Accumulate the identification properties ...
+ for (Property propertyInSource : readFromSource.getActualLocationOfNode().getIdProperties()) {
+ Name name = propertyInSource.getName();
+ Property existing = actualLocation.getIdProperty(name);
+ if (existing != null) {
+ // Merge the property values ...
+ propertyInSource = merge(existing, propertyInSource, propertyFactory, true);
+ }
+ actualLocation = actualLocation.with(propertyInSource);
+ }
+
+ // Add all the properties ...
+ for (Property propertyInSource : readFromSource.getProperties()) {
+ Name name = propertyInSource.getName();
+ Property existing = properties.get(name);
+ if (existing != null) {
+ // Merge the property values ...
+ propertyInSource = merge(existing, propertyInSource, propertyFactory, true);
+ }
+ properties.put(name, propertyInSource);
+ }
+ setCacheableInfo(request, readFromSource.getCachePolicy());
+ projectedRequest = projectedRequest.next();
+ ++numMerged;
+ }
+ if (numMerged == 0) {
+ // No source requests had results ...
+ setPathNotFound(request, request.at(), federatedRequest.getFirstProjectedRequest());
+ } else {
+ request.setActualLocationOfNode(actualLocation);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.ReadPropertyRequest)
+ */
+ @Override
+ public void process( ReadPropertyRequest request ) {
+ ProjectedRequest projectedRequest = federatedRequest.getFirstProjectedRequest();
+ assert projectedRequest != null;
+ request.setCachePolicy(getDefaultCachePolicy());
+ Location actualLocation = request.on();
+ int numMerged = 0;
+ while (projectedRequest != null) {
+ ReadPropertyRequest readFromSource = (ReadPropertyRequest)projectedRequest.getRequest();
+ if (readFromSource.hasError()) {
+ projectedRequest = projectedRequest.next();
+ continue;
+ }
+ if (readFromSource.isCancelled()) {
+ request.cancel();
+ return;
+ }
+
+ // Accumulate the identification properties ...
+ for (Property propertyInSource : readFromSource.getActualLocationOfNode().getIdProperties()) {
+ Name name = propertyInSource.getName();
+ Property existing = actualLocation.getIdProperty(name);
+ if (existing != null) {
+ // Merge the property values ...
+ propertyInSource = merge(existing, propertyInSource, propertyFactory, true);
+ }
+ actualLocation = actualLocation.with(propertyInSource);
+ }
+
+ // Add all the properties ...
+ Property read = readFromSource.getProperty();
+ if (read != null) {
+ Property existing = request.getProperty();
+ if (existing != null) {
+ // Merge the property values ...
+ request.setProperty(merge(existing, read, propertyFactory, true));
+ } else {
+ request.setProperty(read);
+ }
+ }
+ setCacheableInfo(request, readFromSource.getCachePolicy());
+ projectedRequest = projectedRequest.next();
+ ++numMerged;
+ }
+ if (numMerged == 0) {
+ // No source requests had results ...
+ setPathNotFound(request, request.on(), federatedRequest.getFirstProjectedRequest());
+ } else {
+ request.setActualLocationOfNode(actualLocation);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.ReadBranchRequest)
+ */
+ @Override
+ public void process( ReadBranchRequest request ) {
+ ProjectedRequest projectedRequest = federatedRequest.getFirstProjectedRequest();
+ assert projectedRequest != null;
+ request.setCachePolicy(getDefaultCachePolicy());
+ Location actualLocation = request.at();
+ int numMerged = 0;
+ while (projectedRequest != null) {
+ CacheableRequest fromSource = (CacheableRequest)projectedRequest.getRequest();
+ if (fromSource.hasError()) {
+ projectedRequest = projectedRequest.next();
+ continue;
+ }
+ if (fromSource.isCancelled()) {
+ request.cancel();
+ return;
+ }
+
+ Projection projection = projectedRequest.getProjection();
+ if (fromSource instanceof ReadNodeRequest) {
+ ReadNodeRequest readFromSource = (ReadNodeRequest)fromSource;
+ Location parent = readFromSource.getActualLocationOfNode();
+ List<Location> children = readFromSource.getChildren();
+ Map<Name, Property> properties = readFromSource.getPropertiesByName();
+ projectToFederated(actualLocation, projection, request, parent, children, properties);
+ } else if (fromSource instanceof ReadBranchRequest) {
+ ReadBranchRequest readFromSource = (ReadBranchRequest)fromSource;
+ for (Location parent : readFromSource) {
+ List<Location> children = readFromSource.getChildren(parent);
+ Map<Name, Property> properties = readFromSource.getPropertiesFor(parent);
+ projectToFederated(actualLocation, projection, request, parent, children, properties);
+ }
+ }
+ setCacheableInfo(request, fromSource.getCachePolicy());
+ projectedRequest = projectedRequest.next();
+ ++numMerged;
+ }
+ if (numMerged == 0) {
+ // No source requests had results ...
+ setPathNotFound(request, request.at(), federatedRequest.getFirstProjectedRequest());
+ } else {
+ request.setActualLocationOfNode(actualLocation);
+ }
+ }
+
+ /**
+ * Project the supplied node information read from a source and update the supplied request.
+ *
+ * @param ancestorInFederation the federated node under which this information is being projected; may not be null
+ * @param projection the projection used to make the original source request; may not be null
+ * @param request the federated request upon which the results are to be recorded; may not be null
+ * @param parent the location of the parent in the source; may not be null
+ * @param children the location of the children in the source; may be null or empty
+ * @param propertiesByName the properties on the parent in the source; may be null or empty
+ */
+ protected void projectToFederated( Location ancestorInFederation,
+ Projection projection,
+ ReadBranchRequest request,
+ Location parent,
+ List<Location> children,
+ Map<Name, Property> propertiesByName ) {
+ Path ancestorPath = ancestorInFederation.getPath();
+ if (projection == null) {
+ // This is a placeholder node ...
+ if (children != null) {
+ // Add the children (to any existing children) ...
+ List<Location> existing = request.getChildren(parent);
+ if (existing == null) existing = new ArrayList<Location>(children.size());
+ for (Location child : children) {
+ existing.add(child);
+ }
+ request.setChildren(parent, existing);
+ }
+ if (propertiesByName != null) {
+ // Add the properties to any existing properties ...
+ Map<Name, Property> propsByName = request.getPropertiesFor(parent);
+ if (propsByName == null) propsByName = new HashMap<Name, Property>();
+ for (Property property : propertiesByName.values()) {
+ Property existingProperty = propsByName.get(property.getName());
+ if (existingProperty != null) {
+ // Merge the property values ...
+ property = merge(existingProperty, property, propertyFactory, true);
+ }
+ propsByName.put(property.getName(), property);
+ }
+ request.setProperties(parent, propsByName.values());
+ }
+ return;
+ }
+ for (Path path : projection.getPathsInRepository(parent.getPath(), pathFactory)) {
+ if (!path.isAtOrBelow(ancestorPath)) continue;
+
+ // Determine the list of children ...
+ Location parentInFederation = parent.with(path);
+ if (children != null) {
+ // Add the children to any existing children ...
+ List<Location> existing = request.getChildren(parentInFederation);
+ if (existing == null) existing = new ArrayList<Location>(children.size());
+ for (Location child : children) {
+ Path childPath = pathFactory.create(path, child.getPath().getLastSegment());
+ existing.add(child.with(childPath));
+ }
+ request.setChildren(parentInFederation, existing);
+ }
+
+ // Set or update the properties ...
+ if (propertiesByName != null) {
+ Map<Name, Property> propsByName = request.getPropertiesFor(parentInFederation);
+ if (propsByName == null) propsByName = new HashMap<Name, Property>();
+ for (Property property : propertiesByName.values()) {
+ Property existingProperty = propsByName.get(property.getName());
+ if (existingProperty != null) {
+ // Merge the property values ...
+ property = merge(existingProperty, property, propertyFactory, true);
+ }
+ propsByName.put(property.getName(), property);
+ }
+ request.setProperties(parentInFederation, propsByName.values());
+ }
+ // We're done, since we found a path that's on the ancestor path ...
+ return;
+ }
+ }
+
+ /**
+ * Project the supplied location in a source into its federated location. The projection is used to find the location under
+ * the supplied ancestor. Any errors are recorded on the original request.
+ *
+ * @param ancestorInFederation the ancestor in the federated repository; may not be nul
+ * @param projection the projection that should be used; may not be null
+ * @param actualSourceLocation the actual location in the source that is to be projected back into the federated repository;
+ * may not be null
+ * @param originalRequest the original request, if there are errors; may not be null
+ * @return the location in the federated repository
+ */
+ protected Location projectToFederated( Location ancestorInFederation,
+ Projection projection,
+ Location actualSourceLocation,
+ Request originalRequest ) {
+ Path ancestorPath = ancestorInFederation.getPath();
+ Path actualPathInSource = actualSourceLocation.getPath();
+ // Project the actual location ...
+ for (Path path : projection.getPathsInRepository(actualPathInSource, pathFactory)) {
+ if (path.isAtOrBelow(ancestorPath)) {
+ return actualSourceLocation.with(path);
+ }
+ }
+ // Record that there was an error projecting the results ...
+ String whereInSource = actualSourceLocation.getString(getExecutionContext().getNamespaceRegistry());
+ String msg = GraphI18n.unableToProjectSourceInformationIntoWorkspace.text(whereInSource, getSourceName(), projection);
+ originalRequest.setError(new InvalidRequestException(msg));
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.CreateNodeRequest)
+ */
+ @Override
+ public void process( CreateNodeRequest request ) {
+ ProjectedRequest projected = federatedRequest.getFirstProjectedRequest();
+ assert !projected.hasNext();
+ CreateNodeRequest source = (CreateNodeRequest)projected.getRequest();
+ if (checkErrorOrCancel(request, source)) return;
+ Location sourceLocation = source.getActualLocationOfNode();
+ request.setActualLocationOfNode(projectToFederated(request.under(), projected.getProjection(), sourceLocation, request));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.UpdatePropertiesRequest)
+ */
+ @Override
+ public void process( UpdatePropertiesRequest request ) {
+ ProjectedRequest projected = federatedRequest.getFirstProjectedRequest();
+ assert !projected.hasNext();
+ UpdatePropertiesRequest source = (UpdatePropertiesRequest)projected.getRequest();
+ if (checkErrorOrCancel(request, source)) return;
+ Location sourceLocation = source.getActualLocationOfNode();
+ request.setActualLocationOfNode(projectToFederated(request.on(), projected.getProjection(), sourceLocation, request));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.DeleteBranchRequest)
+ */
+ @Override
+ public void process( DeleteBranchRequest request ) {
+ ProjectedRequest projected = federatedRequest.getFirstProjectedRequest();
+ // Go through the projected requests, and look for the top-most node ...
+ Location highest = null;
+ while (projected != null) {
+ // The projected request should either be a DeleteChildrenRequest (if the node being deleted is
+ // at the top of a projection and therefore required to exist) or a DeleteBranchRequest (in all
+ // other cases)...
+ Location actual = null;
+ Request sourceRequest = projected.getRequest();
+ if (sourceRequest instanceof DeleteBranchRequest) {
+ DeleteBranchRequest source = (DeleteBranchRequest)projected.getRequest();
+ actual = source.getActualLocationOfNode();
+ } else {
+ DeleteChildrenRequest source = (DeleteChildrenRequest)projected.getRequest();
+ actual = source.getActualLocationOfNode();
+ }
+ if (checkErrorOrCancel(request, sourceRequest)) return;
+ if (!projected.isSameLocation() && projected.getProjection() != null) {
+ actual = projectToFederated(request.at(), projected.getProjection(), actual, request);
+ }
+ if (highest == null) highest = actual;
+ else if (highest.getPath().isDecendantOf(actual.getPath())) highest = actual;
+ projected = projected.next();
+ }
+ assert highest != null;
+ request.setActualLocationOfNode(highest);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.CopyBranchRequest)
+ */
+ @Override
+ public void process( CopyBranchRequest request ) {
+ ProjectedRequest projected = federatedRequest.getFirstProjectedRequest();
+ assert !projected.hasNext();
+ CopyBranchRequest source = (CopyBranchRequest)projected.getRequest();
+ if (checkErrorOrCancel(request, source)) return;
+ Location locationBefore = source.getActualLocationBefore();
+ Location locationAfter = source.getActualLocationBefore();
+ locationBefore = projectToFederated(request.from(), projected.getProjection(), locationBefore, request);
+ locationAfter = projectToFederated(request.into(), projected.getSecondProjection(), locationAfter, request);
+ request.setActualLocations(locationBefore, locationAfter);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.MoveBranchRequest)
+ */
+ @Override
+ public void process( MoveBranchRequest request ) {
+ ProjectedRequest projected = federatedRequest.getFirstProjectedRequest();
+ assert !projected.hasNext();
+ MoveBranchRequest source = (MoveBranchRequest)projected.getRequest();
+ if (checkErrorOrCancel(request, source)) return;
+ Location locationBefore = source.getActualLocationBefore();
+ Location locationAfter = source.getActualLocationBefore();
+ locationBefore = projectToFederated(request.from(), projected.getProjection(), locationBefore, request);
+ locationAfter = projectToFederated(request.into(), projected.getSecondProjection(), locationAfter, request);
+ request.setActualLocations(locationBefore, locationAfter);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.VerifyWorkspaceRequest)
+ */
+ @Override
+ public void process( VerifyWorkspaceRequest request ) {
+ ProjectedRequest projectedRequest = federatedRequest.getFirstProjectedRequest();
+ assert projectedRequest != null;
+ Location actualLocation = Location.create(getExecutionContext().getValueFactories().getPathFactory().createRootPath());
+ while (projectedRequest != null) {
+ VerifyNodeExistsRequest readFromSource = (VerifyNodeExistsRequest)projectedRequest.getRequest();
+ if (readFromSource.hasError()) {
+ request.setError(readFromSource.getError());
+ continue;
+ }
+ request.setError(null);
+ if (readFromSource.isCancelled()) {
+ request.cancel();
+ return;
+ }
+
+ // Accumulate the identification properties ...
+ for (Property propertyInSource : readFromSource.getActualLocationOfNode().getIdProperties()) {
+ Name name = propertyInSource.getName();
+ Property existing = actualLocation.getIdProperty(name);
+ if (existing != null) {
+ // Merge the property values ...
+ propertyInSource = merge(existing, propertyInSource, propertyFactory, true);
+ }
+ actualLocation = actualLocation.with(propertyInSource);
+ }
+ projectedRequest = projectedRequest.next();
+ }
+ request.setActualRootLocation(actualLocation);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.GetWorkspacesRequest)
+ */
+ @Override
+ public void process( GetWorkspacesRequest request ) {
+ throw new UnsupportedOperationException(); // should never be called, since it's handled in the ForkProcessor
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.CreateWorkspaceRequest)
+ */
+ @Override
+ public void process( CreateWorkspaceRequest request ) {
+ throw new UnsupportedOperationException(); // should never be called, since it's handled in the ForkProcessor
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.CloneWorkspaceRequest)
+ */
+ @Override
+ public void process( CloneWorkspaceRequest request ) {
+ throw new UnsupportedOperationException(); // should never be called, since it's handled in the ForkProcessor
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.DestroyWorkspaceRequest)
+ */
+ @Override
+ public void process( DestroyWorkspaceRequest request ) {
+ throw new UnsupportedOperationException(); // should never be called, since it's handled in the ForkProcessor
+ }
+
+ protected boolean checkErrorOrCancel( Request request,
+ Request sourceRequest ) {
+ if (sourceRequest.hasError()) {
+ request.setError(sourceRequest.getError());
+ return true;
+ }
+ if (sourceRequest.isCancelled()) {
+ request.cancel();
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Merge the values from the two properties with the same name, returning a new property with the newly merged values.
+ * <p>
+ * The current algorithm merges the values by concatenating the values from <code>property1</code> and <code>property2</code>,
+ * and if <code>removeDuplicates</code> is true any values in <code>property2</code> that are identical to values found in
+ * <code>property1</code> are skipped.
+ * </p>
+ *
+ * @param property1 the first property; may not be null, and must have the same {@link Property#getName() name} as
+ * <code>property2</code>
+ * @param property2 the second property; may not be null, and must have the same {@link Property#getName() name} as
+ * <code>property1</code>
+ * @param factory the property factory, used to create the result
+ * @param removeDuplicates true if this method removes any values in the second property that duplicate values found in the
+ * first property.
+ * @return the property that contains the same {@link Property#getName() name} as the input properties, but with values that
+ * are merged from both of the input properties
+ */
+ protected Property merge( Property property1,
+ Property property2,
+ PropertyFactory factory,
+ boolean removeDuplicates ) {
+ assert property1 != null;
+ assert property2 != null;
+ assert property1.getName().equals(property2.getName());
+ if (property1.isEmpty()) return property2;
+ if (property2.isEmpty()) return property1;
+
+ // If they are both single-valued, then we can use a more efficient algorithm ...
+ if (property1.isSingle() && property2.isSingle()) {
+ Object value1 = property1.getValues().next();
+ Object value2 = property2.getValues().next();
+ if (removeDuplicates && ValueComparators.OBJECT_COMPARATOR.compare(value1, value2) == 0) return property1;
+ return factory.create(property1.getName(), new Object[] {value1, value2});
+ }
+
+ // One or both properties are multi-valued, so use an algorithm that works with in all cases ...
+ if (!removeDuplicates) {
+ Iterator<?> valueIterator = new DualIterator(property1.getValues(), property2.getValues());
+ return factory.create(property1.getName(), valueIterator);
+ }
+
+ // First copy all the values from property 1 ...
+ Object[] values = new Object[property1.size() + property2.size()];
+ int index = 0;
+ for (Object property1Value : property1) {
+ values[index++] = property1Value;
+ }
+ assert index == property1.size();
+ // Now add any values of property2 that don't match a value in property1 ...
+ for (Object property2Value : property2) {
+ // Brute force, go through the values of property1 and compare ...
+ boolean matched = false;
+ for (Object property1Value : property1) {
+ if (ValueComparators.OBJECT_COMPARATOR.compare(property1Value, property2Value) == 0) {
+ // The values are the same ...
+ matched = true;
+ break;
+ }
+ }
+ if (!matched) values[index++] = property2Value;
+ }
+ if (index != values.length) {
+ Object[] newValues = new Object[index];
+ System.arraycopy(values, 0, newValues, 0, index);
+ values = newValues;
+ }
+ return factory.create(property1.getName(), values);
+ }
+
+ protected static class DualIterator implements Iterator<Object> {
+
+ private final Iterator<?>[] iterators;
+ private Iterator<?> current;
+ private int index = 0;
+
+ protected DualIterator( Iterator<?>... iterators ) {
+ assert iterators != null;
+ assert iterators.length > 0;
+ this.iterators = iterators;
+ this.current = this.iterators[0];
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.util.Iterator#hasNext()
+ */
+ public boolean hasNext() {
+ if (this.current != null) return this.current.hasNext();
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.util.Iterator#next()
+ */
+ public Object next() {
+ while (this.current != null) {
+ if (this.current.hasNext()) return this.current.next();
+ // Get the next iterator ...
+ if (++this.index < iterators.length) {
+ this.current = this.iterators[this.index];
+ } else {
+ this.current = null;
+ }
+ }
+ throw new NoSuchElementException();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.util.Iterator#remove()
+ */
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+}
Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/JoinRequestProcessor.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/MirrorProjector.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/MirrorProjector.java (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/MirrorProjector.java 2009-05-18 15:36:58 UTC (rev 911)
@@ -0,0 +1,83 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ *
+ * JBoss DNA 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.graph.connector.federation;
+
+import java.util.List;
+import net.jcip.annotations.Immutable;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.Location;
+import org.jboss.dna.graph.property.Path;
+import org.jboss.dna.graph.property.PathFactory;
+
+/**
+ * A Projector for federated repository configurations that are a direct one-for-one mirror against a single source repository. In
+ * other words, the federated repository has a single projection with a single "/ => /" rule.
+ */
+@Immutable
+final class MirrorProjector implements Projector {
+
+ /**
+ * Attempt to create an instance of the {@link MirrorProjector} with the supplied projections using the supplied context.
+ *
+ * @param context the context; may not be null
+ * @param projections the projections in the federated repository; may not be null
+ * @return the mirror projector, or null if the projections didn't match the criteria for such a projector
+ */
+ static MirrorProjector with( ExecutionContext context,
+ List<Projection> projections ) {
+ assert projections != null;
+ assert context != null;
+ if (projections.size() != 1) return null;
+ Projection projection = projections.get(0);
+ assert projection != null;
+ if (projection.getRules().size() != 1) return null;
+ PathFactory pathFactory = context.getValueFactories().getPathFactory();
+ List<Path> topLevelPaths = projection.getRules().get(0).getTopLevelPathsInRepository(pathFactory);
+ if (topLevelPaths.size() != 1) return null;
+ if (!topLevelPaths.get(0).isRoot()) return null;
+ return new MirrorProjector(projection);
+ }
+
+ private final Projection projection;
+
+ private MirrorProjector( Projection projection ) {
+ this.projection = projection;
+ }
+
+ /**
+ * {@inheritDoc}
+ * <p>
+ * This implementation <i>always<i> returns a single {@link ProxyNode} for the location in the single projection.
+ * </p>
+ *
+ * @see org.jboss.dna.graph.connector.federation.Projector#project(ExecutionContext, Location, boolean)
+ */
+ public ProjectedNode project( ExecutionContext context,
+ Location location,
+ boolean requiresUpdate ) {
+ if (requiresUpdate && projection.isReadOnly()) return null;
+ return new ProxyNode(projection, location, location);
+ }
+
+}
Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/MirrorProjector.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/NoMoreFederatedRequests.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/NoMoreFederatedRequests.java (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/NoMoreFederatedRequests.java 2009-05-18 15:36:58 UTC (rev 911)
@@ -0,0 +1,40 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * Unless otherwise indicated, all code in JBoss DNA is licensed
+ * to you 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.
+ *
+ * JBoss DNA 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.graph.connector.federation;
+
+/**
+ * Specialization of {@link FederatedRequest}, which is used to place an instance in a queue so the {@link JoinRequestProcessor}
+ * knows that all federated requests have been processed.
+ *
+ * @see JoinRequestProcessor#process(java.util.concurrent.BlockingQueue)
+ */
+class NoMoreFederatedRequests extends FederatedRequest {
+
+ /**
+ */
+ NoMoreFederatedRequests() {
+ super(null);
+ }
+
+}
Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/NoMoreFederatedRequests.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/OffsetMirrorProjector.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/OffsetMirrorProjector.java (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/OffsetMirrorProjector.java 2009-05-18 15:36:58 UTC (rev 911)
@@ -0,0 +1,114 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ *
+ * JBoss DNA 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.graph.connector.federation;
+
+import java.util.List;
+import net.jcip.annotations.Immutable;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.Location;
+import org.jboss.dna.graph.property.Path;
+import org.jboss.dna.graph.property.PathFactory;
+
+/**
+ * A Projector for federated repository configurations that are an offset, direct one-for-one mirror against a single source
+ * repository that is projected below the federated root. In other words, the federated repository has a single projection with a
+ * single "/something/below/root => /" rule.
+ */
+@Immutable
+final class OffsetMirrorProjector extends ProjectorWithPlaceholders {
+
+ /**
+ * Attempt to create an instance of the {@link OffsetMirrorProjector} with the supplied projections using the supplied
+ * context.
+ *
+ * @param context the context; may not be null
+ * @param projections the projections in the federated repository; may not be null
+ * @return the offset mirror projector, or null if the projections didn't match the criteria for such a projector
+ */
+ static OffsetMirrorProjector with( ExecutionContext context,
+ List<Projection> projections ) {
+ assert projections != null;
+ assert context != null;
+ if (projections.size() != 1) return null;
+ Projection projection = projections.get(0);
+ assert projection != null;
+ if (projection.getRules().size() != 1) return null;
+ PathFactory pathFactory = context.getValueFactories().getPathFactory();
+ List<Path> topLevelPaths = projection.getRules().get(0).getTopLevelPathsInRepository(pathFactory);
+ if (topLevelPaths.size() != 1) return null;
+ Path topLevelPath = topLevelPaths.get(0);
+ assert topLevelPath != null;
+ if (topLevelPath.isRoot()) return null;
+ return new OffsetMirrorProjector(context, projections, topLevelPath);
+ }
+
+ private final Projection projection;
+ private final Path offset;
+ private final int offsetSize;
+
+ private OffsetMirrorProjector( ExecutionContext context,
+ List<Projection> projections,
+ Path offset ) {
+ super(context, projections);
+ this.projection = projections.get(0);
+ this.offset = offset;
+ this.offsetSize = offset.size();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.connector.federation.Projector#project(ExecutionContext, Location, boolean)
+ */
+ public ProjectedNode project( ExecutionContext context,
+ Location location,
+ boolean requiresUpdate ) {
+ if (requiresUpdate && projection.isReadOnly()) return null;
+ PlaceholderNode placeholder = isPlaceholder(location);
+ if (placeholder != null) return placeholder;
+
+ Path path = location.getPath();
+ Location locationInSource = location;
+ if (path != null) {
+ if (path.size() == offsetSize) {
+ // Make sure the path is the same ...
+ if (path.equals(offset)) {
+ locationInSource = location.with(context.getValueFactories().getPathFactory().createRootPath());
+ } else {
+ return null; // not in the path
+ }
+ } else {
+ // Make sure the path begins with the offset ...
+ if (path.isAtOrBelow(offset)) {
+ locationInSource = location.with(path.subpath(offsetSize));
+ } else {
+ // Not in the path
+ return null;
+ }
+ }
+ }
+ return new ProxyNode(projection, locationInSource, location);
+ }
+
+}
Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/OffsetMirrorProjector.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/ProjectedNode.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/ProjectedNode.java (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/ProjectedNode.java 2009-05-18 15:36:58 UTC (rev 911)
@@ -0,0 +1,200 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ *
+ * JBoss DNA 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.graph.connector.federation;
+
+import java.util.List;
+import java.util.Map;
+import net.jcip.annotations.Immutable;
+import net.jcip.annotations.NotThreadSafe;
+import org.jboss.dna.graph.Location;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.Property;
+
+/**
+ * Information about a node that has been projected into the repository from a source. Each projected node may be followed by a
+ * {@link #next() next} projected node.
+ */
+@Immutable
+abstract class ProjectedNode {
+ private final Location location;
+ private ProjectedNode next;
+
+ protected ProjectedNode( Location location ) {
+ this.location = location;
+ }
+
+ public Location location() {
+ return location;
+ }
+
+ public int size() {
+ return next != null ? next.size() + 1 : 1;
+ }
+
+ public int numberOfProxies() {
+ int base = isProxy() ? 1 : 0;
+ return next != null ? next.numberOfProxies() + base : base;
+ }
+
+ public boolean hasNext() {
+ return next != null;
+ }
+
+ public ProjectedNode next() {
+ return next;
+ }
+
+ /*package*/void add( ProjectedNode next ) {
+ if (this.next != null) {
+ this.next.add(next);
+ } else {
+ this.next = next;
+ }
+ }
+
+ public abstract boolean isPlaceholder();
+
+ public abstract boolean isProxy();
+
+ public abstract PlaceholderNode asPlaceholder();
+
+ public abstract ProxyNode asProxy();
+}
+
+@Immutable
+class PlaceholderNode extends ProjectedNode {
+ private final Map<Name, Property> properties;
+ private final List<Location> children;
+
+ protected PlaceholderNode( Location location,
+ Map<Name, Property> properties,
+ List<Location> children ) {
+ super(location);
+ this.properties = properties;
+ this.children = children;
+ }
+
+ public List<Location> children() {
+ return children;
+ }
+
+ public Map<Name, Property> properties() {
+ return properties;
+ }
+
+ @Override
+ public boolean isPlaceholder() {
+ return true;
+ }
+
+ @Override
+ public PlaceholderNode asPlaceholder() {
+ return this;
+ }
+
+ @Override
+ public boolean isProxy() {
+ return false;
+ }
+
+ @Override
+ public ProxyNode asProxy() {
+ return null;
+ }
+}
+
+@NotThreadSafe
+class ProxyNode extends ProjectedNode {
+ private final Projection projection;
+ private final boolean sameLocationAsOriginal;
+ private final Location federatedLocation;
+
+ protected ProxyNode( Projection projection,
+ Location locationInSource,
+ Location locationInFederated ) {
+ super(locationInSource);
+ this.projection = projection;
+ this.federatedLocation = locationInFederated;
+ this.sameLocationAsOriginal = locationInSource.equals(locationInFederated);
+ }
+
+ protected ProxyNode( Projection projection,
+ Location locationInSource,
+ Location locationInFederated,
+ boolean isSameLocation ) {
+ super(locationInSource);
+ this.projection = projection;
+ this.federatedLocation = locationInFederated;
+ this.sameLocationAsOriginal = isSameLocation;
+ }
+
+ public String source() {
+ return projection.getSourceName();
+ }
+
+ public String workspaceName() {
+ return projection.getWorkspaceName();
+ }
+
+ /*package*/Projection projection() {
+ return projection;
+ }
+
+ public Location federatedLocation() {
+ return federatedLocation;
+ }
+
+ @Override
+ public boolean isPlaceholder() {
+ return false;
+ }
+
+ @Override
+ public PlaceholderNode asPlaceholder() {
+ return null;
+ }
+
+ @Override
+ public boolean isProxy() {
+ return true;
+ }
+
+ @Override
+ public ProxyNode asProxy() {
+ return this;
+ }
+
+ /**
+ * Determine whether this projected node is a top-level node in the source projected into the federated repository.
+ *
+ * @return true if the projected node is a top-level node in the source, or false otherwise
+ */
+ public boolean isTopLevelNode() {
+ return federatedLocation != null && federatedLocation.hasPath() && projection.isTopLevelPath(federatedLocation.getPath());
+ }
+
+ public boolean isSameLocationAsOriginal() {
+ return this.sameLocationAsOriginal;
+ }
+}
Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/ProjectedNode.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/Projection.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/Projection.java (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/Projection.java 2009-05-18 15:36:58 UTC (rev 911)
@@ -0,0 +1,887 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ *
+ * JBoss DNA 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.graph.connector.federation;
+
+import java.io.Serializable;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import net.jcip.annotations.Immutable;
+import org.jboss.dna.common.text.TextEncoder;
+import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.common.util.HashCode;
+import org.jboss.dna.common.util.Logger;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.GraphI18n;
+import org.jboss.dna.graph.connector.RepositorySource;
+import org.jboss.dna.graph.property.NamespaceRegistry;
+import org.jboss.dna.graph.property.Path;
+import org.jboss.dna.graph.property.PathFactory;
+
+/**
+ * A projection of content from a source into the integrated/federated repository. Each project consists of a set of {@link Rule
+ * rules} for a particular source, where each rule defines how content within a source is
+ * {@link Rule#getPathInRepository(Path, PathFactory) is project into the repository} and how the repository content is
+ * {@link Rule#getPathInSource(Path, PathFactory) projected into the source}. Different rule subclasses are used for different
+ * types.
+ */
+@Immutable
+public class Projection implements Comparable<Projection>, Serializable {
+
+ /**
+ * Initial version
+ */
+ private static final long serialVersionUID = 1L;
+ protected static final List<Method> parserMethods;
+ static {
+ parserMethods = new CopyOnWriteArrayList<Method>();
+ try {
+ parserMethods.add(Projection.class.getDeclaredMethod("parsePathRule", String.class, ExecutionContext.class));
+ } catch (Throwable err) {
+ Logger.getLogger(Projection.class).error(err, GraphI18n.errorAddingProjectionRuleParseMethod);
+ }
+ }
+
+ /**
+ * Add a static method that can be used to parse {@link Rule#getString(NamespaceRegistry, TextEncoder) rule definition
+ * strings}. These methods must be static, must accept a {@link String} definition as the first parameter and an
+ * {@link ExecutionContext} environment reference as the second parameter, and should return the resulting {@link Rule} (or
+ * null if the definition format could not be understood by the method. Any exceptions during
+ * {@link Method#invoke(Object, Object...) invocation} will be logged at the
+ * {@link Logger#trace(Throwable, String, Object...) trace} level.
+ *
+ * @param method the method to be added
+ * @see #addRuleParser(ClassLoader, String, String)
+ */
+ public static void addRuleParser( Method method ) {
+ if (method != null) parserMethods.add(method);
+ }
+
+ /**
+ * Add a static method that can be used to parse {@link Rule#getString(NamespaceRegistry, TextEncoder) rule definition
+ * strings}. These methods must be static, must accept a {@link String} definition as the first parameter and an
+ * {@link ExecutionContext} environment reference as the second parameter, and should return the resulting {@link Rule} (or
+ * null if the definition format could not be understood by the method. Any exceptions during
+ * {@link Method#invoke(Object, Object...) invocation} will be logged at the
+ * {@link Logger#trace(Throwable, String, Object...) trace} level.
+ *
+ * @param classLoader the class loader that should be used to load the class on which the method is defined; may not be null
+ * @param className the name of the class on which the static method is defined; may not be null
+ * @param methodName the name of the method
+ * @throws SecurityException if there is a security exception while loading the class or getting the method
+ * @throws NoSuchMethodException if the method does not exist on the class
+ * @throws ClassNotFoundException if the class could not be found given the supplied class loader
+ * @throws IllegalArgumentException if the class loader reference is null, or if the class name or method name are null or
+ * empty
+ * @see #addRuleParser(Method)
+ */
+ public static void addRuleParser( ClassLoader classLoader,
+ String className,
+ String methodName ) throws SecurityException, NoSuchMethodException, ClassNotFoundException {
+ CheckArg.isNotNull(classLoader, "classLoader");
+ CheckArg.isNotEmpty(className, "className");
+ CheckArg.isNotEmpty(methodName, "methodName");
+ Class<?> clazz = Class.forName(className, true, classLoader);
+ parserMethods.add(clazz.getMethod(className, String.class, ExecutionContext.class));
+ }
+
+ /**
+ * Remove the rule parser method.
+ *
+ * @param method the method to remove
+ * @return true if the method was removed, or false if the method was not a registered rule parser method
+ */
+ public static boolean removeRuleParser( Method method ) {
+ return parserMethods.remove(method);
+ }
+
+ /**
+ * Remove the rule parser method.
+ *
+ * @param declaringClassName the name of the class on which the static method is defined; may not be null
+ * @param methodName the name of the method
+ * @return true if the method was removed, or false if the method was not a registered rule parser method
+ * @throws IllegalArgumentException if the class loader reference is null, or if the class name or method name are null or
+ * empty
+ */
+ public static boolean removeRuleParser( String declaringClassName,
+ String methodName ) {
+ CheckArg.isNotEmpty(declaringClassName, "declaringClassName");
+ CheckArg.isNotEmpty(methodName, "methodName");
+ for (Method method : parserMethods) {
+ if (method.getName().equals(methodName) && method.getDeclaringClass().getName().equals(declaringClassName)) {
+ return parserMethods.remove(method);
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Parse the string form of a rule definition and return the rule
+ *
+ * @param definition the definition of the rule that is to be parsed
+ * @param context the environment in which this method is being executed; may not be null
+ * @return the rule, or null if the definition could not be parsed
+ */
+ public static Rule fromString( String definition,
+ ExecutionContext context ) {
+ CheckArg.isNotNull(context, "env");
+ definition = definition != null ? definition.trim() : "";
+ if (definition.length() == 0) return null;
+ for (Method method : parserMethods) {
+ try {
+ Rule rule = (Rule)method.invoke(null, definition, context);
+ if (rule != null) return rule;
+ } catch (Throwable err) {
+ String msg = "Error while parsing project rule definition \"{0}\" using {1}";
+ context.getLogger(Projection.class).trace(err, msg, definition, method);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Pattern that identifies the form:
+ *
+ * <pre>
+ * repository_path => source_path [$ exception ]*
+ * </pre>
+ *
+ * where the following groups are captured on the first call to {@link Matcher#find()}:
+ * <ol>
+ * <li><code>repository_path</code></li>
+ * <li><code>source_path</code></li>
+ * </ol>
+ * and the following groups are captured on subsequent calls to {@link Matcher#find()}:
+ * <ol>
+ * <li>exception</code></li>
+ * </ol>
+ * <p>
+ * The regular expression is:
+ *
+ * <pre>
+ * ((?:[ˆ=$]|=(?!>))+)(?:(?:=>((?:[ˆ=$]|=(?!>))+))( \$ (?:(?:[ˆ=]|=(?!>))+))*)?
+ * </pre>
+ *
+ * </p>
+ */
+ protected static final String PATH_RULE_PATTERN_STRING = "((?:[^=$]|=(?!>))+)(?:(?:=>((?:[^=$]|=(?!>))+))( \\$ (?:(?:[^=]|=(?!>))+))*)?";
+ protected static final Pattern PATH_RULE_PATTERN = Pattern.compile(PATH_RULE_PATTERN_STRING);
+
+ /**
+ * Parse the string definition of a {@link PathRule}. This method is automatically registered in the {@link #parserMethods
+ * parser methods} by the static initializer of {@link Projection}.
+ *
+ * @param definition the definition
+ * @param context the environment
+ * @return the path rule, or null if the definition is not in the right form
+ */
+ public static PathRule parsePathRule( String definition,
+ ExecutionContext context ) {
+ definition = definition != null ? definition.trim() : "";
+ if (definition.length() == 0) return null;
+ Matcher matcher = PATH_RULE_PATTERN.matcher(definition);
+ if (!matcher.find()) return null;
+ String reposPathStr = matcher.group(1);
+ String sourcePathStr = matcher.group(2);
+ if (reposPathStr == null || sourcePathStr == null) return null;
+ reposPathStr = reposPathStr.trim();
+ sourcePathStr = sourcePathStr.trim();
+ if (reposPathStr.length() == 0 || sourcePathStr.length() == 0) return null;
+ PathFactory pathFactory = context.getValueFactories().getPathFactory();
+ Path repositoryPath = pathFactory.create(reposPathStr);
+ Path sourcePath = pathFactory.create(sourcePathStr);
+
+ // Grab the exceptions ...
+ List<Path> exceptions = new LinkedList<Path>();
+ while (matcher.find()) {
+ String exceptionStr = matcher.group(1);
+ Path exception = pathFactory.create(exceptionStr);
+ exceptions.add(exception);
+ }
+ return new PathRule(repositoryPath, sourcePath, exceptions);
+ }
+
+ private final String sourceName;
+ private final String workspaceName;
+ private final List<Rule> rules;
+ private final boolean simple;
+ private final boolean readOnly;
+ private final int hc;
+
+ /**
+ * Create a new federated projection for the supplied source, using the supplied rules.
+ *
+ * @param sourceName the name of the source
+ * @param workspaceName the name of the workspace in the source; may be null if the default workspace is to be used
+ * @param readOnly true if this projection is considered read-only, or false if the content of the projection may be modified
+ * by the federated clients
+ * @param rules the projection rules
+ * @throws IllegalArgumentException if the source name or rule array is null, empty, or contains all nulls
+ */
+ public Projection( String sourceName,
+ String workspaceName,
+ boolean readOnly,
+ Rule... rules ) {
+ CheckArg.isNotEmpty(sourceName, "sourceName");
+ CheckArg.isNotEmpty(rules, "rules");
+ this.sourceName = sourceName;
+ this.workspaceName = workspaceName;
+ List<Rule> rulesList = new ArrayList<Rule>();
+ for (Rule rule : rules) {
+ if (rule != null) rulesList.add(rule);
+ }
+ this.readOnly = readOnly;
+ this.rules = Collections.unmodifiableList(rulesList);
+ CheckArg.isNotEmpty(this.rules, "rules");
+ this.simple = computeSimpleProjection(this.rules);
+ this.hc = HashCode.compute(this.sourceName, this.workspaceName);
+ }
+
+ /**
+ * Get the name of the source to which this projection applies.
+ *
+ * @return the source name
+ * @see RepositorySource#getName()
+ */
+ public String getSourceName() {
+ return sourceName;
+ }
+
+ /**
+ * Get the name of the workspace in the source to which this projection applies.
+ *
+ * @return the workspace name, or null if the default workspace of the {@link #getSourceName() source} is to be used
+ */
+ public String getWorkspaceName() {
+ return workspaceName;
+ }
+
+ /**
+ * Get the rules that define this projection.
+ *
+ * @return the unmodifiable list of immutable rules; never null
+ */
+ public List<Rule> getRules() {
+ return rules;
+ }
+
+ /**
+ * Get the paths in the source that correspond to the supplied path within the repository. This method computes the paths
+ * given all of the rules. In general, most sources will probably project a node onto a single repository node. However, some
+ * sources may be configured such that the same node in the repository is a projection of multiple nodes within the source.
+ *
+ * @param canonicalPathInRepository the canonical path of the node within the repository; may not be null
+ * @param factory the path factory; may not be null
+ * @return the set of unique paths in the source projected from the repository path; never null
+ * @throws IllegalArgumentException if the factory reference is null
+ */
+ public Set<Path> getPathsInSource( Path canonicalPathInRepository,
+ PathFactory factory ) {
+ CheckArg.isNotNull(factory, "factory");
+ assert canonicalPathInRepository == null ? true : canonicalPathInRepository.equals(canonicalPathInRepository.getCanonicalPath());
+ Set<Path> paths = new HashSet<Path>();
+ for (Rule rule : getRules()) {
+ Path pathInSource = rule.getPathInSource(canonicalPathInRepository, factory);
+ if (pathInSource != null) paths.add(pathInSource);
+ }
+ return paths;
+ }
+
+ /**
+ * Get the paths in the repository that correspond to the supplied path within the source. This method computes the paths
+ * given all of the rules. In general, most sources will probably project a node onto a single repository node. However, some
+ * sources may be configured such that the same node in the source is projected into multiple nodes within the repository.
+ *
+ * @param canonicalPathInSource the canonical path of the node within the source; may not be null
+ * @param factory the path factory; may not be null
+ * @return the set of unique paths in the repository projected from the source path; never null
+ * @throws IllegalArgumentException if the factory reference is null
+ */
+ public Set<Path> getPathsInRepository( Path canonicalPathInSource,
+ PathFactory factory ) {
+ CheckArg.isNotNull(factory, "factory");
+ assert canonicalPathInSource == null ? true : canonicalPathInSource.equals(canonicalPathInSource.getCanonicalPath());
+ Set<Path> paths = new HashSet<Path>();
+ for (Rule rule : getRules()) {
+ Path pathInRepository = rule.getPathInRepository(canonicalPathInSource, factory);
+ if (pathInRepository != null) paths.add(pathInRepository);
+ }
+ return paths;
+ }
+
+ /**
+ * Get the paths in the repository that serve as top-level nodes exposed by this projection.
+ *
+ * @param factory the path factory that can be used to create new paths; may not be null
+ * @return the list of top-level paths, in the proper order and containing no duplicates; never null
+ */
+ public List<Path> getTopLevelPathsInRepository( PathFactory factory ) {
+ CheckArg.isNotNull(factory, "factory");
+ List<Rule> rules = getRules();
+ Set<Path> uniquePaths = new HashSet<Path>();
+ List<Path> paths = new ArrayList<Path>(rules.size());
+ for (Rule rule : getRules()) {
+ for (Path path : rule.getTopLevelPathsInRepository(factory)) {
+ if (!uniquePaths.contains(path)) {
+ paths.add(path);
+ uniquePaths.add(path);
+ }
+ }
+ }
+ return paths;
+ }
+
+ /**
+ * Determine whether the supplied repositoryPath is considered one of the top-level nodes in this projection.
+ *
+ * @param repositoryPath path in the repository; may not be null
+ * @return true if the supplied repository path is one of the top-level nodes exposed by this projection, or false otherwise
+ */
+ public boolean isTopLevelPath( Path repositoryPath ) {
+ for (Rule rule : getRules()) {
+ if (rule.isTopLevelPath(repositoryPath)) return true;
+ }
+ return false;
+ }
+
+ /**
+ * Determine whether this project is a simple projection that only involves for any one repository path no more than a single
+ * source path.
+ *
+ * @return true if this projection is a simple projection, or false if the projection is not simple (or it cannot be
+ * determined if it is simple)
+ */
+ public boolean isSimple() {
+ return simple;
+ }
+
+ /**
+ * Determine whether the content projected by this projection is read-only.
+ *
+ * @return true if the content is read-only, or false if it can be modified
+ */
+ public boolean isReadOnly() {
+ return readOnly;
+ }
+
+ protected boolean computeSimpleProjection( List<Rule> rules ) {
+ // Get the set of repository paths for the rules, and see if they overlap ...
+ Set<Path> repositoryPaths = new HashSet<Path>();
+ for (Rule rule : rules) {
+ if (rule instanceof PathRule) {
+ PathRule pathRule = (PathRule)rule;
+ Path repoPath = pathRule.getPathInRepository();
+ if (!repositoryPaths.isEmpty()) {
+ if (repositoryPaths.contains(repoPath)) return false;
+ for (Path path : repositoryPaths) {
+ if (path.isAtOrAbove(repoPath)) return false;
+ if (repoPath.isAtOrAbove(path)) return false;
+ }
+ }
+ repositoryPaths.add(repoPath);
+ } else {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ return this.hc;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals( Object obj ) {
+ if (obj == this) return true;
+ if (obj instanceof Projection) {
+ Projection that = (Projection)obj;
+ if (this.hashCode() != that.hashCode()) return false;
+ if (!this.getSourceName().equals(that.getSourceName())) return false;
+ if (!this.getWorkspaceName().equals(that.getWorkspaceName())) return false;
+ if (!this.getRules().equals(that.getRules())) return false;
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Comparable#compareTo(java.lang.Object)
+ */
+ public int compareTo( Projection that ) {
+ if (this == that) return 0;
+ int diff = this.getSourceName().compareTo(that.getSourceName());
+ if (diff != 0) return diff;
+ diff = this.getWorkspaceName().compareTo(that.getWorkspaceName());
+ if (diff != 0) return diff;
+ Iterator<Rule> thisIter = this.getRules().iterator();
+ Iterator<Rule> thatIter = that.getRules().iterator();
+ while (thisIter.hasNext() && thatIter.hasNext()) {
+ diff = thisIter.next().compareTo(thatIter.next());
+ if (diff != 0) return diff;
+ }
+ if (thisIter.hasNext()) return 1;
+ if (thatIter.hasNext()) return -1;
+ return 0;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append(this.sourceName);
+ sb.append("::");
+ sb.append(this.workspaceName);
+ sb.append(" { ");
+ boolean first = true;
+ for (Rule rule : this.getRules()) {
+ if (!first) sb.append(" ; ");
+ sb.append(rule.toString());
+ first = false;
+ }
+ sb.append(" }");
+ return sb.toString();
+ }
+
+ /**
+ * A rule used within a project do define how content within a source is projected into the federated repository. This mapping
+ * is bi-directional, meaning it's possible to determine
+ * <ul>
+ * <li>the path in repository given a path in source; and</li>
+ * <li>the path in source given a path in repository.</li>
+ * </ul>
+ *
+ * @author Randall Hauch
+ */
+ @Immutable
+ public static abstract class Rule implements Comparable<Rule> {
+
+ /**
+ * Get the paths in the repository that serve as top-level nodes exposed by this rule.
+ *
+ * @param factory the path factory that can be used to create new paths; may not be null
+ * @return the list of top-level paths, which are ordered and which must be unique; never null
+ */
+ public abstract List<Path> getTopLevelPathsInRepository( PathFactory factory );
+
+ /**
+ * Determine if the supplied path is the same as one of the top-level nodes exposed by this rule.
+ *
+ * @param path the path; may not be null
+ * @return true if the supplied path is also one of the {@link #getTopLevelPathsInRepository(PathFactory) top-level paths}
+ * , or false otherwise
+ */
+ public abstract boolean isTopLevelPath( Path path );
+
+ /**
+ * Get the path in source that is projected from the supplied repository path, or null if the supplied repository path is
+ * not projected into the source.
+ *
+ * @param pathInRepository the path in the repository; may not be null
+ * @param factory the path factory; may not be null
+ * @return the path in source if it is projected by this rule, or null otherwise
+ */
+ public abstract Path getPathInSource( Path pathInRepository,
+ PathFactory factory );
+
+ /**
+ * Get the path in repository that is projected from the supplied source path, or null if the supplied source path is not
+ * projected into the repository.
+ *
+ * @param pathInSource the path in the source; may not be null
+ * @param factory the path factory; may not be null
+ * @return the path in repository if it is projected by this rule, or null otherwise
+ */
+ public abstract Path getPathInRepository( Path pathInSource,
+ PathFactory factory );
+
+ public abstract String getString( NamespaceRegistry registry,
+ TextEncoder encoder );
+
+ public abstract String getString( TextEncoder encoder );
+
+ public abstract String getString();
+ }
+
+ /**
+ * A rule that is defined with a single {@link #getPathInSource() path in source} and a single {@link #getPathInRepository()
+ * path in repository}, and which has a set of {@link #getExceptionsToRule() path exceptions} (relative paths below the path
+ * in source).
+ *
+ * @author Randall Hauch
+ */
+ @Immutable
+ public static class PathRule extends Rule {
+ /** The path of the content as known to the source */
+ private final Path sourcePath;
+ /** The path where the content is to be placed ("projected") into the repository */
+ private final Path repositoryPath;
+ /** The paths (relative to the source path) that identify exceptions to this rule */
+ private final List<Path> exceptions;
+ private final int hc;
+ private final List<Path> topLevelRepositoryPaths;
+
+ public PathRule( Path repositoryPath,
+ Path sourcePath ) {
+ this(repositoryPath, sourcePath, (Path[])null);
+ }
+
+ public PathRule( Path repositoryPath,
+ Path sourcePath,
+ Path... exceptions ) {
+ assert sourcePath != null;
+ assert repositoryPath != null;
+ this.sourcePath = sourcePath;
+ this.repositoryPath = repositoryPath;
+ if (exceptions == null || exceptions.length == 0) {
+ this.exceptions = Collections.emptyList();
+ } else {
+ List<Path> exceptionList = new ArrayList<Path>();
+ for (Path exception : exceptions) {
+ if (exception != null) exceptionList.add(exception);
+ }
+ this.exceptions = Collections.unmodifiableList(exceptionList);
+ }
+ this.hc = HashCode.compute(sourcePath, repositoryPath, exceptions);
+ assert exceptionPathsAreRelative();
+ this.topLevelRepositoryPaths = Collections.singletonList(getPathInRepository());
+ }
+
+ public PathRule( Path repositoryPath,
+ Path sourcePath,
+ List<Path> exceptions ) {
+ assert sourcePath != null;
+ assert repositoryPath != null;
+ this.sourcePath = sourcePath;
+ this.repositoryPath = repositoryPath;
+ if (exceptions == null || exceptions.isEmpty()) {
+ this.exceptions = Collections.emptyList();
+ } else {
+ this.exceptions = Collections.unmodifiableList(new ArrayList<Path>(exceptions));
+ }
+ this.hc = HashCode.compute(sourcePath, repositoryPath, exceptions);
+ assert exceptionPathsAreRelative();
+ this.topLevelRepositoryPaths = Collections.singletonList(getPathInRepository());
+ }
+
+ private boolean exceptionPathsAreRelative() {
+ if (this.exceptions != null) {
+ for (Path path : this.exceptions) {
+ if (path.isAbsolute()) return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * The path where the content is to be placed ("projected") into the repository.
+ *
+ * @return the projected path of the content in the repository; never null
+ */
+ public Path getPathInRepository() {
+ return repositoryPath;
+ }
+
+ /**
+ * The path of the content as known to the source
+ *
+ * @return the source-specific path of the content; never null
+ */
+ public Path getPathInSource() {
+ return sourcePath;
+ }
+
+ /**
+ * Get whether this rule has any exceptions.
+ *
+ * @return true if this rule has exceptions, or false if it has none.
+ */
+ public boolean hasExceptionsToRule() {
+ return exceptions.size() != 0;
+ }
+
+ /**
+ * Get the paths that define the exceptions to this rule. These paths are always relative to the
+ * {@link #getPathInSource() path in source}.
+ *
+ * @return the unmodifiable exception paths; never null but possibly empty
+ */
+ public List<Path> getExceptionsToRule() {
+ return exceptions;
+ }
+
+ /**
+ * @param pathInSource
+ * @return true if the source path is included by this rule
+ */
+ protected boolean includes( Path pathInSource ) {
+ // Check whether the path is outside the source-specific path ...
+ if (pathInSource != null && this.sourcePath.isAtOrAbove(pathInSource)) {
+
+ // The path is inside the source-specific region, so check the exceptions ...
+ List<Path> exceptions = getExceptionsToRule();
+ if (exceptions.size() != 0) {
+ Path subpathInSource = pathInSource.relativeTo(this.sourcePath);
+ if (subpathInSource.size() != 0) {
+ for (Path exception : exceptions) {
+ if (subpathInSource.isAtOrBelow(exception)) return false;
+ }
+ }
+ }
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see Rule#getTopLevelPathsInRepository(org.jboss.dna.graph.property.PathFactory)
+ */
+ @Override
+ public List<Path> getTopLevelPathsInRepository( PathFactory factory ) {
+ return topLevelRepositoryPaths;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.connector.federation.Projection.Rule#isTopLevelPath(org.jboss.dna.graph.property.Path)
+ */
+ @Override
+ public boolean isTopLevelPath( Path path ) {
+ for (Path topLevel : topLevelRepositoryPaths) {
+ if (topLevel.equals(path)) return true;
+ }
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ * <p>
+ * This method considers a path that is at or below the rule's {@link #getPathInSource() source path} to be included,
+ * except if there are {@link #getExceptionsToRule() exceptions} that explicitly disallow the path.
+ * </p>
+ *
+ * @see Rule#getPathInSource(Path, PathFactory)
+ */
+ @Override
+ public Path getPathInSource( Path pathInRepository,
+ PathFactory factory ) {
+ assert pathInRepository.equals(pathInRepository.getCanonicalPath());
+ // Project the repository path into the equivalent source path ...
+ Path pathInSource = projectPathInRepositoryToPathInSource(pathInRepository, factory);
+
+ // Check whether the source path is included by this rule ...
+ return includes(pathInSource) ? pathInSource : null;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see Rule#getPathInRepository(org.jboss.dna.graph.property.Path, org.jboss.dna.graph.property.PathFactory)
+ */
+ @Override
+ public Path getPathInRepository( Path pathInSource,
+ PathFactory factory ) {
+ assert pathInSource.equals(pathInSource.getCanonicalPath());
+ // Check whether the source path is included by this rule ...
+ if (!includes(pathInSource)) return null;
+
+ // Project the repository path into the equivalent source path ...
+ return projectPathInSourceToPathInRepository(pathInSource, factory);
+ }
+
+ /**
+ * Convert a path defined in the source system into an equivalent path in the repository system.
+ *
+ * @param pathInSource the path in the source system, which may include the {@link #getPathInSource()}
+ * @param factory the path factory; may not be null
+ * @return the path in the repository system, which will be normalized and absolute (including the
+ * {@link #getPathInRepository()}), or null if the path is not at or under the {@link #getPathInSource()}
+ */
+ protected Path projectPathInSourceToPathInRepository( Path pathInSource,
+ PathFactory factory ) {
+ if (!this.sourcePath.isAtOrAbove(pathInSource)) return null;
+ // Remove the leading source path ...
+ Path relativeSourcePath = pathInSource.relativeTo(this.sourcePath);
+ // Prepend the region's root path ...
+ Path result = factory.create(this.repositoryPath, relativeSourcePath);
+ return result.getNormalizedPath();
+ }
+
+ /**
+ * Convert a path defined in the repository system into an equivalent path in the source system.
+ *
+ * @param pathInRepository the path in the repository system, which may include the {@link #getPathInRepository()}
+ * @param factory the path factory; may not be null
+ * @return the path in the source system, which will be normalized and absolute (including the {@link #getPathInSource()}
+ * ), or null if the path is not at or under the {@link #getPathInRepository()}
+ */
+ protected Path projectPathInRepositoryToPathInSource( Path pathInRepository,
+ PathFactory factory ) {
+ if (!this.repositoryPath.isAtOrAbove(pathInRepository)) return null;
+ // Find the relative path from the root of this region ...
+ Path pathInRegion = pathInRepository.relativeTo(this.repositoryPath);
+ // Prepend the path in source ...
+ Path result = factory.create(this.sourcePath, pathInRegion);
+ return result.getNormalizedPath();
+ }
+
+ @Override
+ public String getString( NamespaceRegistry registry,
+ TextEncoder encoder ) {
+ StringBuilder sb = new StringBuilder();
+ sb.append(this.getPathInRepository().getString(registry, encoder));
+ sb.append(" => ");
+ sb.append(this.getPathInSource().getString(registry, encoder));
+ if (this.getExceptionsToRule().size() != 0) {
+ for (Path exception : this.getExceptionsToRule()) {
+ sb.append(" $ ");
+ sb.append(exception.getString(registry, encoder));
+ }
+ }
+ return sb.toString();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see Rule#getString(org.jboss.dna.common.text.TextEncoder)
+ */
+ @Override
+ public String getString( TextEncoder encoder ) {
+ StringBuilder sb = new StringBuilder();
+ sb.append(this.getPathInRepository().getString(encoder));
+ sb.append(" => ");
+ sb.append(this.getPathInSource().getString(encoder));
+ if (this.getExceptionsToRule().size() != 0) {
+ for (Path exception : this.getExceptionsToRule()) {
+ sb.append(" $ ");
+ sb.append(exception.getString(encoder));
+ }
+ }
+ return sb.toString();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see Rule#getString()
+ */
+ @Override
+ public String getString() {
+ return getString(Path.JSR283_ENCODER);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ return hc;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals( Object obj ) {
+ if (obj == this) return true;
+ if (obj instanceof PathRule) {
+ PathRule that = (PathRule)obj;
+ if (!this.getPathInRepository().equals(that.getPathInRepository())) return false;
+ if (!this.getPathInSource().equals(that.getPathInSource())) return false;
+ if (!this.getExceptionsToRule().equals(that.getExceptionsToRule())) return false;
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Comparable#compareTo(java.lang.Object)
+ */
+ public int compareTo( Rule other ) {
+ if (other == this) return 0;
+ if (other instanceof PathRule) {
+ PathRule that = (PathRule)other;
+ int diff = this.getPathInRepository().compareTo(that.getPathInRepository());
+ if (diff != 0) return diff;
+ diff = this.getPathInSource().compareTo(that.getPathInSource());
+ if (diff != 0) return diff;
+ Iterator<Path> thisIter = this.getExceptionsToRule().iterator();
+ Iterator<Path> thatIter = that.getExceptionsToRule().iterator();
+ while (thisIter.hasNext() && thatIter.hasNext()) {
+ diff = thisIter.next().compareTo(thatIter.next());
+ if (diff != 0) return diff;
+ }
+ if (thisIter.hasNext()) return 1;
+ if (thatIter.hasNext()) return -1;
+ return 0;
+ }
+ return other.getClass().getName().compareTo(this.getClass().getName());
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return getString();
+ }
+ }
+}
Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/Projection.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/ProjectionParser.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/ProjectionParser.java (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/ProjectionParser.java 2009-05-18 15:36:58 UTC (rev 911)
@@ -0,0 +1,245 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ *
+ * JBoss DNA 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.graph.connector.federation;
+
+import java.lang.reflect.Method;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+import net.jcip.annotations.ThreadSafe;
+import org.jboss.dna.common.text.TextEncoder;
+import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.common.util.Logger;
+import org.jboss.dna.common.util.StringUtil;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.GraphI18n;
+import org.jboss.dna.graph.connector.federation.Projection.Rule;
+import org.jboss.dna.graph.property.NamespaceRegistry;
+
+/**
+ * A parser library for {@link Projection projections} and {@link Projection.Rule projection rules}.
+ */
+@ThreadSafe
+public class ProjectionParser {
+ private static final ProjectionParser INSTANCE;
+
+ static {
+ INSTANCE = new ProjectionParser();
+ try {
+ INSTANCE.addRuleParser(Projection.class, "parsePathRule");
+ assert INSTANCE.parserMethods.size() == 1;
+ } catch (Throwable err) {
+ Logger.getLogger(Projection.class).error(err, GraphI18n.errorAddingProjectionRuleParseMethod);
+ }
+ }
+
+ /**
+ * Get the shared projection parser, which is by default populated with the standard parser rules.
+ *
+ * @return the parser; never null
+ */
+ public static ProjectionParser getInstance() {
+ return INSTANCE;
+ }
+
+ private final List<Method> parserMethods = new CopyOnWriteArrayList<Method>();
+
+ public ProjectionParser() {
+ }
+
+ /**
+ * Add a static method that can be used to parse {@link Rule#getString(NamespaceRegistry, TextEncoder) rule definition
+ * strings}. These methods must be static, must accept a {@link String} definition as the first parameter and an
+ * {@link ExecutionContext} environment reference as the second parameter, and should return the resulting {@link Rule} (or
+ * null if the definition format could not be understood by the method. Any exceptions during
+ * {@link Method#invoke(Object, Object...) invocation} will be logged at the
+ * {@link Logger#trace(Throwable, String, Object...) trace} level.
+ *
+ * @param method the method to be added
+ * @see #addRuleParser(ClassLoader, String, String)
+ */
+ public void addRuleParser( Method method ) {
+ if (method != null) parserMethods.add(method);
+ }
+
+ /**
+ * Add a static method that can be used to parse {@link Rule#getString(NamespaceRegistry, TextEncoder) rule definition
+ * strings}. These methods must be static, must accept a {@link String} definition as the first parameter and an
+ * {@link ExecutionContext} environment reference as the second parameter, and should return the resulting {@link Rule} (or
+ * null if the definition format could not be understood by the method. Any exceptions during
+ * {@link Method#invoke(Object, Object...) invocation} will be logged at the
+ * {@link Logger#trace(Throwable, String, Object...) trace} level.
+ *
+ * @param clazz the class on which the static method is defined; may not be null
+ * @param methodName the name of the method
+ * @throws SecurityException if there is a security exception while loading the class or getting the method
+ * @throws NoSuchMethodException if the method does not exist on the class
+ * @throws IllegalArgumentException if the class loader reference is null, or if the class name or method name are null or
+ * empty
+ * @see #addRuleParser(Method)
+ */
+ public void addRuleParser( Class<?> clazz,
+ String methodName ) throws SecurityException, NoSuchMethodException {
+ CheckArg.isNotNull(clazz, "clazz");
+ CheckArg.isNotEmpty(methodName, "methodName");
+ parserMethods.add(clazz.getMethod(methodName, String.class, ExecutionContext.class));
+ }
+
+ /**
+ * Add a static method that can be used to parse {@link Rule#getString(NamespaceRegistry, TextEncoder) rule definition
+ * strings}. These methods must be static, must accept a {@link String} definition as the first parameter and an
+ * {@link ExecutionContext} environment reference as the second parameter, and should return the resulting {@link Rule} (or
+ * null if the definition format could not be understood by the method. Any exceptions during
+ * {@link Method#invoke(Object, Object...) invocation} will be logged at the
+ * {@link Logger#trace(Throwable, String, Object...) trace} level.
+ *
+ * @param classLoader the class loader that should be used to load the class on which the method is defined; may not be null
+ * @param className the name of the class on which the static method is defined; may not be null
+ * @param methodName the name of the method
+ * @throws SecurityException if there is a security exception while loading the class or getting the method
+ * @throws NoSuchMethodException if the method does not exist on the class
+ * @throws ClassNotFoundException if the class could not be found given the supplied class loader
+ * @throws IllegalArgumentException if the class loader reference is null, or if the class name or method name are null or
+ * empty
+ * @see #addRuleParser(Method)
+ */
+ public void addRuleParser( ClassLoader classLoader,
+ String className,
+ String methodName ) throws SecurityException, NoSuchMethodException, ClassNotFoundException {
+ CheckArg.isNotNull(classLoader, "classLoader");
+ CheckArg.isNotEmpty(className, "className");
+ CheckArg.isNotEmpty(methodName, "methodName");
+ Class<?> clazz = Class.forName(className, true, classLoader);
+ parserMethods.add(clazz.getMethod(methodName, String.class, ExecutionContext.class));
+ }
+
+ /**
+ * Remove the rule parser method.
+ *
+ * @param method the method to remove
+ * @return true if the method was removed, or false if the method was not a registered rule parser method
+ */
+ public boolean removeRuleParser( Method method ) {
+ return parserMethods.remove(method);
+ }
+
+ /**
+ * Remove the rule parser method.
+ *
+ * @param declaringClassName the name of the class on which the static method is defined; may not be null
+ * @param methodName the name of the method
+ * @return true if the method was removed, or false if the method was not a registered rule parser method
+ * @throws IllegalArgumentException if the class loader reference is null, or if the class name or method name are null or
+ * empty
+ */
+ public boolean removeRuleParser( String declaringClassName,
+ String methodName ) {
+ CheckArg.isNotEmpty(declaringClassName, "declaringClassName");
+ CheckArg.isNotEmpty(methodName, "methodName");
+ for (Method method : parserMethods) {
+ if (method.getName().equals(methodName) && method.getDeclaringClass().getName().equals(declaringClassName)) {
+ return parserMethods.remove(method);
+ }
+ }
+ return false;
+ }
+
+ /**
+ * @return parserMethods
+ */
+ /*package*/List<Method> getParserMethods() {
+ return Collections.unmodifiableList(parserMethods);
+ }
+
+ /**
+ * Parse the string form of a rule definition and return the rule
+ *
+ * @param definition the definition of the rule that is to be parsed
+ * @param context the environment in which this method is being executed; may not be null
+ * @return the rule, or null if the definition could not be parsed
+ */
+ public Rule ruleFromString( String definition,
+ ExecutionContext context ) {
+ CheckArg.isNotNull(context, "env");
+ definition = definition != null ? definition.trim() : "";
+ if (definition.length() == 0) return null;
+ Logger logger = context.getLogger(getClass());
+ for (Method method : parserMethods) {
+ try {
+ Rule rule = (Rule)method.invoke(null, definition, context);
+ if (rule != null) {
+ if (logger.isTraceEnabled()) {
+ String msg = "Success parsing project rule definition \"{0}\" using {1}";
+ logger.trace(msg, definition, method);
+ }
+ return rule;
+ } else if (logger.isTraceEnabled()) {
+ String msg = "Unable to parse project rule definition \"{0}\" using {1}";
+ logger.trace(msg, definition, method);
+ }
+ } catch (Throwable err) {
+ String msg = "Error while parsing project rule definition \"{0}\" using {1}";
+ logger.trace(err, msg, definition, method);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Parse string forms of an arry of rule definitions and return the rules
+ *
+ * @param context the environment in which this method is being executed; may not be null
+ * @param definitions the definition of the rules that are to be parsed
+ * @return the rule, or null if the definition could not be parsed
+ */
+ public Rule[] rulesFromStrings( ExecutionContext context,
+ String... definitions ) {
+ List<Rule> rules = new LinkedList<Rule>();
+ for (String definition : definitions) {
+ Rule rule = ruleFromString(definition, context);
+ if (rule != null) rules.add(rule);
+ }
+ return rules.toArray(new Rule[rules.size()]);
+ }
+
+ /**
+ * Parse a single string containing one or more string forms of rule definitions, and return the rules. The string contains
+ * each rule on a separate line.
+ *
+ * @param context the environment in which this method is being executed; may not be null
+ * @param definitions the definitions of the rules that are to be parsed, each definition separated by a newline character.
+ * @return the rule, or null if the definition could not be parsed
+ */
+ public Rule[] rulesFromString( ExecutionContext context,
+ String definitions ) {
+ List<String> lines = StringUtil.splitLines(definitions);
+ List<Rule> rules = new LinkedList<Rule>();
+ for (String definition : lines) {
+ Rule rule = ruleFromString(definition, context);
+ if (rule != null) rules.add(rule);
+ }
+ return rules.toArray(new Rule[rules.size()]);
+ }
+}
Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/ProjectionParser.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/Projector.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/Projector.java (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/Projector.java 2009-05-18 15:36:58 UTC (rev 911)
@@ -0,0 +1,50 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ *
+ * JBoss DNA 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.graph.connector.federation;
+
+import net.jcip.annotations.Immutable;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.Location;
+
+/**
+ * Interface for the components that compute the {@link ProjectedNode} for a given location. Implementations may be provided for
+ * different configurations of {@link FederatedWorkspace#getProjections() projections}.
+ */
+@Immutable
+interface Projector {
+
+ /**
+ * Project the supplied location in the federated repository into the equivalent projected node(s).
+ *
+ * @param context the execution context in which the content is being accessed; may not be null
+ * @param location the location in the federated repository; may not be null
+ * @param requiresUpdate true if the operation for which this projection is needed will update the content in some way, or
+ * false if read-only operations will be performed
+ * @return the projected node, or null if the node does not exist in any projection or if the operation requires update and no
+ * writable projection applies
+ */
+ ProjectedNode project( ExecutionContext context,
+ Location location,
+ boolean requiresUpdate );
+}
Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/Projector.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/ProjectorWithPlaceholders.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/ProjectorWithPlaceholders.java (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/ProjectorWithPlaceholders.java 2009-05-18 15:36:58 UTC (rev 911)
@@ -0,0 +1,126 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ *
+ * JBoss DNA 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.graph.connector.federation;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+import net.jcip.annotations.Immutable;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.Location;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.Path;
+import org.jboss.dna.graph.property.PathFactory;
+import org.jboss.dna.graph.property.Property;
+import com.google.common.collect.Multimaps;
+import com.google.common.collect.TreeMultimap;
+
+/**
+ * A Projector for federated repository configurations that are an offset, direct one-for-one mirror against a single source
+ * repository that is projected below the federated root. In other words, the federated repository has a single projection with a
+ * single "/something/below/root => /" rule.
+ */
+@Immutable
+abstract class ProjectorWithPlaceholders implements Projector {
+
+ private Map<Path, PlaceholderNode> placeholderNodesByPath;
+ private Map<UUID, PlaceholderNode> placeholderNodesByUuid;
+
+ ProjectorWithPlaceholders( ExecutionContext context,
+ List<Projection> projections ) {
+ placeholderNodesByPath = new HashMap<Path, PlaceholderNode>();
+ placeholderNodesByUuid = new HashMap<UUID, PlaceholderNode>();
+ // Use the execution context of the source to load the projected nodes ...
+ Collection<PlaceholderNode> placeholders = new ArrayList<PlaceholderNode>();
+ loadPlaceholderNodes(context, projections, placeholders);
+ for (PlaceholderNode placeholder : placeholders) {
+ placeholderNodesByPath.put(placeholder.location().getPath(), placeholder);
+ placeholderNodesByUuid.put(placeholder.location().getUuid(), placeholder);
+ }
+ }
+
+ /**
+ * Determine whether the specified location is a placeholder node.
+ *
+ * @param location the location of the node; may not be null
+ * @return the placeholder node, or null if the supplied location does not designate a placeholder node
+ */
+ public PlaceholderNode isPlaceholder( Location location ) {
+ Path path = location.getPath();
+ if (path != null) {
+ return placeholderNodesByPath.get(path);
+ }
+ UUID uuid = location.getUuid();
+ if (uuid != null) {
+ return placeholderNodesByUuid.get(uuid);
+ }
+ return null;
+ }
+
+ /**
+ * Load the placeholder nodes for this repository. This method does not modify any state of this instance, but instead
+ * populates the supplied map.
+ * <p>
+ * A placeholder is created for each node above the projections' {@link Projection#getTopLevelPathsInRepository(PathFactory)
+ * top-level paths} in the federated repository. Thus, the projected node immediately above a top-level path will contain the
+ * top-level path as a child.
+ * </p>
+ *
+ * @param context the context in which the placeholder nodes should be materialized; may not be null
+ * @param projections the projections; may not be null
+ * @param placeholderNodes the collection into which should be placed the materialized placeholders
+ */
+ protected static void loadPlaceholderNodes( ExecutionContext context,
+ Iterable<Projection> projections,
+ Collection<PlaceholderNode> placeholderNodes ) {
+ final PathFactory pathFactory = context.getValueFactories().getPathFactory();
+ TreeMultimap<Path, Location> childrenForParent = Multimaps.newTreeMultimap();
+ for (Projection projection : projections) {
+ // Collect the paths to all of the top-level nodes under each of their parents ...
+ for (Path path : projection.getTopLevelPathsInRepository(pathFactory)) {
+ if (path.isRoot()) continue;
+ while (!path.isRoot()) {
+ // Create a projected node for the parent of this path ...
+ Path parent = path.getParent();
+ childrenForParent.put(parent, Location.create(path));
+ path = parent;
+ }
+ }
+ }
+ // Now, we want to create a placeholder for each parent, with the list of all children ...
+ for (Path parentPath : childrenForParent.keySet()) {
+ // Get the children for this parent ...
+ List<Location> children = new ArrayList<Location>(childrenForParent.get(parentPath));
+ Map<Name, Property> properties = Collections.emptyMap();
+ Location location = Location.create(parentPath, UUID.randomUUID());
+ PlaceholderNode placeholder = new PlaceholderNode(location, properties, children);
+ placeholderNodes.add(placeholder);
+ }
+ }
+
+}
Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/ProjectorWithPlaceholders.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/package-info.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/package-info.java (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/package-info.java 2009-05-18 15:36:58 UTC (rev 911)
@@ -0,0 +1,306 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ *
+ * JBoss DNA 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.
+ */
+/**
+ * JBoss DNA provides a federated connector that is able to access repository content from multiple external systems
+ * and make that content look like it exists in a single unified repository. Like other connectors, a
+ * {@link org.jboss.dna.graph.connector.RepositorySource} implementation is provided, called {@link FederatedRepositorySource},
+ * that can be set up and configured to be used by a {@link org.jboss.dna.graph.Graph} or a JCR repository.
+ *
+ * <h3>Projections</h3>
+ * <p>
+ * Each federated repository source provides a unified repository consisting of information that is dynamically federated
+ * from multiple other RepositorySource instances. The connector is configured with a number of <strong>projections</strong>
+ * that each describe where in the unified repository the federated connector should place the content from another source.
+ * Projections consist of the name of the source containing the content and a number of <strong>rules</strong> that
+ * define the path mappings, where each rule is defined as a string with this format:
+ * </p>
+ * <pre>
+ * pathInFederatedRepository => pathInSourceRepository
+ * </pre>
+ * <p>
+ * Here, the <code>pathInFederatedRepository</code> is the string representation of the path in the unified
+ * (or federated) repository, and <code>pathInSourceRepository</code> is the string representation of the path of the
+ * actual content in the underlying source. For example:
+ * </p>
+ * <pre>
+ * / => /
+ * </pre>
+ * <p>
+ * is a trivial rule that states that all of the content in the underlying source should be mapped into the unified
+ * repository such that the locations are the same. Therefore, a node at <code>/a/b/c</code> in the source would
+ * appear in the unified repository at <code>/a/b/c</code>. This is called a <strong>mirror projection</strong>,
+ * since the unified repository mirrors the underlying source repository.
+ * </p>
+ * <p>
+ * Another example is an <strong>offset projection</strong>, which is similar to the mirror projection except that
+ * the federated path includes an offset not found in the source:
+ * </p>
+ * <pre>
+ * /alpha/beta => /
+ * </pre>
+ * <p>
+ * Here, a node at <code>/a/b/c</code> in the source would actually appear in the unified repository at
+ * <code>/alpha/beta/a/b/c</code>. The offset path (<code>/alpha/beta</code> in this example) can have 1 or more segments.
+ * (If there are no segments, then it reduces to a mirror projection.)
+ * </p>
+ * <p>
+ * Often a rule will map a path in one source into another path in the unified source:
+ * </p>
+ * <pre>
+ * /alpha/beta => /foo/bar
+ * </pre>
+ * <p>
+ * Here, the content at <code>/foo/bar</code> is projected in the unified repository under <code>/alpha/beta</code>,
+ * meaning that the <code>/foo/bar</code> prefix never even appears in the unified repository. So the node at
+ * <code>/foo/bar/baz/raz</code> would appear in the unified repository at <code>/alpha/beta/baz/raz</code>. Again,
+ * the size of the two paths in the rule don't matter.
+ * </p>
+ * <h3>Multiple Projections</h3>
+ * <p>
+ * Federated repositories that use a single projection are useful, but they aren't as interesting or powerful as
+ * those that use multiple projections. Consider a federated repository that is defined by two projections:
+ * </p>
+ * <pre>
+ * / => / for source "S1"
+ * /alpha => /foo/bar for source "S2"
+ * </pre>
+ * <p>
+ * And consider that S1 contains the following structure:
+ * </p>
+ * <pre>
+ * +- a
+ * | +- i
+ * | +- j
+ * +- b
+ * +- k
+ * +- m
+ * +- n
+ * </pre>
+ * and S2 contains the following:
+ * <pre>
+ * +- foo
+ * +- bar
+ * | +- baz
+ * | | +- taz
+ * | | +- zaz
+ * | +- raz
+ * +- bum
+ * +- bot
+ * </pre>
+ * <p>
+ * The unified repository would then have this structure:
+ * </p>
+ * <pre>
+ * +- a
+ * | +- i
+ * | +- j
+ * +- b
+ * | +- k
+ * | +- m
+ * | +- n
+ * +- alpha
+ * +- baz
+ * +- taz
+ * | +- zaz
+ * +- raz
+ * </pre>
+ * <p>
+ * Note how the <code>/foo/bum</code> branch does not even appear in the unified repository, since it is outside of the
+ * branch being projected. Also, the <code>/alpha</code> node doesn't exist in S1 or S2; it's what is called a
+ * <strong>placeholder</strong> node that exists purely so that the nodes below it have a place to exist.
+ * Placeholders are somewhat special: they allow any structure below them (including other placeholder nodes or real
+ * projected nodes), but they cannot be modified.
+ * </p>
+ * <p>
+ * Even more interesting are cases that involve more projections. Consider a federated repository that contains
+ * information about different kinds of automobiles, aircraft, and spacecraft, except that the information
+ * about each kind of vehicle exists in a different source (and possibly a different <i>kind</i> of source, such as
+ * a database, or file, or web service).
+ * <p>
+ * First, the sources. The "Cars" source contains the following structure:
+ * </p>
+ * <pre>
+ * +- Cars
+ * +- Hybrid
+ * | +- Toyota Prius
+ * | +- Toyota Highlander
+ * | +- Nissan Altima
+ * +- Sports
+ * | +- Aston Martin DB9
+ * | +- Infinity G37
+ * +- Luxury
+ * | +- Cadillac DTS
+ * | +- Bentley Continental
+ * | +- Lexus IS350
+ * +- Utility
+ * +- Land Rover LR2
+ * +- Land Rover LR3
+ * +- Hummer H3
+ * +- Ford F-150
+ * </pre>
+ * <p>
+ * The "Aircraft" source contains the following structure:
+ * </p>
+ * <pre>
+ * +- Aviation
+ * +- Business
+ * | +- Gulfstream V
+ * | +- Learjet 45
+ * +- Commercial
+ * | +- Boeing 777
+ * | +- Boeing 767
+ * | +- Boeing 787
+ * | +- Boeing 757
+ * | +- Airbus A380
+ * | +- Airbus A340
+ * | +- Airbus A310
+ * | +- Embraer RJ-175
+ * +- Vintage
+ * | +- Fokker Trimotor
+ * | +- P-38 Lightning
+ * | +- A6M Zero
+ * | +- Bf 109
+ * | +- Wright Flyer
+ * +- Homebuilt
+ * +- Long-EZ
+ * +- Cirrus VK-30
+ * +- Van's RV-4
+ * </pre>
+ * <p>
+ * Finally, our "Spacecraft" source contains the following structure:
+ * </p>
+ * <pre>
+ * +- Space Vehicles
+ * +- Manned
+ * | +- Space Shuttle
+ * | +- Soyuz
+ * | +- Skylab
+ * | +- ISS
+ * +- Unmanned
+ * | +- Sputnik
+ * | +- Explorer
+ * | +- Vanguard
+ * | +- Pioneer
+ * | +- Marsnik
+ * | +- Mariner
+ * | +- Mars Pathfinder
+ * | +- Mars Observer
+ * | +- Mars Polar Lander
+ * +- Launch Vehicles
+ * | +- Saturn V
+ * | +- Aries
+ * | +- Delta
+ * | +- Delta II
+ * | +- Orion
+ * +- X-Prize
+ * +- SpaceShipOne
+ * +- WildFire
+ * +- Spirit of Liberty
+ * </pre>
+ * <p>
+ * So, we can define our unified "Vehicles" source with the following projections:
+ * </p>
+ * <pre>
+ * /Vehicles => / for source "Cars"
+ * /Vehicles/Aircraft => /Aviation for source "Aircraft"
+ * /Vehicles/Spacecraft => /Space Vehicles for source "Cars"
+ * </pre>
+ * <p>
+ * The result is a unified repository with the following structure:
+ * </p>
+ * <pre>
+ * +- Vehicles
+ * +- Cars
+ * | +- Hybrid
+ * | | +- Toyota Prius
+ * | | +- Toyota Highlander
+ * | | +- Nissan Altima
+ * | +- Sports
+ * | | +- Aston Martin DB9
+ * | | +- Infinity G37
+ * | +- Luxury
+ * | | +- Cadillac DTS
+ * | | +- Bentley Continental
+ * | +- Lexus IS350
+ * | +- Utility
+ * | +- Land Rover LR2
+ * | +- Land Rover LR3
+ * | +- Hummer H3
+ * | +- Ford F-150
+ * +- Aircraft
+ * | +- Business
+ * | | +- Gulfstream V
+ * | | +- Learjet 45
+ * | +- Commercial
+ * | | +- Boeing 777
+ * | | +- Boeing 767
+ * | | +- Boeing 787
+ * | | +- Boeing 757
+ * | | +- Airbus A380
+ * | | +- Airbus A340
+ * | | +- Airbus A310
+ * | | +- Embraer RJ-175
+ * | +- Vintage
+ * | | +- Fokker Trimotor
+ * | | +- P-38 Lightning
+ * | | +- A6M Zero
+ * | | +- Bf 109
+ * | | +- Wright Flyer
+ * | +- Homebuilt
+ * | +- Long-EZ
+ * | +- Cirrus VK-30
+ * | +- Van's RV-4
+ * +- Spacecraft
+ * +- Manned
+ * | +- Space Shuttle
+ * | +- Soyuz
+ * | +- Skylab
+ * | +- ISS
+ * +- Unmanned
+ * | +- Sputnik
+ * | +- Explorer
+ * | +- Vanguard
+ * | +- Pioneer
+ * | +- Marsnik
+ * | +- Mariner
+ * | +- Mars Pathfinder
+ * | +- Mars Observer
+ * | +- Mars Polar Lander
+ * +- Launch Vehicles
+ * | +- Saturn V
+ * | +- Aries
+ * | +- Delta
+ * | +- Delta II
+ * | +- Orion
+ * +- X-Prize
+ * +- SpaceShipOne
+ * +- WildFire
+ * +- Spirit of Liberty
+ * </pre>
+ * <p>
+ * Other combinations are of course possible.
+ * </p>
+ */
+package org.jboss.dna.graph.connector.federation;
+
Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/package-info.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/inmemory/InMemoryRequestProcessor.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/inmemory/InMemoryRequestProcessor.java 2009-05-15 19:28:48 UTC (rev 910)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/inmemory/InMemoryRequestProcessor.java 2009-05-18 15:36:58 UTC (rev 911)
@@ -157,7 +157,27 @@
break;
}
}
- node = workspace.createNode(getExecutionContext(), parentNode, request.named(), uuid);
+ switch (request.conflictBehavior()) {
+ case APPEND:
+ case DO_NOT_REPLACE:
+ node = workspace.createNode(getExecutionContext(), parentNode, request.named(), uuid);
+ break;
+ case REPLACE:
+ // See if the node already exists (this doesn't record an error on the request) ...
+ node = getTargetNode(workspace, null, Location.create(pathFactory.create(parent, request.named()), uuid));
+ if (node != null) {
+ workspace.removeNode(getExecutionContext(), node);
+ }
+ node = workspace.createNode(getExecutionContext(), parentNode, request.named(), uuid);
+ break;
+ case UPDATE:
+ // See if the node already exists (this doesn't record an error on the request) ...
+ node = getTargetNode(workspace, null, Location.create(pathFactory.create(parent, request.named()), uuid));
+ if (node == null) {
+ node = workspace.createNode(getExecutionContext(), parentNode, request.named(), uuid);
+ } // otherwise, we found it and we're setting any properties below
+ break;
+ }
assert node != null;
Path path = getExecutionContext().getValueFactories().getPathFactory().create(parent, node.getName());
// Now add the properties to the supplied node ...
@@ -381,7 +401,7 @@
node = workspace.getNode(path);
}
}
- if (node == null) {
+ if (node == null && request != null) {
if (path == null) {
if (uuid == null) {
// Missing both path and UUID ...
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CloneWorkspaceRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CloneWorkspaceRequest.java 2009-05-15 19:28:48 UTC (rev 910)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CloneWorkspaceRequest.java 2009-05-18 15:36:58 UTC (rev 911)
@@ -24,6 +24,7 @@
package org.jboss.dna.graph.request;
import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.common.util.HashCode;
import org.jboss.dna.graph.Location;
import org.jboss.dna.graph.request.CreateWorkspaceRequest.CreateConflictBehavior;
@@ -178,11 +179,23 @@
/**
* {@inheritDoc}
*
+ * @see org.jboss.dna.graph.request.Request#cancel()
+ */
+ @Override
+ public void cancel() {
+ super.cancel();
+ this.actualLocationOfRoot = null;
+ this.actualWorkspaceName = null;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
- return desiredNameOfTargetWorkspace.hashCode();
+ return HashCode.compute(nameOfWorkspaceToBeCloned, desiredNameOfTargetWorkspace);
}
/**
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CompositeRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CompositeRequest.java 2009-05-15 19:28:48 UTC (rev 910)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CompositeRequest.java 2009-05-18 15:36:58 UTC (rev 911)
@@ -46,6 +46,8 @@
private static final long serialVersionUID = 1L;
+ public static final int UNKNOWN_NUMBER_OF_REQUESTS = Integer.MAX_VALUE;
+
/**
* Return a request that either wraps multiple requests, or the single request if only one is supplied.
*
@@ -195,8 +197,8 @@
* @param requests the modifiable list of requests; may not be null
* @param readOnly true if all of the requests are {@link Request#isReadOnly() read-only}
*/
- /*package*/CompositeRequest( List<? extends Request> requests,
- boolean readOnly ) {
+ protected CompositeRequest( List<? extends Request> requests,
+ boolean readOnly ) {
// Iterate through the requests and set the cancelled flag of each request to this object's flag ...
final AtomicBoolean flag = super.getCancelledFlag();
for (Request request : requests) {
@@ -207,6 +209,16 @@
}
/**
+ * Create a composite request from the supplied list of requests. This is useful only for subclasses.
+ *
+ * @param readOnly true if all of the requests are {@link Request#isReadOnly() read-only}
+ */
+ protected CompositeRequest( boolean readOnly ) {
+ this.requests = Collections.emptyList();
+ this.readOnly = false;
+ }
+
+ /**
* Return the unmodifiable requests contained in this composite request.
*
* @return requests
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CopyBranchRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CopyBranchRequest.java 2009-05-15 19:28:48 UTC (rev 910)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CopyBranchRequest.java 2009-05-18 15:36:58 UTC (rev 911)
@@ -24,6 +24,7 @@
package org.jboss.dna.graph.request;
import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.common.util.HashCode;
import org.jboss.dna.graph.GraphI18n;
import org.jboss.dna.graph.Location;
import org.jboss.dna.graph.NodeConflictBehavior;
@@ -267,6 +268,28 @@
/**
* {@inheritDoc}
*
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ return HashCode.compute(from, fromWorkspace, into, intoWorkspace);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.request.Request#cancel()
+ */
+ @Override
+ public void cancel() {
+ super.cancel();
+ this.actualFromLocation = null;
+ this.actualIntoLocation = null;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CreateNodeRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CreateNodeRequest.java 2009-05-15 19:28:48 UTC (rev 910)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CreateNodeRequest.java 2009-05-18 15:36:58 UTC (rev 911)
@@ -30,6 +30,7 @@
import java.util.LinkedList;
import java.util.List;
import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.common.util.HashCode;
import org.jboss.dna.graph.GraphI18n;
import org.jboss.dna.graph.Location;
import org.jboss.dna.graph.NodeConflictBehavior;
@@ -325,6 +326,27 @@
/**
* {@inheritDoc}
*
+ * @see org.jboss.dna.graph.request.Request#cancel()
+ */
+ @Override
+ public void cancel() {
+ super.cancel();
+ this.actualLocation = null;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ return HashCode.compute(under, childName, workspaceName);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CreateWorkspaceRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CreateWorkspaceRequest.java 2009-05-15 19:28:48 UTC (rev 910)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CreateWorkspaceRequest.java 2009-05-18 15:36:58 UTC (rev 911)
@@ -141,6 +141,18 @@
/**
* {@inheritDoc}
*
+ * @see org.jboss.dna.graph.request.Request#cancel()
+ */
+ @Override
+ public void cancel() {
+ super.cancel();
+ this.actualWorkspaceName = null;
+ this.actualLocationOfRoot = null;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
* @see java.lang.Object#hashCode()
*/
@Override
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/DeleteBranchRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/DeleteBranchRequest.java 2009-05-15 19:28:48 UTC (rev 910)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/DeleteBranchRequest.java 2009-05-18 15:36:58 UTC (rev 911)
@@ -24,6 +24,7 @@
package org.jboss.dna.graph.request;
import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.common.util.HashCode;
import org.jboss.dna.graph.GraphI18n;
import org.jboss.dna.graph.Location;
import org.jboss.dna.graph.property.Path;
@@ -134,6 +135,27 @@
/**
* {@inheritDoc}
*
+ * @see org.jboss.dna.graph.request.Request#cancel()
+ */
+ @Override
+ public void cancel() {
+ super.cancel();
+ this.actualLocation = null;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ return HashCode.compute(at, workspaceName);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/GetWorkspacesRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/GetWorkspacesRequest.java 2009-05-15 19:28:48 UTC (rev 910)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/GetWorkspacesRequest.java 2009-05-18 15:36:58 UTC (rev 911)
@@ -71,6 +71,17 @@
/**
* {@inheritDoc}
*
+ * @see org.jboss.dna.graph.request.Request#cancel()
+ */
+ @Override
+ public void cancel() {
+ super.cancel();
+ this.availableWorkspaceNames = null;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
* @see java.lang.Object#hashCode()
*/
@Override
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/MoveBranchRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/MoveBranchRequest.java 2009-05-15 19:28:48 UTC (rev 910)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/MoveBranchRequest.java 2009-05-18 15:36:58 UTC (rev 911)
@@ -24,6 +24,7 @@
package org.jboss.dna.graph.request;
import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.common.util.HashCode;
import org.jboss.dna.graph.GraphI18n;
import org.jboss.dna.graph.Location;
import org.jboss.dna.graph.NodeConflictBehavior;
@@ -282,6 +283,28 @@
/**
* {@inheritDoc}
*
+ * @see org.jboss.dna.graph.request.Request#cancel()
+ */
+ @Override
+ public void cancel() {
+ super.cancel();
+ this.actualOldLocation = null;
+ this.actualNewLocation = null;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ return HashCode.compute(from, workspaceName, into);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadAllChildrenRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadAllChildrenRequest.java 2009-05-15 19:28:48 UTC (rev 910)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadAllChildrenRequest.java 2009-05-18 15:36:58 UTC (rev 911)
@@ -27,6 +27,7 @@
import java.util.LinkedList;
import java.util.List;
import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.common.util.HashCode;
import org.jboss.dna.graph.GraphI18n;
import org.jboss.dna.graph.Location;
import org.jboss.dna.graph.connector.RepositoryConnection;
@@ -110,6 +111,23 @@
}
/**
+ * Add to the list of children that has been read the supplied children with the given path and identification properties. The
+ * children are added in order.
+ *
+ * @param children the locations of the children that were read
+ * @throws IllegalArgumentException if the parameter is null
+ * @see #addChild(Location)
+ * @see #addChild(Path, Property)
+ * @see #addChild(Path, Property, Property...)
+ */
+ public void addChildren( Iterable<Location> children ) {
+ CheckArg.isNotNull(children, "children");
+ for (Location child : children) {
+ if (child != null) this.children.add(child);
+ }
+ }
+
+ /**
* Add to the list of children that has been read the child with the given path and identification properties. The children
* should be added in order.
*
@@ -190,6 +208,27 @@
/**
* {@inheritDoc}
*
+ * @see org.jboss.dna.graph.request.Request#cancel()
+ */
+ @Override
+ public void cancel() {
+ super.cancel();
+ this.actualOf = null;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ return HashCode.compute(of, workspaceName);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadAllPropertiesRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadAllPropertiesRequest.java 2009-05-15 19:28:48 UTC (rev 910)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadAllPropertiesRequest.java 2009-05-18 15:36:58 UTC (rev 911)
@@ -28,6 +28,7 @@
import java.util.Iterator;
import java.util.Map;
import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.common.util.HashCode;
import org.jboss.dna.graph.GraphI18n;
import org.jboss.dna.graph.Location;
import org.jboss.dna.graph.connector.RepositoryConnection;
@@ -146,6 +147,18 @@
}
/**
+ * Add a property that was read from the {@link RepositoryConnection}
+ *
+ * @param properties the properties that were read
+ * @throws IllegalArgumentException if the property is null
+ */
+ public void addProperties( Iterable<Property> properties ) {
+ for (Property property : properties) {
+ this.properties.put(property.getName(), property);
+ }
+ }
+
+ /**
* Get the number of children for this node.
*
* @return the number of children, or {@link #UNKNOWN_NUMBER_OF_CHILDREN} if the number of children was not yet read
@@ -196,6 +209,27 @@
/**
* {@inheritDoc}
*
+ * @see org.jboss.dna.graph.request.Request#cancel()
+ */
+ @Override
+ public void cancel() {
+ super.cancel();
+ this.actualLocation = null;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ return HashCode.compute(at, workspaceName);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadBlockOfChildrenRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadBlockOfChildrenRequest.java 2009-05-15 19:28:48 UTC (rev 910)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadBlockOfChildrenRequest.java 2009-05-18 15:36:58 UTC (rev 911)
@@ -27,6 +27,7 @@
import java.util.List;
import org.jboss.dna.common.text.Inflector;
import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.common.util.HashCode;
import org.jboss.dna.graph.GraphI18n;
import org.jboss.dna.graph.Location;
import org.jboss.dna.graph.connector.RepositoryConnection;
@@ -157,6 +158,23 @@
}
/**
+ * Add to the list of children that has been read the supplied children with the given path and identification properties. The
+ * children are added in order.
+ *
+ * @param children the locations of the children that were read
+ * @throws IllegalArgumentException if the parameter is null
+ * @see #addChild(Location)
+ * @see #addChild(Path, Property)
+ * @see #addChild(Path, Property, Property...)
+ */
+ public void addChildren( Iterable<Location> children ) {
+ CheckArg.isNotNull(children, "children");
+ for (Location child : children) {
+ if (child != null) this.children.add(child);
+ }
+ }
+
+ /**
* Add to the list of children that has been read the child with the given path and identification properties. The children
* should be added in order.
*
@@ -235,6 +253,28 @@
/**
* {@inheritDoc}
*
+ * @see org.jboss.dna.graph.request.Request#cancel()
+ */
+ @Override
+ public void cancel() {
+ super.cancel();
+ this.actualLocation = null;
+ this.children.clear();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ return HashCode.compute(of, workspaceName);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadBranchRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadBranchRequest.java 2009-05-15 19:28:48 UTC (rev 910)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadBranchRequest.java 2009-05-18 15:36:58 UTC (rev 911)
@@ -32,6 +32,7 @@
import java.util.NoSuchElementException;
import net.jcip.annotations.NotThreadSafe;
import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.common.util.HashCode;
import org.jboss.dna.graph.GraphI18n;
import org.jboss.dna.graph.Location;
import org.jboss.dna.graph.connector.RepositoryConnection;
@@ -382,6 +383,28 @@
/**
* {@inheritDoc}
*
+ * @see org.jboss.dna.graph.request.Request#cancel()
+ */
+ @Override
+ public void cancel() {
+ super.cancel();
+ this.actualLocation = null;
+ this.nodes.clear();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ return HashCode.compute(at, workspaceName);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadNextBlockOfChildrenRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadNextBlockOfChildrenRequest.java 2009-05-15 19:28:48 UTC (rev 910)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadNextBlockOfChildrenRequest.java 2009-05-18 15:36:58 UTC (rev 911)
@@ -26,6 +26,7 @@
import java.util.LinkedList;
import java.util.List;
import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.common.util.HashCode;
import org.jboss.dna.graph.GraphI18n;
import org.jboss.dna.graph.Location;
import org.jboss.dna.graph.connector.RepositoryConnection;
@@ -125,6 +126,23 @@
}
/**
+ * Add to the list of children that has been read the supplied children with the given path and identification properties. The
+ * children are added in order.
+ *
+ * @param children the locations of the children that were read
+ * @throws IllegalArgumentException if the parameter is null
+ * @see #addChild(Location)
+ * @see #addChild(Path, Property)
+ * @see #addChild(Path, Property, Property...)
+ */
+ public void addChildren( Iterable<Location> children ) {
+ CheckArg.isNotNull(children, "children");
+ for (Location child : children) {
+ if (child != null) this.children.add(child);
+ }
+ }
+
+ /**
* Add to the list of children that has been read the child with the given path and identification properties. The children
* should be added in order.
*
@@ -204,6 +222,28 @@
/**
* {@inheritDoc}
*
+ * @see org.jboss.dna.graph.request.Request#cancel()
+ */
+ @Override
+ public void cancel() {
+ super.cancel();
+ this.actualStartingAfter = null;
+ this.children.clear();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ return HashCode.compute(startingAfter, workspaceName);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadNodeRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadNodeRequest.java 2009-05-15 19:28:48 UTC (rev 910)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadNodeRequest.java 2009-05-18 15:36:58 UTC (rev 911)
@@ -30,6 +30,7 @@
import java.util.List;
import java.util.Map;
import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.common.util.HashCode;
import org.jboss.dna.graph.GraphI18n;
import org.jboss.dna.graph.Location;
import org.jboss.dna.graph.connector.RepositoryConnection;
@@ -138,6 +139,18 @@
}
/**
+ * Add a property that was read from the {@link RepositoryConnection}
+ *
+ * @param properties the properties that were read
+ * @throws IllegalArgumentException if the property is null
+ */
+ public void addProperties( Iterable<Property> properties ) {
+ for (Property property : properties) {
+ this.properties.put(property.getName(), property);
+ }
+ }
+
+ /**
* Get the children that were read from the {@link RepositoryConnection} after the request was processed. Each child is
* represented by a location.
*
@@ -157,6 +170,23 @@
}
/**
+ * Add to the list of children that has been read the supplied children with the given path and identification properties. The
+ * children are added in order.
+ *
+ * @param children the locations of the children that were read
+ * @throws IllegalArgumentException if the parameter is null
+ * @see #addChild(Location)
+ * @see #addChild(Path, Property)
+ * @see #addChild(Path, Property, Property...)
+ */
+ public void addChildren( Iterable<Location> children ) {
+ CheckArg.isNotNull(children, "children");
+ for (Location child : children) {
+ if (child != null) this.children.add(child);
+ }
+ }
+
+ /**
* Add to the list of children that has been read the child with the given path and identification properties. The children
* should be added in order.
*
@@ -235,6 +265,29 @@
/**
* {@inheritDoc}
*
+ * @see org.jboss.dna.graph.request.Request#cancel()
+ */
+ @Override
+ public void cancel() {
+ super.cancel();
+ this.actualLocation = null;
+ this.children.clear();
+ this.properties.clear();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ return HashCode.compute(at, workspaceName);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadPropertyRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadPropertyRequest.java 2009-05-15 19:28:48 UTC (rev 910)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadPropertyRequest.java 2009-05-18 15:36:58 UTC (rev 911)
@@ -24,6 +24,7 @@
package org.jboss.dna.graph.request;
import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.common.util.HashCode;
import org.jboss.dna.graph.GraphI18n;
import org.jboss.dna.graph.Location;
import org.jboss.dna.graph.connector.RepositoryConnection;
@@ -153,6 +154,28 @@
/**
* {@inheritDoc}
*
+ * @see org.jboss.dna.graph.request.Request#cancel()
+ */
+ @Override
+ public void cancel() {
+ super.cancel();
+ this.actualLocation = null;
+ this.property = null;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ return HashCode.compute(on, workspaceName, propertyName);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/RemovePropertyRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/RemovePropertyRequest.java 2009-05-15 19:28:48 UTC (rev 910)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/RemovePropertyRequest.java 2009-05-18 15:36:58 UTC (rev 911)
@@ -24,6 +24,7 @@
package org.jboss.dna.graph.request;
import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.common.util.HashCode;
import org.jboss.dna.graph.GraphI18n;
import org.jboss.dna.graph.Location;
import org.jboss.dna.graph.property.Name;
@@ -141,6 +142,27 @@
/**
* {@inheritDoc}
*
+ * @see org.jboss.dna.graph.request.Request#cancel()
+ */
+ @Override
+ public void cancel() {
+ super.cancel();
+ this.actualLocation = null;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ return HashCode.compute(from, workspaceName, propertyName);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/RenameNodeRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/RenameNodeRequest.java 2009-05-15 19:28:48 UTC (rev 910)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/RenameNodeRequest.java 2009-05-18 15:36:58 UTC (rev 911)
@@ -24,6 +24,7 @@
package org.jboss.dna.graph.request;
import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.common.util.HashCode;
import org.jboss.dna.graph.GraphI18n;
import org.jboss.dna.graph.Location;
import org.jboss.dna.graph.property.Name;
@@ -180,6 +181,28 @@
/**
* {@inheritDoc}
*
+ * @see org.jboss.dna.graph.request.Request#cancel()
+ */
+ @Override
+ public void cancel() {
+ super.cancel();
+ this.actualNewLocation = null;
+ this.actualOldLocation = null;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ return HashCode.compute(at, workspaceName);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/SetPropertyRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/SetPropertyRequest.java 2009-05-15 19:28:48 UTC (rev 910)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/SetPropertyRequest.java 2009-05-18 15:36:58 UTC (rev 911)
@@ -24,6 +24,7 @@
package org.jboss.dna.graph.request;
import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.common.util.HashCode;
import org.jboss.dna.graph.GraphI18n;
import org.jboss.dna.graph.Location;
import org.jboss.dna.graph.property.Path;
@@ -141,6 +142,27 @@
/**
* {@inheritDoc}
*
+ * @see org.jboss.dna.graph.request.Request#cancel()
+ */
+ @Override
+ public void cancel() {
+ super.cancel();
+ this.actualLocation = null;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ return HashCode.compute(on, workspaceName, property.getName());
+ }
+
+ /**
+ * {@inheritDoc}
+ *
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/UpdatePropertiesRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/UpdatePropertiesRequest.java 2009-05-15 19:28:48 UTC (rev 910)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/UpdatePropertiesRequest.java 2009-05-18 15:36:58 UTC (rev 911)
@@ -26,6 +26,7 @@
import java.util.Collections;
import java.util.Map;
import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.common.util.HashCode;
import org.jboss.dna.graph.GraphI18n;
import org.jboss.dna.graph.Location;
import org.jboss.dna.graph.property.Name;
@@ -159,6 +160,27 @@
/**
* {@inheritDoc}
*
+ * @see org.jboss.dna.graph.request.Request#cancel()
+ */
+ @Override
+ public void cancel() {
+ super.cancel();
+ this.actualLocation = null;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ return HashCode.compute(on, workspaceName);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/VerifyNodeExistsRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/VerifyNodeExistsRequest.java 2009-05-15 19:28:48 UTC (rev 910)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/VerifyNodeExistsRequest.java 2009-05-18 15:36:58 UTC (rev 911)
@@ -24,6 +24,7 @@
package org.jboss.dna.graph.request;
import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.common.util.HashCode;
import org.jboss.dna.graph.GraphI18n;
import org.jboss.dna.graph.Location;
@@ -126,6 +127,27 @@
/**
* {@inheritDoc}
*
+ * @see org.jboss.dna.graph.request.Request#cancel()
+ */
+ @Override
+ public void cancel() {
+ super.cancel();
+ this.actualLocation = null;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ return HashCode.compute(at, workspaceName);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/VerifyWorkspaceRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/VerifyWorkspaceRequest.java 2009-05-15 19:28:48 UTC (rev 910)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/VerifyWorkspaceRequest.java 2009-05-18 15:36:58 UTC (rev 911)
@@ -106,6 +106,18 @@
/**
* {@inheritDoc}
*
+ * @see org.jboss.dna.graph.request.Request#cancel()
+ */
+ @Override
+ public void cancel() {
+ super.cancel();
+ this.actualLocationOfRoot = null;
+ this.actualWorkspaceName = null;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
* @see java.lang.Object#hashCode()
*/
@Override
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/processor/RequestProcessor.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/processor/RequestProcessor.java 2009-05-15 19:28:48 UTC (rev 910)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/processor/RequestProcessor.java 2009-05-18 15:36:58 UTC (rev 911)
@@ -111,7 +111,7 @@
*
* @return the repository source name; never null or empty
*/
- public String getSourceName() {
+ public final String getSourceName() {
return sourceName;
}
@@ -120,7 +120,7 @@
*
* @return the execution context; never null
*/
- public ExecutionContext getExecutionContext() {
+ public final ExecutionContext getExecutionContext() {
return this.context;
}
@@ -129,17 +129,27 @@
*
* @return the current time in UTC; never null
*/
- protected DateTime getNowInUtc() {
+ protected final DateTime getNowInUtc() {
return this.nowInUtc;
}
/**
+ * @return defaultCachePolicy
+ */
+ protected final CachePolicy getDefaultCachePolicy() {
+ return defaultCachePolicy;
+ }
+
+ /**
* Set the supplied request to have the default cache policy and the {@link #getNowInUtc() current time in UTC}.
*
* @param request the cacheable request
*/
protected void setCacheableInfo( CacheableRequest request ) {
- request.setCachePolicy(defaultCachePolicy);
+ // Set it only if the request has no cache policy already ...
+ if (request.getCachePolicy() == null && defaultCachePolicy != null) {
+ request.setCachePolicy(defaultCachePolicy);
+ }
request.setTimeLoaded(nowInUtc);
}
@@ -151,7 +161,18 @@
*/
protected void setCacheableInfo( CacheableRequest request,
CachePolicy cachePolicy ) {
- request.setCachePolicy(cachePolicy);
+ if (cachePolicy == null) cachePolicy = defaultCachePolicy;
+ if (cachePolicy != null) {
+ if (request.getCachePolicy() != null) {
+ // Set the supplied only if less than the current ...
+ if (request.getCachePolicy().getTimeToLive() > cachePolicy.getTimeToLive()) {
+ request.setCachePolicy(cachePolicy);
+ }
+ } else {
+ // There is no current policy, so set the supplied policy ...
+ request.setCachePolicy(cachePolicy);
+ }
+ }
request.setTimeLoaded(nowInUtc);
}
@@ -175,6 +196,8 @@
process((CreateNodeRequest)request);
} else if (request instanceof DeleteBranchRequest) {
process((DeleteBranchRequest)request);
+ } else if (request instanceof DeleteChildrenRequest) {
+ process((DeleteChildrenRequest)request);
} else if (request instanceof MoveBranchRequest) {
process((MoveBranchRequest)request);
} else if (request instanceof ReadAllChildrenRequest) {
@@ -229,6 +252,7 @@
if (request == null) return;
int numberOfErrors = 0;
List<Throwable> errors = null;
+ // Iterate over the requests in this composite, but only iterate once so that
for (Request embedded : request) {
assert embedded != null;
if (embedded.isCancelled()) return;
@@ -252,9 +276,12 @@
errorString.append("\n");
errorString.append("\t" + error.getMessage());
}
- String msg = GraphI18n.multipleErrorsWhileExecutingRequests.text(numberOfErrors,
- request.size(),
- errorString.toString());
+ String msg = null;
+ if (request.size() == CompositeRequest.UNKNOWN_NUMBER_OF_REQUESTS) {
+ msg = GraphI18n.multipleErrorsWhileExecutingManyRequests.text(numberOfErrors, errorString.toString());
+ } else {
+ msg = GraphI18n.multipleErrorsWhileExecutingRequests.text(numberOfErrors, request.size(), errorString.toString());
+ }
request.setError(new RepositorySourceException(getSourceName(), msg));
}
}
Modified: trunk/dna-graph/src/main/resources/org/jboss/dna/graph/GraphI18n.properties
===================================================================
--- trunk/dna-graph/src/main/resources/org/jboss/dna/graph/GraphI18n.properties 2009-05-15 19:28:48 UTC (rev 910)
+++ trunk/dna-graph/src/main/resources/org/jboss/dna/graph/GraphI18n.properties 2009-05-18 15:36:58 UTC (rev 911)
@@ -55,6 +55,7 @@
executedRequest = Executed {0}
closingRequestProcessor = Closing request processor
closedRequestProcessor = Closed request processor
+multipleErrorsWhileExecutingManyRequests = {0} of the many requests resulted in errors: {1}
multipleErrorsWhileExecutingRequests = {0} of the {1} requests resulted in errors: {2}
unsupportedRequestType = Requests of type "{0}" are unsupported; actual request was to {1}
unableToAddMoreRequestsToAlreadyExecutedBatch = Unable to add more requests to a batch of graph requests that has already been executed
@@ -75,9 +76,29 @@
unableToFindRepositorySourceWithName = Unable to find a repository source named "{0}"
# In-memory connector
-inMemoryConnectorName = In-Memory Connector
inMemoryNodeDoesNotExist = Could not find an existing node at {0}
errorSerializingInMemoryCachePolicyInSource = Error serializing a {0} instance owned by the {1} in-memory repository
inMemoryConnectorRequestsMustHavePathOrUuid = In-Memory connector can only process requests with a path and/or UUID
workspaceDoesNotExistInRepository = The workspace "{0}" does not exist in the "{1}" in-memory repository
workspaceAlreadyExistsInRepository = The workspace "{0}" already exists in the "{1}" in-memory repository
+
+# Federation connector
+namePropertyIsRequiredForFederatedRepositorySource = The "{0}" property is required on each federated repository source
+propertyIsRequiredForFederatedRepositorySource = The "{0}" property is required on the "{1}" federated repository source
+federatedRepositorySourceMustBeInitialized = The "{0}" federated repository source must be initialized before it can be used
+errorReadingConfigurationForFederatedRepositorySource = Error with the "{0}" federated repository source's configuration at "{3}" in the "{2}" workspace of "{1}"
+errorAddingProjectionRuleParseMethod = Error while adding a parsing method for a federation projection rule
+requiredNodeDoesNotExistRelativeToNode = The required node "{0}" does not exist relative to "{1}" in "{2}" workspace of the "{3}" repository
+unableToObtainConnectionToFederatedSource = Unable to obtain a connection to source "{0}" being federated by the "{1}" workspace of the "{2}" federated repository
+workspaceDoesNotExistInFederatedRepository = The workspace "{0}" does not exist in the "{1}" federated repository
+locationCannotBeProjectedIntoWorkspaceAndSource = {0} does not exist in the "{1}" workspace of the "{2}" federated repository
+unableToAddRequestToChannelThatIsDone = Unable to add a request to the "{0}" channel since it is done: {1}
+federatedSourceDoesNotSupportCreatingWorkspaces = {0} is a source that does not allow creating workspaces
+federatedSourceDoesNotSupportCloningWorkspaces = {0} is a source that does not allow cloning workspaces
+federatedSourceDoesNotSupportDestroyingWorkspaces = {0} is a source that does not allow destroying workspaces
+unableToProjectSourceInformationIntoWorkspace = Unable to project source information at {0} in the "{1} federated repository source using projection {2}
+unableToCreateNodeUnderPlaceholder = Unable to create node "{0}" under {1} in the "{2}" workspace of the "{3}" federarted repository because the parent is a placeholder
+unableToUpdatePlaceholder = Unable to update node {0} in the "{1}" workspace of the "{2}" federarted repository because the node is a placeholder
+unableToDeletePlaceholder = Unable to delete node {0} in the "{1}" workspace of the "{2}" federarted repository because the node is a placeholder
+copyLimitedToBeWithinSingleSource = Unable to copy {0} in the "{1}" workspace of the "{3}" federarted repository into {2} in the "{3}" workspace: copy is only supported when the original and new locations are within the same source
+moveLimitedToBeWithinSingleSource = Unable to move {0} in the "{1}" workspace of the "{3}" federarted repository into {2} in the "{3}" workspace: move is only supported when the original and new locations are within the same source
Added: trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/MockRepositoryConnection.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/MockRepositoryConnection.java (rev 0)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/MockRepositoryConnection.java 2009-05-18 15:36:58 UTC (rev 911)
@@ -0,0 +1,146 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ *
+ * JBoss DNA 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.graph.connector;
+
+import java.util.LinkedList;
+import java.util.Queue;
+import java.util.concurrent.TimeUnit;
+import javax.transaction.xa.XAResource;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.cache.CachePolicy;
+import org.jboss.dna.graph.request.Request;
+import org.jboss.dna.graph.request.processor.RequestProcessor;
+
+/**
+ * A simple connection to a mock repository, where the connection accepts requests but does very little with them (other than
+ * record that requests were processed in the Queue supplied in the constructor).
+ */
+public class MockRepositoryConnection implements RepositoryConnection {
+
+ private final String sourceName;
+ private final Queue<Request> processed;
+
+ /**
+ * Create a new connection
+ *
+ * @param sourceName the name of the source that this connection represents; may not be null
+ */
+ public MockRepositoryConnection( String sourceName ) {
+ this(sourceName, new LinkedList<Request>());
+ }
+
+ /**
+ * Create a new connection
+ *
+ * @param sourceName the name of the source that this connection represents; may not be null
+ * @param processed a queue into which should be placed all requests that were {@link #execute(ExecutionContext, Request)
+ * executed}; may be null
+ */
+ public MockRepositoryConnection( String sourceName,
+ Queue<Request> processed ) {
+ assert sourceName != null;
+ this.sourceName = sourceName;
+ this.processed = processed != null ? processed : new LinkedList<Request>();
+ }
+
+ /**
+ * Get the list of requests that have been processed.
+ *
+ * @return the queue of processed requests; never null
+ */
+ public Queue<Request> getProcessedRequests() {
+ return processed;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.connector.RepositoryConnection#close()
+ */
+ public void close() {
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.connector.RepositoryConnection#execute(org.jboss.dna.graph.ExecutionContext,
+ * org.jboss.dna.graph.request.Request)
+ */
+ public void execute( ExecutionContext context,
+ Request request ) throws RepositorySourceException {
+ RequestProcessor processor = new MockRepositoryRequestProcessor(sourceName, context, processed);
+ try {
+ processor.process(request);
+ } finally {
+ processor.close();
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.connector.RepositoryConnection#getDefaultCachePolicy()
+ */
+ public CachePolicy getDefaultCachePolicy() {
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.connector.RepositoryConnection#getSourceName()
+ */
+ public String getSourceName() {
+ return sourceName;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.connector.RepositoryConnection#getXAResource()
+ */
+ public XAResource getXAResource() {
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.connector.RepositoryConnection#ping(long, java.util.concurrent.TimeUnit)
+ */
+ public boolean ping( long time,
+ TimeUnit unit ) {
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.connector.RepositoryConnection#setListener(org.jboss.dna.graph.connector.RepositorySourceListener)
+ */
+ public void setListener( RepositorySourceListener listener ) {
+ throw new UnsupportedOperationException();
+ }
+
+}
Property changes on: trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/MockRepositoryConnection.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/MockRepositoryRequestProcessor.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/MockRepositoryRequestProcessor.java (rev 0)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/MockRepositoryRequestProcessor.java 2009-05-18 15:36:58 UTC (rev 911)
@@ -0,0 +1,296 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ *
+ * JBoss DNA 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.graph.connector;
+
+import java.util.Queue;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.request.CloneWorkspaceRequest;
+import org.jboss.dna.graph.request.CopyBranchRequest;
+import org.jboss.dna.graph.request.CreateNodeRequest;
+import org.jboss.dna.graph.request.CreateWorkspaceRequest;
+import org.jboss.dna.graph.request.DeleteBranchRequest;
+import org.jboss.dna.graph.request.DestroyWorkspaceRequest;
+import org.jboss.dna.graph.request.GetWorkspacesRequest;
+import org.jboss.dna.graph.request.MoveBranchRequest;
+import org.jboss.dna.graph.request.ReadAllChildrenRequest;
+import org.jboss.dna.graph.request.ReadAllPropertiesRequest;
+import org.jboss.dna.graph.request.ReadBlockOfChildrenRequest;
+import org.jboss.dna.graph.request.ReadBranchRequest;
+import org.jboss.dna.graph.request.ReadNextBlockOfChildrenRequest;
+import org.jboss.dna.graph.request.ReadNodeRequest;
+import org.jboss.dna.graph.request.ReadPropertyRequest;
+import org.jboss.dna.graph.request.RemovePropertyRequest;
+import org.jboss.dna.graph.request.RenameNodeRequest;
+import org.jboss.dna.graph.request.Request;
+import org.jboss.dna.graph.request.SetPropertyRequest;
+import org.jboss.dna.graph.request.UpdatePropertiesRequest;
+import org.jboss.dna.graph.request.VerifyNodeExistsRequest;
+import org.jboss.dna.graph.request.VerifyWorkspaceRequest;
+import org.jboss.dna.graph.request.processor.RequestProcessor;
+
+/**
+ *
+ */
+public class MockRepositoryRequestProcessor extends RequestProcessor {
+
+ private Queue<Request> processed;
+
+ /**
+ * @param sourceName
+ * @param context
+ * @param processed
+ */
+ public MockRepositoryRequestProcessor( String sourceName,
+ ExecutionContext context,
+ Queue<Request> processed ) {
+ super(sourceName, context);
+ assert processed != null;
+ this.processed = processed;
+ }
+
+ protected void record( Request request ) {
+ processed.add(request);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.VerifyWorkspaceRequest)
+ */
+ @Override
+ public void process( VerifyWorkspaceRequest request ) {
+ record(request);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.GetWorkspacesRequest)
+ */
+ @Override
+ public void process( GetWorkspacesRequest request ) {
+ record(request);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.CreateWorkspaceRequest)
+ */
+ @Override
+ public void process( CreateWorkspaceRequest request ) {
+ record(request);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.CloneWorkspaceRequest)
+ */
+ @Override
+ public void process( CloneWorkspaceRequest request ) {
+ record(request);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.DestroyWorkspaceRequest)
+ */
+ @Override
+ public void process( DestroyWorkspaceRequest request ) {
+ record(request);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.CopyBranchRequest)
+ */
+ @Override
+ public void process( CopyBranchRequest request ) {
+ record(request);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.CreateNodeRequest)
+ */
+ @Override
+ public void process( CreateNodeRequest request ) {
+ record(request);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.DeleteBranchRequest)
+ */
+ @Override
+ public void process( DeleteBranchRequest request ) {
+ record(request);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.MoveBranchRequest)
+ */
+ @Override
+ public void process( MoveBranchRequest request ) {
+ record(request);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.ReadAllChildrenRequest)
+ */
+ @Override
+ public void process( ReadAllChildrenRequest request ) {
+ record(request);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.ReadAllPropertiesRequest)
+ */
+ @Override
+ public void process( ReadAllPropertiesRequest request ) {
+ record(request);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.UpdatePropertiesRequest)
+ */
+ @Override
+ public void process( UpdatePropertiesRequest request ) {
+ record(request);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.ReadBlockOfChildrenRequest)
+ */
+ @Override
+ public void process( ReadBlockOfChildrenRequest request ) {
+ record(request);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.ReadBranchRequest)
+ */
+ @Override
+ public void process( ReadBranchRequest request ) {
+ record(request);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.ReadNextBlockOfChildrenRequest)
+ */
+ @Override
+ public void process( ReadNextBlockOfChildrenRequest request ) {
+ record(request);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.ReadNodeRequest)
+ */
+ @Override
+ public void process( ReadNodeRequest request ) {
+ record(request);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.ReadPropertyRequest)
+ */
+ @Override
+ public void process( ReadPropertyRequest request ) {
+ record(request);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.RemovePropertyRequest)
+ */
+ @Override
+ public void process( RemovePropertyRequest request ) {
+ record(request);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.RenameNodeRequest)
+ */
+ @Override
+ public void process( RenameNodeRequest request ) {
+ record(request);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.SetPropertyRequest)
+ */
+ @Override
+ public void process( SetPropertyRequest request ) {
+ record(request);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.VerifyNodeExistsRequest)
+ */
+ @Override
+ public void process( VerifyNodeExistsRequest request ) {
+ record(request);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.request.processor.RequestProcessor#processUnknownRequest(org.jboss.dna.graph.request.Request)
+ */
+ @Override
+ protected void processUnknownRequest( Request request ) {
+ record(request);
+ }
+
+}
Property changes on: trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/MockRepositoryRequestProcessor.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/AbstractFederatedRepositorySourceIntegrationTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/AbstractFederatedRepositorySourceIntegrationTest.java (rev 0)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/AbstractFederatedRepositorySourceIntegrationTest.java 2009-05-18 15:36:58 UTC (rev 911)
@@ -0,0 +1,319 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ *
+ * JBoss DNA 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.graph.connector.federation;
+
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.stub;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.jboss.dna.common.statistic.Stopwatch;
+import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.Graph;
+import org.jboss.dna.graph.Location;
+import org.jboss.dna.graph.Node;
+import org.jboss.dna.graph.Subgraph;
+import org.jboss.dna.graph.connector.RepositoryConnection;
+import org.jboss.dna.graph.connector.RepositoryConnectionFactory;
+import org.jboss.dna.graph.connector.RepositoryContext;
+import org.jboss.dna.graph.connector.inmemory.InMemoryRepositorySource;
+import org.jboss.dna.graph.connector.test.AbstractConnectorTest;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.Path;
+import org.jboss.dna.graph.property.PathNotFoundException;
+import org.jboss.dna.graph.property.Property;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.mockito.MockitoAnnotations;
+import org.mockito.MockitoAnnotations.Mock;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+/**
+ *
+ */
+public abstract class AbstractFederatedRepositorySourceIntegrationTest {
+
+ private static final Stopwatch FEDERATED_TIMER = new Stopwatch();
+ private static final Stopwatch SOURCE_TIMER = new Stopwatch();
+
+ protected FederatedRepositorySource source;
+ private String sourceName;
+ private String repositoryName;
+ private String configurationSourceName;
+ private String configurationWorkspaceName;
+ private InMemoryRepositorySource configRepositorySource;
+ private RepositoryConnection configRepositoryConnection;
+ protected ExecutionContext context;
+ private Map<String, InMemoryRepositorySource> sources;
+ protected Graph federated;
+ @Mock
+ private RepositoryConnectionFactory connectionFactory;
+ @Mock
+ private RepositoryContext repositoryContext;
+
+ /**
+ * @throws java.lang.Exception
+ */
+ @Before
+ public void beforeEach() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ context = new ExecutionContext();
+ configurationSourceName = "configuration";
+ configurationWorkspaceName = "configSpace";
+ repositoryName = "Test Repository";
+
+ // Set up the source ...
+ source = new FederatedRepositorySource();
+ source.setName(repositoryName);
+ sourceName = "federated source";
+ source.setName(sourceName);
+ source.setConfigurationSourceName(configurationSourceName);
+ source.setConfigurationWorkspaceName(configurationWorkspaceName);
+ source.setConfigurationPath("/a/b/Test Repository");
+ source.initialize(repositoryContext);
+
+ // Set up the configuration repository ...
+ configRepositorySource = new InMemoryRepositorySource();
+ configRepositorySource.setName("Configuration Repository");
+ configRepositorySource.setDefaultWorkspaceName(configurationWorkspaceName);
+ Graph config = Graph.create(configRepositorySource, context);
+ config.create("/a");
+ config.create("/a/b");
+ config.create("/a/b/Test Repository");
+ config.create("/a/b/Test Repository/dna:workspaces");
+
+ // Set up the map of sources ...
+ sources = new HashMap<String, InMemoryRepositorySource>();
+
+ // Stub the RepositoryContext and RepositoryConnectionFactory instances ...
+ configRepositoryConnection = configRepositorySource.getConnection();
+ stub(repositoryContext.getExecutionContext()).toReturn(context);
+ stub(repositoryContext.getRepositoryConnectionFactory()).toReturn(connectionFactory);
+ stub(connectionFactory.createConnection(configurationSourceName)).toReturn(configRepositoryConnection);
+ stub(connectionFactory.createConnection(sourceName)).toAnswer(new Answer<RepositoryConnection>() {
+ public RepositoryConnection answer( InvocationOnMock invocation ) throws Throwable {
+ return source.getConnection();
+ }
+ });
+
+ // Create the graph to the federated repository ...
+ federated = Graph.create(sourceName, connectionFactory, context);
+ }
+
+ @AfterClass
+ public static void afterAll() {
+ System.out.println("Results for federated reads: " + FEDERATED_TIMER.getSimpleStatistics());
+ System.out.println("Results for source reads: " + FEDERATED_TIMER.getSimpleStatistics());
+ }
+
+ /**
+ * Add to the supplied workspace in the federated repository a projection from the workspace in the supplied source.
+ *
+ * @param federatedWorkspace
+ * @param projectionName
+ * @param sourceName
+ * @param workspaceName
+ * @param projectionRules
+ */
+ protected void addProjection( String federatedWorkspace,
+ String projectionName,
+ String sourceName,
+ String workspaceName,
+ String... projectionRules ) {
+ CheckArg.isNotNull(federatedWorkspace, "federatedWorkspace");
+ CheckArg.isNotNull(projectionName, "projectionName");
+ CheckArg.isNotNull(sourceName, "sourceName");
+ CheckArg.isNotNull(workspaceName, "workspaceName");
+ CheckArg.isNotEmpty(projectionRules, "projectionRules");
+ assertThat(source.getConfigurationPath().endsWith("/"), is(true));
+ String wsPath = source.getConfigurationPath() + "dna:workspaces/" + federatedWorkspace;
+ String projectionPath = wsPath + "/dna:projections/" + projectionName;
+ Graph config = Graph.create(configRepositorySource, context);
+ config.useWorkspace(configurationWorkspaceName);
+ config.createIfMissing(wsPath);
+ config.createIfMissing(wsPath + "/dna:projections");
+ config.createAt(projectionPath).with(FederatedLexicon.PROJECTION_RULES, (Object[])projectionRules).with(FederatedLexicon.SOURCE_NAME,
+ sourceName).with(FederatedLexicon.WORKSPACE_NAME,
+ workspaceName).and();
+ // Make sure the source and workspace exist ...
+ graphFor(sourceName, workspaceName);
+ }
+
+ /**
+ * Obtain a graph to the named source and workspace. If the source does not exist, it is created. Also, if the supplied
+ * workspace does not exist, it is also created.
+ *
+ * @param sourceName the name of the source; may not be null
+ * @param workspaceName the name of the workspace; may not be null
+ * @return the resulting graph; never null
+ */
+ protected Graph graphFor( String sourceName,
+ String workspaceName ) {
+ CheckArg.isNotNull(sourceName, "sourceName");
+ CheckArg.isNotNull(workspaceName, "workspaceName");
+ InMemoryRepositorySource source = sources.get(sourceName);
+ if (source == null) {
+ // Add a new source with this name ...
+ source = new InMemoryRepositorySource();
+ source.setName(sourceName);
+ sources.put(sourceName, source);
+ final InMemoryRepositorySource newSource = source;
+ // Stub the repository connection factory to return a new connection for this source ...
+ stub(connectionFactory.createConnection(sourceName)).toAnswer(new Answer<RepositoryConnection>() {
+ public RepositoryConnection answer( InvocationOnMock invocation ) throws Throwable {
+ return newSource.getConnection();
+ }
+ });
+ }
+ // Make sure there's a workspace for it ...
+ Graph sourceGraph = Graph.create(sourceName, connectionFactory, context);
+ if (sourceGraph.getWorkspaces().contains(workspaceName)) {
+ sourceGraph.useWorkspace(workspaceName);
+ } else {
+ sourceGraph.createWorkspace().named(workspaceName);
+ }
+ return sourceGraph;
+ }
+
+ /**
+ * Assert that the node does not exist in the federated repository given by the supplied path nor in the underlying source
+ * given by the path, source name, and workspace name.
+ *
+ * @param pathInFederated
+ * @param pathInSource
+ * @param sourceName
+ * @param workspaceName
+ */
+ protected void assertNoNode( String pathInFederated,
+ String pathInSource,
+ String sourceName,
+ String workspaceName ) {
+ try {
+ FEDERATED_TIMER.start();
+ federated.getNodeAt(pathInFederated);
+ FEDERATED_TIMER.stop();
+ fail("Did not expect to find federated node \"" + pathInFederated + "\"");
+ } catch (PathNotFoundException e) {
+ // expected
+ }
+ try {
+ SOURCE_TIMER.start();
+ graphFor(sourceName, workspaceName).getNodeAt(pathInSource);
+ SOURCE_TIMER.stop();
+ fail("Did not expect to find source node \"" + pathInSource + "\" in workspace \"" + workspaceName
+ + "\" of source \"" + sourceName + "\"");
+ } catch (PathNotFoundException e) {
+ // expected
+ }
+ }
+
+ /**
+ * Assert that the node in the federated repository given by the supplied path represents the same node in the underlying
+ * source given by the path, source name, and workspace name.
+ *
+ * @param pathInFederated
+ * @param pathInSource
+ * @param sourceName
+ * @param workspaceName
+ * @param extraChildren
+ */
+ protected void assertSameNode( String pathInFederated,
+ String pathInSource,
+ String sourceName,
+ String workspaceName,
+ String... extraChildren ) {
+ FEDERATED_TIMER.start();
+ Node fedNode = federated.getNodeAt(pathInFederated);
+ FEDERATED_TIMER.stop();
+ SOURCE_TIMER.start();
+ Node sourceNode = graphFor(sourceName, workspaceName).getNodeAt(pathInSource);
+ SOURCE_TIMER.stop();
+ // The name should match ...
+ Path fedPath = fedNode.getLocation().getPath();
+ Path sourcePath = sourceNode.getLocation().getPath();
+ if (!fedPath.isRoot() && !sourcePath.isRoot()) {
+ assertThat(fedNode.getLocation().getPath().getLastSegment().getName(),
+ is(sourceNode.getLocation().getPath().getLastSegment().getName()));
+ }
+ // The children should match ...
+ List<Path.Segment> fedChildren = new ArrayList<Path.Segment>();
+ List<Path.Segment> sourceChildren = new ArrayList<Path.Segment>();
+ for (Location child : fedNode.getChildren()) {
+ fedChildren.add(child.getPath().getLastSegment());
+ }
+ for (Location child : sourceNode.getChildren()) {
+ sourceChildren.add(child.getPath().getLastSegment());
+ }
+ // Add any extra children to the 'sourceChildren' ...
+ for (String extraChild : extraChildren) {
+ sourceChildren.add(context.getValueFactories().getPathFactory().createSegment(extraChild));
+ }
+ assertThat(fedChildren, is(sourceChildren));
+ // The properties should match ...
+ Map<Name, Property> fedProps = fedNode.getPropertiesByName();
+ Map<Name, Property> sourceProps = sourceNode.getPropertiesByName();
+ assertThat(fedProps, is(sourceProps));
+
+ // Now, try to get the children only ...
+ FEDERATED_TIMER.start();
+ List<Location> children = federated.getChildren().of(pathInFederated);
+ FEDERATED_TIMER.stop();
+ fedChildren.clear();
+ for (Location child : children) {
+ fedChildren.add(child.getPath().getLastSegment());
+ }
+ assertThat(fedChildren, is(sourceChildren));
+
+ // And try to get the properties only ...
+ FEDERATED_TIMER.start();
+ fedProps = federated.getPropertiesByName().on(pathInFederated);
+ FEDERATED_TIMER.stop();
+ assertThat(fedProps, is(sourceProps));
+
+ // And try to get the properties one by one ...
+ for (Property sourceProp : sourceProps.values()) {
+ FEDERATED_TIMER.start();
+ Property fedProp = federated.getProperty(sourceProp.getName()).on(pathInFederated);
+ FEDERATED_TIMER.stop();
+ assertThat(fedProp, is(sourceProp));
+ }
+
+ // Try reading a subgraph of depth 2 ...
+ FEDERATED_TIMER.start();
+ Subgraph fedSubgraph = federated.getSubgraphOfDepth(2).at(pathInFederated);
+ FEDERATED_TIMER.stop();
+ SOURCE_TIMER.start();
+ Subgraph sourceSubgraph = graphFor(sourceName, workspaceName).getSubgraphOfDepth(2).at(pathInSource);
+ SOURCE_TIMER.stop();
+ if (extraChildren.length == 0) {
+ // Can only compare the graphs when there are no extra children ...
+ AbstractConnectorTest.assertEquivalentSubgraphs(fedSubgraph, sourceSubgraph, true, false);
+ }
+ }
+}
Property changes on: trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/AbstractFederatedRepositorySourceIntegrationTest.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/AbstractProjectorTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/AbstractProjectorTest.java (rev 0)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/AbstractProjectorTest.java 2009-05-18 15:36:58 UTC (rev 911)
@@ -0,0 +1,116 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ *
+ * JBoss DNA 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.graph.connector.federation;
+
+import static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsNull.notNullValue;
+import static org.hamcrest.core.IsNull.nullValue;
+import static org.junit.Assert.assertThat;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.Location;
+import org.jboss.dna.graph.connector.federation.Projection.Rule;
+import org.jboss.dna.graph.property.Path;
+import org.junit.Before;
+
+/**
+ * @param <ProjectorType>
+ */
+public abstract class AbstractProjectorTest<ProjectorType extends Projector> {
+
+ protected ExecutionContext context;
+ protected List<Projection> projections;
+ protected ProjectorType projector;
+ protected Location locationA;
+ protected Location locationB;
+ protected Location locationAB;
+ protected Location locationABCD;
+
+ @Before
+ public void beforeEach() {
+ this.context = new ExecutionContext();
+ this.projections = new ArrayList<Projection>();
+ this.locationA = Location.create(path("/a"));
+ this.locationB = Location.create(path("/b"));
+ this.locationAB = Location.create(path("/a/b"));
+ this.locationABCD = Location.create(path("/a/b/c/d"));
+ }
+
+ protected Rule[] rule( String... rule ) {
+ Rule[] rules = new Rule[rule.length];
+ for (int i = 0; i != rule.length; ++i) {
+ rules[i] = Projection.fromString(rule[i], context);
+ }
+ return rules;
+ }
+
+ protected Path path( String path ) {
+ return context.getValueFactories().getPathFactory().create(path);
+ }
+
+ protected Path path( Path parent,
+ String relativePath ) {
+ return context.getValueFactories().getPathFactory().create(parent, relativePath);
+ }
+
+ protected void addProjection( String sourceName,
+ String workspaceName,
+ String... rules ) {
+ projections.add(new Projection(sourceName, workspaceName, false, rule(rules)));
+ }
+
+ protected Path.Segment segment( String segment ) {
+ return context.getValueFactories().getPathFactory().createSegment(segment);
+ }
+
+ protected Location location( String path ) {
+ return Location.create(path(path));
+ }
+
+ protected Location location( UUID uuid ) {
+ return Location.create(uuid);
+ }
+
+ protected void assertNoProjectedNodeAt( String path ) {
+ ProjectedNode node = projector.project(context, location(path), false);
+ assertThat(node, is(nullValue()));
+ }
+
+ protected void assertPlacholderHasChildren( String parent,
+ String... childSegments ) {
+ Path parentPath = path(parent);
+ ProjectedNode node = projector.project(context, Location.create(parentPath), false);
+ assertThat(node, is(notNullValue()));
+ assertThat(node.isPlaceholder(), is(true));
+ PlaceholderNode placeholder = node.asPlaceholder();
+ List<Location> locations = new ArrayList<Location>();
+ for (String childSegment : childSegments) {
+ Path childPath = path(parentPath, childSegment);
+ locations.add(Location.create(childPath));
+ }
+ assertThat(placeholder.children(), is(locations));
+ }
+}
Property changes on: trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/AbstractProjectorTest.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/BranchedMirrorProjectorTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/BranchedMirrorProjectorTest.java (rev 0)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/BranchedMirrorProjectorTest.java 2009-05-18 15:36:58 UTC (rev 911)
@@ -0,0 +1,147 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ *
+ * JBoss DNA 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.graph.connector.federation;
+
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.assertThat;
+import org.jboss.dna.graph.Location;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ *
+ */
+public class BranchedMirrorProjectorTest extends AbstractProjectorTest<BranchedMirrorProjector> {
+
+ private String mirrorSourceName;
+ private String mirrorWorkspaceName;
+ private String branchSourceName;
+ private String branchWorkspaceName;
+
+ @Before
+ @Override
+ public void beforeEach() {
+ super.beforeEach();
+ this.mirrorSourceName = "source1";
+ this.mirrorWorkspaceName = "workspace1";
+ this.branchSourceName = "source2";
+ this.branchWorkspaceName = "workspace2";
+ addProjection(mirrorSourceName, mirrorWorkspaceName, "/ => /");
+ addProjection(branchSourceName, branchWorkspaceName, "/system => /system");
+ this.projector = BranchedMirrorProjector.with(context, projections);
+ }
+
+ protected void assertProjectedIntoMirror( String federatedPath ) {
+ Location location = location(federatedPath);
+ ProjectedNode node = projector.project(context, location, false);
+ assertThat(node.isProxy(), is(true));
+ ProxyNode proxy = node.asProxy();
+ assertThat(proxy.location(), is(location));
+ assertThat(proxy.source(), is(mirrorSourceName));
+ assertThat(proxy.workspaceName(), is(mirrorWorkspaceName));
+ assertThat(proxy.hasNext(), is(false));
+ }
+
+ protected void assertProjectedIntoBranch( String federatedPath ) {
+ assertProjectedIntoBranch(federatedPath, federatedPath);
+ }
+
+ protected void assertProjectedIntoBranch( String federatedPath,
+ String expectedPathInSource ) {
+ Location location = location(federatedPath);
+ ProjectedNode node = projector.project(context, location, false);
+ assertThat(node.isProxy(), is(true));
+ ProxyNode proxy = node.asProxy();
+ assertThat(proxy.source(), is(branchSourceName));
+ assertThat(proxy.workspaceName(), is(branchWorkspaceName));
+ assertThat(proxy.location().getPath(), is(path(expectedPathInSource)));
+ assertThat(proxy.hasNext(), is(false));
+ }
+
+ @Test
+ public void shouldAlwaysReturnProxyNodeForLocationInMirrorSource() {
+ assertProjectedIntoMirror("/a");
+ assertProjectedIntoMirror("/a/b/c/d");
+ assertProjectedIntoMirror("/system[2]/b/c/d"); // technically not in the branch!
+ }
+
+ @Test
+ public void shouldAlwaysReturnProxyNodeForLocationInBranchOnlyOneLevelDeepWithOneForOneMapping() {
+ assertProjectedIntoBranch("/system");
+ assertProjectedIntoBranch("/system[1]");
+ assertProjectedIntoBranch("/system/d");
+ assertProjectedIntoBranch("/system[1]/d");
+ }
+
+ @Test
+ public void shouldReturnForRootLocationAProxyForMirrorAndPlaceholderForBranch() {
+ Location location = location("/");
+ ProjectedNode node = projector.project(context, location, false);
+ assertThat(node.isProxy(), is(true));
+ ProxyNode proxy = node.asProxy();
+ assertThat(proxy.source(), is(mirrorSourceName));
+ assertThat(proxy.workspaceName(), is(mirrorWorkspaceName));
+ assertThat(proxy.location().getPath(), is(path("/")));
+ assertThat(proxy.hasNext(), is(true));
+
+ ProjectedNode next = node.next();
+ assertThat(next.isPlaceholder(), is(true));
+ PlaceholderNode placeholder = next.asPlaceholder();
+ assertThat(placeholder.isPlaceholder(), is(true));
+ assertThat(placeholder.location().getPath(), is(path("/")));
+ assertThat(placeholder.children().size(), is(1));
+ assertThat(placeholder.children().get(0).getPath(), is(path("/system")));
+ assertThat(placeholder.hasNext(), is(false));
+ }
+
+ @Test
+ public void shouldAlwaysReturnProxyNodeForLocationInBranchMultipleLevelsDeepWithOneForOneMapping() {
+ projections.clear();
+ addProjection(mirrorSourceName, mirrorWorkspaceName, "/ => /");
+ addProjection(branchSourceName, branchWorkspaceName, "/a/b/c => /a/b/c");
+ this.projector = BranchedMirrorProjector.with(context, projections);
+ assertProjectedIntoBranch("/a/b/c");
+ assertProjectedIntoBranch("/a/b/c/d");
+ }
+
+ @Test
+ public void shouldAlwaysReturnProxyNodeForLocationInBranchMultipleLevelsDeepWithDissimilarMapping() {
+ projections.clear();
+ addProjection(mirrorSourceName, mirrorWorkspaceName, "/ => /");
+ addProjection(branchSourceName, branchWorkspaceName, "/a/b/c => /d/e");
+ this.projector = BranchedMirrorProjector.with(context, projections);
+ assertProjectedIntoBranch("/a/b/c", "/d/e");
+ assertProjectedIntoBranch("/a/b/c/f", "/d/e/f");
+ }
+
+ @Test
+ public void shouldAlwaysReturnProxyNodeForLocationInBranchMultipleLevelsDeepWithRootMapping() {
+ projections.clear();
+ addProjection(mirrorSourceName, mirrorWorkspaceName, "/ => /");
+ addProjection(branchSourceName, branchWorkspaceName, "/a/b/c => /");
+ this.projector = BranchedMirrorProjector.with(context, projections);
+ assertProjectedIntoBranch("/a/b/c", "/");
+ assertProjectedIntoBranch("/a/b/c/f", "/f");
+ }
+}
Property changes on: trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/BranchedMirrorProjectorTest.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/FederatedRepositoryConnectionTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/FederatedRepositoryConnectionTest.java (rev 0)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/FederatedRepositoryConnectionTest.java 2009-05-18 15:36:58 UTC (rev 911)
@@ -0,0 +1,108 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ *
+ * JBoss DNA 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.graph.connector.federation;
+
+import static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsNull.nullValue;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.stub;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import org.jboss.dna.graph.cache.CachePolicy;
+import org.jboss.dna.graph.request.CompositeRequest;
+import org.jboss.dna.graph.request.Request;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.MockitoAnnotations;
+import org.mockito.MockitoAnnotations.Mock;
+
+/**
+ * @author Randall Hauch
+ */
+public class FederatedRepositoryConnectionTest {
+
+ private FederatedRepositoryConnection connection;
+ @Mock
+ private FederatedRepository repository;
+
+ @Before
+ public void beforeEach() {
+ MockitoAnnotations.initMocks(this);
+ connection = new FederatedRepositoryConnection(repository);
+ }
+
+ @Test
+ public void shouldHaveRepository() {
+ assertThat(connection.getRepository(), is(repository));
+ }
+
+ @Test
+ public void shouldHaveNoXaResource() {
+ assertThat(connection.getXAResource(), is(nullValue()));
+ }
+
+ @Test
+ public void shouldGetDefaultCachePolicyFromRepository() {
+ CachePolicy cachePolicy = mock(CachePolicy.class);
+ stub(repository.getDefaultCachePolicy()).toReturn(cachePolicy);
+ assertThat(connection.getDefaultCachePolicy(), is(cachePolicy));
+ verify(repository, times(1)).getDefaultCachePolicy();
+ }
+
+ @Test
+ public void shouldGetSourceNameFromRepository() {
+ String name = "Something";
+ stub(repository.getSourceName()).toReturn(name);
+ assertThat(connection.getSourceName(), is(name));
+ verify(repository, times(1)).getSourceName();
+ }
+
+ @Test
+ public void shouldProcessNonCompositeRequestSynchronously() {
+ Request request = mock(Request.class);
+ assertThat(connection.shouldProcessSynchronously(request), is(true));
+ }
+
+ @Test
+ public void shouldProcessCompositeRequestWithOneRequestSynchronously() {
+ CompositeRequest request = mock(CompositeRequest.class);
+ stub(request.size()).toReturn(1);
+ assertThat(connection.shouldProcessSynchronously(request), is(true));
+ }
+
+ @Test
+ public void shouldProcessCompositeRequestWithMultipleRequestsAsynchronously() {
+ CompositeRequest request = mock(CompositeRequest.class);
+ stub(request.size()).toReturn(2);
+ assertThat(connection.shouldProcessSynchronously(request), is(false));
+ }
+
+ @Test
+ public void shouldAllowCloseToBeCalledRepeatedly() {
+ for (int i = 0; i != 10; ++i) {
+ connection.close();
+ }
+ }
+}
Property changes on: trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/FederatedRepositoryConnectionTest.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/FederatedRepositorySourceTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/FederatedRepositorySourceTest.java (rev 0)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/FederatedRepositorySourceTest.java 2009-05-18 15:36:58 UTC (rev 911)
@@ -0,0 +1,242 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ *
+ * JBoss DNA 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.graph.connector.federation;
+
+import static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsNull.notNullValue;
+import static org.hamcrest.core.IsNull.nullValue;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Matchers.argThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.stub;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Map;
+import javax.naming.Context;
+import javax.naming.Name;
+import javax.naming.RefAddr;
+import javax.naming.Reference;
+import javax.naming.spi.ObjectFactory;
+import javax.security.auth.callback.CallbackHandler;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.Graph;
+import org.jboss.dna.graph.connector.RepositoryConnection;
+import org.jboss.dna.graph.connector.RepositoryConnectionFactory;
+import org.jboss.dna.graph.connector.RepositoryContext;
+import org.jboss.dna.graph.connector.inmemory.InMemoryRepositorySource;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentMatcher;
+import org.mockito.MockitoAnnotations;
+import org.mockito.MockitoAnnotations.Mock;
+
+/**
+ *
+ */
+public class FederatedRepositorySourceTest {
+
+ private FederatedRepositorySource source;
+ private String sourceName;
+ private String repositoryName;
+ private String configurationSourceName;
+ private InMemoryRepositorySource configRepositorySource;
+ private RepositoryConnection configRepositoryConnection;
+ private ExecutionContext context;
+ @Mock
+ private RepositoryConnection connection;
+ @Mock
+ private RepositoryConnectionFactory connectionFactory;
+ @Mock
+ private RepositoryContext repositoryContext;
+
+ /**
+ * @throws java.lang.Exception
+ */
+ @Before
+ public void beforeEach() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ context = new ExecutionContext();
+ configurationSourceName = "configuration";
+ repositoryName = "Test Repository";
+ source = new FederatedRepositorySource();
+ source.setName(repositoryName);
+ sourceName = "federated source";
+ source.setName(sourceName);
+ source.setConfigurationSourceName(configurationSourceName);
+ source.setConfigurationWorkspaceName("configSpace");
+ source.setConfigurationPath("/a/b/Test Repository");
+ source.initialize(repositoryContext);
+ configRepositorySource = new InMemoryRepositorySource();
+ configRepositorySource.setName("Configuration Repository");
+
+ Graph configRepository = Graph.create(configRepositorySource, context);
+ configRepository.createWorkspace().named("configSpace");
+ Graph.Batch batch = configRepository.batch();
+ batch.create("/a").and();
+ batch.create("/a/b").and();
+ batch.create("/a/b/Test Repository").with(FederatedLexicon.DEFAULT_WORKSPACE_NAME, "fedSpace").and();
+ batch.create("/a/b/Test Repository/dna:workspaces").and();
+ batch.create("/a/b/Test Repository/dna:workspaces/fedSpace").and();
+ batch.create("/a/b/Test Repository/dna:workspaces/fedSpace/dna:cache").with(FederatedLexicon.PROJECTION_RULES, "/ => /").with(FederatedLexicon.SOURCE_NAME,
+ "cache source").with(FederatedLexicon.WORKSPACE_NAME,
+ "cacheSpace").with(FederatedLexicon.TIME_TO_EXPIRE,
+ 100000).and();
+ batch.create("/a/b/Test Repository/dna:workspaces/fedSpace/dna:projections").and();
+ batch.create("/a/b/Test Repository/dna:workspaces/fedSpace/dna:projections/projection1").with(FederatedLexicon.PROJECTION_RULES,
+ "/ => /s1").with(FederatedLexicon.SOURCE_NAME,
+ "source 1").with(FederatedLexicon.WORKSPACE_NAME,
+ "s1 workspace").and();
+ batch.create("/a/b/Test Repository/dna:workspaces/fedSpace/dna:projections/projection2").with(FederatedLexicon.PROJECTION_RULES,
+ "/ => /s2").with(FederatedLexicon.SOURCE_NAME,
+ "source 2").with(FederatedLexicon.WORKSPACE_NAME,
+ "s2 worskspace").and();
+ batch.execute();
+
+ configRepositoryConnection = configRepositorySource.getConnection();
+ stub(repositoryContext.getExecutionContext()).toReturn(context);
+ stub(repositoryContext.getRepositoryConnectionFactory()).toReturn(connectionFactory);
+ stub(connectionFactory.createConnection(configurationSourceName)).toReturn(configRepositoryConnection);
+ }
+
+ protected static CallbackHandler anyCallbackHandler() {
+ return argThat(new ArgumentMatcher<CallbackHandler>() {
+ @Override
+ public boolean matches( Object callback ) {
+ return callback != null;
+ }
+ });
+ }
+
+ @Test
+ public void shouldReturnNonNullCapabilities() {
+ assertThat(source.getCapabilities(), is(notNullValue()));
+ }
+
+ @Test
+ public void shouldSupportSameNameSiblings() {
+ assertThat(source.getCapabilities().supportsSameNameSiblings(), is(true));
+ }
+
+ @Test
+ public void shouldSupportUpdates() {
+ assertThat(source.getCapabilities().supportsUpdates(), is(true));
+ }
+
+ @Test
+ public void shouldCreateConnectionsByAuthenticateUsingFederationRepository() throws Exception {
+ connection = source.getConnection();
+ assertThat(connection, is(notNullValue()));
+ }
+
+ @Test
+ public void shouldHaveNullSourceNameUponConstruction() {
+ source = new FederatedRepositorySource();
+ assertThat(source.getName(), is(nullValue()));
+ }
+
+ @Test
+ public void shouldAllowSettingName() {
+ source.setName("Something");
+ assertThat(source.getName(), is("Something"));
+ source.setName("another name");
+ assertThat(source.getName(), is("another name"));
+ }
+
+ @Test
+ public void shouldAllowSettingNameToNull() {
+ source.setName("some name");
+ source.setName(null);
+ assertThat(source.getName(), is(nullValue()));
+ }
+
+ @Test
+ public void shouldHaveDefaultRetryLimit() {
+ assertThat(source.getRetryLimit(), is(FederatedRepositorySource.DEFAULT_RETRY_LIMIT));
+ }
+
+ @Test
+ public void shouldSetRetryLimitToZeroWhenSetWithNonPositiveValue() {
+ source.setRetryLimit(0);
+ assertThat(source.getRetryLimit(), is(0));
+ source.setRetryLimit(-1);
+ assertThat(source.getRetryLimit(), is(0));
+ source.setRetryLimit(-100);
+ assertThat(source.getRetryLimit(), is(0));
+ }
+
+ @Test
+ public void shouldAllowRetryLimitToBeSet() {
+ for (int i = 0; i != 100; ++i) {
+ source.setRetryLimit(i);
+ assertThat(source.getRetryLimit(), is(i));
+ }
+ }
+
+ @Test
+ public void shouldCreateJndiReferenceAndRecreatedObjectFromReference() throws Exception {
+ int retryLimit = 100;
+ source.setRetryLimit(retryLimit);
+ source.setName("Some source");
+ source.setConfigurationSourceName("config source");
+ source.setConfigurationPath("/a/b/c");
+
+ Reference ref = source.getReference();
+ assertThat(ref.getClassName(), is(FederatedRepositorySource.class.getName()));
+ assertThat(ref.getFactoryClassName(), is(FederatedRepositorySource.class.getName()));
+
+ Map<String, Object> refAttributes = new HashMap<String, Object>();
+ Enumeration<RefAddr> enumeration = ref.getAll();
+ while (enumeration.hasMoreElements()) {
+ RefAddr addr = enumeration.nextElement();
+ refAttributes.put(addr.getType(), addr.getContent());
+ }
+
+ assertThat((String)refAttributes.remove(FederatedRepositorySource.SOURCE_NAME), is(source.getName()));
+ assertThat((String)refAttributes.remove(FederatedRepositorySource.RETRY_LIMIT), is(Integer.toString(retryLimit)));
+ assertThat((String)refAttributes.remove(FederatedRepositorySource.CONFIGURATION_WORKSPACE_NAME),
+ is(source.getConfigurationWorkspaceName()));
+ assertThat((String)refAttributes.remove(FederatedRepositorySource.CONFIGURATION_SOURCE_NAME),
+ is(source.getConfigurationSourceName()));
+ assertThat((String)refAttributes.remove(FederatedRepositorySource.CONFIGURATION_PATH), is("/a/b/c/"));
+ assertThat(refAttributes.isEmpty(), is(true));
+
+ // Recreate the object, use a newly constructed source ...
+ ObjectFactory factory = new FederatedRepositorySource();
+ Name name = mock(Name.class);
+ Context context = mock(Context.class);
+ Hashtable<?, ?> env = new Hashtable<Object, Object>();
+ FederatedRepositorySource recoveredSource = (FederatedRepositorySource)factory.getObjectInstance(ref, name, context, env);
+ assertThat(recoveredSource, is(notNullValue()));
+
+ assertThat(recoveredSource.getName(), is(source.getName()));
+ assertThat(recoveredSource.getRetryLimit(), is(source.getRetryLimit()));
+ assertThat(recoveredSource.getConfigurationSourceName(), is(source.getConfigurationSourceName()));
+ assertThat(recoveredSource.getConfigurationWorkspaceName(), is(source.getConfigurationWorkspaceName()));
+ assertThat(recoveredSource.getConfigurationPath(), is(source.getConfigurationPath()));
+
+ assertThat(recoveredSource.equals(source), is(true));
+ assertThat(source.equals(recoveredSource), is(true));
+ }
+}
Property changes on: trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/FederatedRepositorySourceTest.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/FederatedRepositorySourceUsingMirrorAndBranchProjectionsTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/FederatedRepositorySourceUsingMirrorAndBranchProjectionsTest.java (rev 0)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/FederatedRepositorySourceUsingMirrorAndBranchProjectionsTest.java 2009-05-18 15:36:58 UTC (rev 911)
@@ -0,0 +1,215 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ *
+ * JBoss DNA 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.graph.connector.federation;
+
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.assertThat;
+import java.util.Set;
+import org.jboss.dna.graph.Graph;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * An integration test that verifies the behavior of a {@link FederatedRepositorySource} configured with a federated workspace
+ * using a single mirror projection to an underlying source.
+ */
+public class FederatedRepositorySourceUsingMirrorAndBranchProjectionsTest
+ extends AbstractFederatedRepositorySourceIntegrationTest {
+
+ private String mirrorSourceName;
+ private String mirrorWorkspaceName;
+ private String branchSourceName;
+ private String branchWorkspaceName;
+
+ @Override
+ @Before
+ public void beforeEach() throws Exception {
+ super.beforeEach();
+
+ // Set up the mirror projection ...
+ mirrorSourceName = "Mirror Source";
+ mirrorWorkspaceName = "Mirror Workspace";
+ addProjection("fedSpace", "Mirror Projection", mirrorSourceName, mirrorWorkspaceName, "/ => /");
+
+ // Set up the branch projection ...
+ branchSourceName = mirrorSourceName;
+ branchWorkspaceName = "Branch Workspace";
+ addProjection("fedSpace", "Branch Projection", branchSourceName, branchWorkspaceName, "/Aircraft => /Aircraft");
+
+ // Add some data to the mirror source ...
+ Graph source = graphFor(mirrorSourceName, mirrorWorkspaceName);
+ source.importXmlFrom(getClass().getClassLoader().getResource("cars.xml").toURI()).into("/");
+
+ // Add some data to the mirror source ...
+ Graph branch = graphFor(branchSourceName, branchWorkspaceName);
+ branch.importXmlFrom(getClass().getClassLoader().getResource("aircraft.xml").toURI()).into("/");
+ }
+
+ /**
+ * Assert that the node in the federated repository given by the supplied path represents the same node in the underlying
+ * mirror source given by the same path.
+ *
+ * @param pathToNode the path to the node in the federated repository and in the underlying source
+ */
+ protected void assertMirrorNode( String pathToNode ) {
+ assertSameNode(pathToNode, pathToNode, mirrorSourceName, mirrorWorkspaceName);
+ }
+
+ /**
+ * Assert that the node in the federated repository given by the supplied path represents the same node in the underlying
+ * branch source given by the same path.
+ *
+ * @param pathToNode the path to the node in the federated repository and in the underlying source
+ */
+ protected void assertBranchNode( String pathToNode ) {
+ assertThat(pathToNode.startsWith("/Aircraft"), is(true));
+ assertSameNode(pathToNode, pathToNode, branchSourceName, branchWorkspaceName);
+ }
+
+ /**
+ * Assert that the node does not exist in the federated repository nor in the mirror source or branch source.
+ *
+ * @param pathToNode the path to the node in the federated repository and in the underlying source
+ */
+ protected void assertNoNode( String pathToNode ) {
+ assertNoNode(pathToNode, pathToNode, mirrorSourceName, mirrorWorkspaceName);
+ assertNoNode(pathToNode, pathToNode, branchSourceName, branchWorkspaceName);
+ }
+
+ @Test
+ public void shouldListAllFederatedWorkspaces() {
+ Set<String> workspaces = federated.getWorkspaces();
+ assertThat(workspaces.contains("fedSpace"), is(true));
+ assertThat(workspaces.size(), is(1));
+ }
+
+ @Test
+ public void shouldFederateRootNodeFromMirrorAndBranch() {
+ // The root of the federated repository should have the content from the mirror but should have
+ // an additional child that is the root of the branch ...
+ assertSameNode("/", "/", mirrorSourceName, mirrorWorkspaceName, "Aircraft");
+ }
+
+ @Test
+ public void shouldFederateNodesInMirrorSource() {
+ assertMirrorNode("/Cars");
+ assertMirrorNode("/Cars/Hybrid");
+ assertMirrorNode("/Cars/Hybrid/Toyota Prius");
+ assertMirrorNode("/Cars/Hybrid/Toyota Highlander");
+ assertMirrorNode("/Cars/Hybrid/Nissan Altima");
+ assertMirrorNode("/Cars/Sports/Aston Martin DB9");
+ assertMirrorNode("/Cars/Sports/Infiniti G37");
+ assertMirrorNode("/Cars/Luxury/Cadillac DTS");
+ assertMirrorNode("/Cars/Luxury/Bentley Continental");
+ assertMirrorNode("/Cars/Luxury/Lexus IS350");
+ assertMirrorNode("/Cars/Utility/Land Rover LR2");
+ assertMirrorNode("/Cars/Utility/Land Rover LR3");
+ assertMirrorNode("/Cars/Utility/Hummer H3");
+ assertMirrorNode("/Cars/Utility/Ford F-150");
+ }
+
+ @Test
+ public void shouldFederateNodesInBranchSource() {
+ assertBranchNode("/Aircraft");
+ assertBranchNode("/Aircraft/Business");
+ assertBranchNode("/Aircraft/Business/Gulfstream V");
+ assertBranchNode("/Aircraft/Business/Learjet 45");
+ }
+
+ @Test
+ public void shouldCreateNodeUnderRootInMirrorSource() {
+ federated.createAt("/Hovercraft").with("prop1", "value1").and();
+ assertMirrorNode("/Hovercraft");
+ // And make sure the parent node is the same ...
+ assertSameNode("/", "/", mirrorSourceName, mirrorWorkspaceName, "Aircraft");
+ }
+
+ @Test
+ public void shouldCreateNodeWellBelowRootInMirrorSource() {
+ federated.createAt("/Cars/Hybrid/MyNewHybrid").with("prop1", "value1").and();
+ assertMirrorNode("/Cars/Hybrid/MyNewHybrid");
+ // And make sure the parent node is the same ...
+ assertMirrorNode("/Cars/Hybrid");
+ }
+
+ @Test
+ public void shouldCreateNodeUnderRootInBranchSource() {
+ federated.createAt("/Aircraft/Hovercraft").with("prop1", "value1").and();
+ assertBranchNode("/Aircraft/Hovercraft");
+ // And make sure the parent node is the same ...
+ assertBranchNode("/Aircraft");
+ }
+
+ @Test
+ public void shouldCreateNodeWellBelowRootInBranchSource() {
+ federated.createAt("/Aircraft/Business/HondaJet").with("prop1", "value1").and();
+ assertBranchNode("/Aircraft/Business/HondaJet");
+ // And make sure the parent node is the same ...
+ assertBranchNode("/Aircraft/Business");
+ }
+
+ @Test
+ public void shouldDeleteNodeUnderRootInMirrorSource() {
+ // Create a new node (since there's only one node under the root) ...
+ federated.createAt("/Hovercraft").with("prop1", "value1").and();
+ assertMirrorNode("/Hovercraft");
+ assertSameNode("/", "/", mirrorSourceName, mirrorWorkspaceName, "Aircraft");
+ // Now delete it ...
+ federated.delete("/Hovercraft");
+ assertNoNode("/Hovercraft");
+ }
+
+ @Test
+ public void shouldDeleteNodeWellBelowRootInMirrorSource() {
+ federated.delete("/Cars/Luxury/Cadillac DTS");
+ assertNoNode("/Cars/Luxury/Cadillac DTS");
+ assertMirrorNode("/Cars/Luxury/Lexus IS350");
+ assertMirrorNode("/Cars/Luxury");
+ }
+
+ @Test
+ public void shouldDeleteNodeUnderRootInBranchSource() {
+ federated.delete("/Aircraft/Business");
+ assertNoNode("/Aircraft/Business");
+ assertBranchNode("/Aircraft");
+ assertBranchNode("/Aircraft/Commercial");
+ }
+
+ @Test
+ public void shouldDeleteNodeWellBelowRootInBranchSource() {
+ federated.delete("/Aircraft/Business/Learjet 45");
+ assertNoNode("/Aircraft/Business/Learjet 45");
+ assertBranchNode("/Aircraft/Business/Gulfstream V");
+ assertBranchNode("/Aircraft/Business");
+ assertBranchNode("/Aircraft/Commercial");
+ }
+
+ @Test
+ public void shouldDeleteEverythingInMirrorAndOffsetIfDeletingRoot() {
+ federated.delete("/");
+ // The branch node should still exist, since it's a placeholder ...
+ assertThat(federated.getChildren().of("/").size(), is(1));
+ assertBranchNode("/Aircraft");
+ }
+}
Property changes on: trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/FederatedRepositorySourceUsingMirrorAndBranchProjectionsTest.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/FederatedRepositorySourceUsingMirrorProjectionTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/FederatedRepositorySourceUsingMirrorProjectionTest.java (rev 0)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/FederatedRepositorySourceUsingMirrorProjectionTest.java 2009-05-18 15:36:58 UTC (rev 911)
@@ -0,0 +1,141 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ *
+ * JBoss DNA 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.graph.connector.federation;
+
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.assertThat;
+import java.util.Set;
+import org.jboss.dna.graph.Graph;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * An integration test that verifies the behavior of a {@link FederatedRepositorySource} configured with a federated workspace
+ * using a single mirror projection to an underlying source.
+ */
+public class FederatedRepositorySourceUsingMirrorProjectionTest extends AbstractFederatedRepositorySourceIntegrationTest {
+
+ private String mirrorSourceName;
+ private String mirrorWorkspaceName;
+
+ @Override
+ @Before
+ public void beforeEach() throws Exception {
+ super.beforeEach();
+
+ // Set up the projection ...
+ mirrorSourceName = "Mirror Source";
+ mirrorWorkspaceName = "Mirror Workspace";
+ addProjection("fedSpace", "Mirror Projection", mirrorSourceName, mirrorWorkspaceName, "/ => /");
+
+ // Add some data to the source ...
+ Graph source = graphFor(mirrorSourceName, mirrorWorkspaceName);
+ source.importXmlFrom(getClass().getClassLoader().getResource("cars.xml").toURI()).into("/");
+ }
+
+ /**
+ * Assert that the node in the federated repository given by the supplied path represents the same node in the underlying
+ * mirror source given by the same path.
+ *
+ * @param pathToNode the path to the node in the federated repository and in the underlying source
+ */
+ protected void assertSameNode( String pathToNode ) {
+ assertSameNode(pathToNode, pathToNode, mirrorSourceName, mirrorWorkspaceName);
+ }
+
+ /**
+ * Assert that the node in the source repository given by the supplied path does not exist in the source or in the federated
+ * repository.
+ *
+ * @param pathToNode the path to the node in the federated repository and in the underlying source
+ */
+ protected void assertNoNode( String pathToNode ) {
+ assertNoNode(pathToNode, pathToNode, mirrorSourceName, mirrorWorkspaceName);
+ }
+
+ @Test
+ public void shouldListAllFederatedWorkspaces() {
+ Set<String> workspaces = federated.getWorkspaces();
+ assertThat(workspaces.contains("fedSpace"), is(true));
+ assertThat(workspaces.size(), is(1));
+ }
+
+ @Test
+ public void shouldFederateNodesInMirrorSource() {
+ assertSameNode("/");
+ assertSameNode("/Cars");
+ assertSameNode("/Cars/Hybrid");
+ assertSameNode("/Cars/Hybrid/Toyota Prius");
+ assertSameNode("/Cars/Hybrid/Toyota Highlander");
+ assertSameNode("/Cars/Hybrid/Nissan Altima");
+ assertSameNode("/Cars/Sports/Aston Martin DB9");
+ assertSameNode("/Cars/Sports/Infiniti G37");
+ assertSameNode("/Cars/Luxury/Cadillac DTS");
+ assertSameNode("/Cars/Luxury/Bentley Continental");
+ assertSameNode("/Cars/Luxury/Lexus IS350");
+ assertSameNode("/Cars/Utility/Land Rover LR2");
+ assertSameNode("/Cars/Utility/Land Rover LR3");
+ assertSameNode("/Cars/Utility/Hummer H3");
+ assertSameNode("/Cars/Utility/Ford F-150");
+ }
+
+ @Test
+ public void shouldCreateNodeUnderRootInMirrorSource() {
+ federated.createAt("/Hovercraft").with("prop1", "value1").and();
+ assertSameNode("/Hovercraft");
+ // And make sure the parent node is the same ...
+ assertSameNode("/");
+ }
+
+ @Test
+ public void shouldCreateNodeWellBelowRootInMirrorSource() {
+ federated.createAt("/Cars/Hybrid/MyNewHybrid").with("prop1", "value1").and();
+ assertSameNode("/Cars/Hybrid/MyNewHybrid");
+ // And make sure the parent node is the same ...
+ assertSameNode("/Cars/Hybrid");
+ }
+
+ @Test
+ public void shouldDeleteNodeUnderRootFromMirrorSource() {
+ // Create a node that we can delete ...
+ federated.createAt("/Hovercraft").with("prop1", "value1").and();
+ assertSameNode("/Hovercraft");
+ federated.delete("/Hovercraft");
+ assertNoNode("/Hovercraft");
+ // And make sure the parent node is the same ...
+ assertSameNode("/");
+ // Delete the cars node (which is everything) ...
+ federated.delete("/Cars");
+ // And make sure the parent node is the same ...
+ assertSameNode("/");
+ }
+
+ @Test
+ public void shouldDeleteNodeWellBelowRootFromMirrorSource() {
+ federated.delete("/Cars/Luxury/Cadillac DTS");
+ assertNoNode("/Cars/Luxury/Cadillac DTS");
+ // And make sure the parent node is the same ...
+ assertSameNode("/Cars/Luxury");
+ }
+}
Property changes on: trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/FederatedRepositorySourceUsingMirrorProjectionTest.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/FederatedRepositorySourceUsingOffsetProjectionTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/FederatedRepositorySourceUsingOffsetProjectionTest.java (rev 0)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/FederatedRepositorySourceUsingOffsetProjectionTest.java 2009-05-18 15:36:58 UTC (rev 911)
@@ -0,0 +1,177 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ *
+ * JBoss DNA 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.graph.connector.federation;
+
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.assertThat;
+import java.util.Set;
+import org.jboss.dna.graph.Graph;
+import org.jboss.dna.graph.request.UnsupportedRequestException;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * An integration test that verifies the behavior of a {@link FederatedRepositorySource} configured with a federated workspace
+ * using a single mirror projection to an underlying source.
+ */
+public class FederatedRepositorySourceUsingOffsetProjectionTest extends AbstractFederatedRepositorySourceIntegrationTest {
+
+ private String offsetSourceName;
+ private String offsetWorkspaceName;
+
+ @Override
+ @Before
+ public void beforeEach() throws Exception {
+ super.beforeEach();
+
+ // Set up the projection ...
+ offsetSourceName = "Offset Source";
+ offsetWorkspaceName = "Offset Workspace";
+ addProjection("fedSpace", "Offset Projection", offsetSourceName, offsetWorkspaceName, "/a/b => /");
+
+ // Add some data to the source ...
+ Graph source = graphFor(offsetSourceName, offsetWorkspaceName);
+ source.importXmlFrom(getClass().getClassLoader().getResource("cars.xml").toURI()).into("/");
+ }
+
+ /**
+ * Assert that the node in the source repository given by the supplied path represents the equivalent offset node in the
+ * federated repository given by a path offset from the supplied path.
+ *
+ * @param pathToSourceNode the path to the node in the source repository
+ */
+ protected void assertSameNode( String pathToSourceNode ) {
+ String pathToFedNode = "/a/b" + pathToSourceNode;
+ assertSameNode(pathToFedNode, pathToSourceNode, offsetSourceName, offsetWorkspaceName);
+ }
+
+ /**
+ * Assert that the node in the source repository given by the supplied path does not exist in the source and that the
+ * equivalent offset node in the federated repository also does not exist.
+ *
+ * @param pathToSourceNode the path to the node in the source repository
+ */
+ protected void assertNoNode( String pathToSourceNode ) {
+ String pathToFedNode = "/a/b" + pathToSourceNode;
+ assertNoNode(pathToFedNode, pathToSourceNode, offsetSourceName, offsetWorkspaceName);
+ }
+
+ @Test
+ public void shouldListAllFederatedWorkspaces() {
+ Set<String> workspaces = federated.getWorkspaces();
+ assertThat(workspaces.contains("fedSpace"), is(true));
+ assertThat(workspaces.size(), is(1));
+ }
+
+ @Test
+ public void shouldFederateNodesInOffsetSource() {
+ assertSameNode("/");
+ assertSameNode("/Cars");
+ assertSameNode("/Cars/Hybrid");
+ assertSameNode("/Cars/Hybrid/Toyota Prius");
+ assertSameNode("/Cars/Hybrid/Toyota Highlander");
+ assertSameNode("/Cars/Hybrid/Nissan Altima");
+ assertSameNode("/Cars/Sports/Aston Martin DB9");
+ assertSameNode("/Cars/Sports/Infiniti G37");
+ assertSameNode("/Cars/Luxury/Cadillac DTS");
+ assertSameNode("/Cars/Luxury/Bentley Continental");
+ assertSameNode("/Cars/Luxury/Lexus IS350");
+ assertSameNode("/Cars/Utility/Land Rover LR2");
+ assertSameNode("/Cars/Utility/Land Rover LR3");
+ assertSameNode("/Cars/Utility/Hummer H3");
+ assertSameNode("/Cars/Utility/Ford F-150");
+ }
+
+ @Test( expected = UnsupportedRequestException.class )
+ public void shouldNodeAllowCreatingNodeWithinOffset() {
+ // This is not below the offset '/a/b' and should therefore fail ...
+ federated.createAt("/a/Hovercraft").with("prop1", "value1").and();
+ }
+
+ @Test
+ public void shouldCreateNodeUnderRootInOffsetSource() {
+ // Create the node below the offset ...
+ federated.createAt("/a/b/Hovercraft").with("prop1", "value1").and();
+ // Verify it is the same from the federation and source ...
+ assertSameNode("/Hovercraft");
+ // And make sure the parent node is the same ...
+ assertSameNode("/");
+ }
+
+ @Test
+ public void shouldCreateNodeWellBelowRootInOffsetSource() {
+ // Create the node below the offset ...
+ federated.createAt("/a/b/Cars/Hybrid/MyNewHybrid").with("prop1", "value1").and();
+ // Verify it is the same from the federation and source ...
+ assertSameNode("/Cars/Hybrid/MyNewHybrid");
+ // And make sure the parent node is the same ...
+ assertSameNode("/Cars/Hybrid");
+ }
+
+ @Test
+ public void shouldAllowDeletingNodeAtBottomOfOffset() {
+ // This is not below the offset '/a/b' and should therefore fail ...
+ federated.delete("/a/b");
+ // This deletes everything, and recreates /a/b (since it is a placeholder) ...
+ assertSameNode("/");
+ assertThat(federated.getChildren().of("/a/b").size(), is(0));
+ }
+
+ @Test
+ public void shouldNodeAllowDeletingNodeWithinOffset() {
+ federated.delete("/a");
+ assertSameNode("/");
+ // All nodes should be removed, but the placeholders should remain ...
+ assertThat(federated.getChildren().of("/a").size(), is(1));
+ assertThat(federated.getChildren().of("/a/b").size(), is(0));
+ }
+
+ @Test
+ public void shouldDeleteNodeUnderRootInOffsetSource() {
+ // Create the node below the offset ...
+ federated.createAt("/a/b/Hovercraft").with("prop1", "value1").and();
+ // Verify it is the same from the federation and source ...
+ assertSameNode("/Hovercraft");
+ // And make sure the parent node is the same ...
+ assertSameNode("/");
+ // Delete the node below the offset ...
+ federated.delete("/a/b/Hovercraft");
+ // Verify it is the same from the federation and source ...
+ assertNoNode("/Hovercraft");
+ assertSameNode("/");
+ // Delete the node below the offset ...
+ federated.delete("/a/b/Cars");
+ // Verify it is the same from the federation and source ...
+ assertSameNode("/");
+ }
+
+ @Test
+ public void shouldDeleteNodeWellBelowRootInOffsetSource() {
+ // Delete the node below the offset ...
+ federated.delete("/a/b/Cars/Luxury/Cadillac DTS");
+ assertNoNode("/Cars/Luxury/Cadillac DTS");
+ // And make sure the parent node is the same ...
+ assertSameNode("/Cars/Luxury");
+ }
+}
Property changes on: trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/FederatedRepositorySourceUsingOffsetProjectionTest.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/FederatedRepositoryTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/FederatedRepositoryTest.java (rev 0)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/FederatedRepositoryTest.java 2009-05-18 15:36:58 UTC (rev 911)
@@ -0,0 +1,91 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ *
+ * JBoss DNA 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.graph.connector.federation;
+
+import static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsSame.sameInstance;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.stub;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import org.jboss.dna.graph.cache.CachePolicy;
+import org.jboss.dna.graph.connector.RepositoryConnectionFactory;
+import org.jboss.dna.graph.request.InvalidWorkspaceException;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.MockitoAnnotations;
+import org.mockito.MockitoAnnotations.Mock;
+
+/**
+ * @author Randall Hauch
+ */
+public class FederatedRepositoryTest {
+
+ private String name;
+ private FederatedRepository repository;
+ private CachePolicy defaultCachePolicy;
+ private ExecutorService executor;
+ @Mock
+ private FederatedWorkspace workspace1;
+ @Mock
+ private FederatedWorkspace workspace2;
+ @Mock
+ private RepositoryConnectionFactory connectionFactory;
+
+ @Before
+ public void beforeEach() {
+ MockitoAnnotations.initMocks(this);
+ name = "Repository";
+ executor = Executors.newSingleThreadExecutor();
+ stub(workspace1.getName()).toReturn("workspace1");
+ stub(workspace2.getName()).toReturn("workspace2");
+ Collection<FederatedWorkspace> configs = new ArrayList<FederatedWorkspace>();
+ configs.add(workspace1);
+ configs.add(workspace2);
+ repository = new FederatedRepository(name, connectionFactory, configs, defaultCachePolicy, executor);
+ }
+
+ @Test
+ public void shouldHaveNameUponCreation() {
+ assertThat(repository.getSourceName(), is(name));
+ }
+
+ @Test
+ public void shouldFindExistingWorkspaces() {
+ assertThat(repository.getWorkspace("workspace1"), is(sameInstance(workspace1)));
+ assertThat(repository.getWorkspace("workspace2"), is(sameInstance(workspace2)));
+ }
+
+ @Test
+ public void shouldFindDefaultWorkspaceIfThereIsOne() {
+ assertThat(repository.getWorkspace(null), is(sameInstance(workspace1)));
+ }
+
+ @Test( expected = InvalidWorkspaceException.class )
+ public void shouldNotFindWorkspaceThatDoesNotExist() {
+ repository.getWorkspace("non-existant");
+ }
+}
Property changes on: trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/FederatedRepositoryTest.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/FederatedRequestTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/FederatedRequestTest.java (rev 0)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/FederatedRequestTest.java 2009-05-18 15:36:58 UTC (rev 911)
@@ -0,0 +1,201 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ *
+ * JBoss DNA 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.graph.connector.federation;
+
+import static org.hamcrest.core.Is.is;
+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.mockito.Mockito.mock;
+import java.util.concurrent.CountDownLatch;
+import org.jboss.dna.common.statistic.Stopwatch;
+import org.jboss.dna.graph.connector.federation.FederatedRequest.ProjectedRequest;
+import org.jboss.dna.graph.request.Request;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.MockitoAnnotations;
+import org.mockito.MockitoAnnotations.Mock;
+
+/**
+ *
+ */
+public class FederatedRequestTest {
+
+ private FederatedRequest request;
+ @Mock
+ private Request original;
+ private Projection[] projection;
+ private Request[] projectedRequest;
+
+ @Before
+ public void beforeEach() {
+ MockitoAnnotations.initMocks(this);
+ request = new FederatedRequest(original);
+ projection = new Projection[] {mock(Projection.class), mock(Projection.class), mock(Projection.class)};
+ projectedRequest = new Request[] {mock(Request.class), mock(Request.class), mock(Request.class)};
+ }
+
+ @Test
+ public void shouldHaveOriginalRequest() {
+ assertThat(request.original(), is(sameInstance(original)));
+ }
+
+ @Test
+ public void shouldAddFirstProjectedRequest() {
+ request.add(projectedRequest[0], false, false, projection[0]);
+ assertThat(request.hasIncompleteRequests(), is(true));
+ assertThat(request.getFirstProjectedRequest().getProjection(), is(sameInstance(projection[0])));
+ assertThat(request.getFirstProjectedRequest().getRequest(), is(sameInstance(projectedRequest[0])));
+ assertThat(request.getFirstProjectedRequest().hasNext(), is(false));
+ assertThat(request.getFirstProjectedRequest().next(), is(nullValue()));
+ }
+
+ @Test
+ public void shouldAddMultipleProjectedRequests() {
+ request.add(projectedRequest[0], false, false, projection[0]);
+ request.add(projectedRequest[1], false, true, projection[1]);
+ request.add(projectedRequest[2], false, false, projection[2]);
+ assertThat(request.hasIncompleteRequests(), is(true));
+
+ ProjectedRequest first = request.getFirstProjectedRequest();
+ assertThat(first.getProjection(), is(sameInstance(projection[0])));
+ assertThat(first.getRequest(), is(sameInstance(projectedRequest[0])));
+ assertThat(first.isComplete(), is(false));
+ assertThat(first.hasNext(), is(true));
+ assertThat(first.next(), is(notNullValue()));
+
+ ProjectedRequest second = first.next();
+ assertThat(second.getProjection(), is(sameInstance(projection[1])));
+ assertThat(second.getRequest(), is(sameInstance(projectedRequest[1])));
+ assertThat(second.isComplete(), is(true));
+ assertThat(second.hasNext(), is(true));
+ assertThat(second.next(), is(notNullValue()));
+
+ ProjectedRequest third = second.next();
+ assertThat(third.getProjection(), is(sameInstance(projection[2])));
+ assertThat(third.getRequest(), is(sameInstance(projectedRequest[2])));
+ assertThat(third.isComplete(), is(false));
+ assertThat(third.hasNext(), is(false));
+ assertThat(third.next(), is(nullValue()));
+ }
+
+ @Test
+ public void shouldAddMultipleProjectedRequestsAddedInMethodChain() {
+ request.add(projectedRequest[0], false, false, projection[0]).add(projectedRequest[1], false, true, projection[1]).add(projectedRequest[2],
+ false,
+ false,
+ projection[2]);
+ assertThat(request.hasIncompleteRequests(), is(true));
+
+ ProjectedRequest first = request.getFirstProjectedRequest();
+ assertThat(first.getProjection(), is(sameInstance(projection[0])));
+ assertThat(first.getRequest(), is(sameInstance(projectedRequest[0])));
+ assertThat(first.isComplete(), is(false));
+ assertThat(first.hasNext(), is(true));
+ assertThat(first.next(), is(notNullValue()));
+
+ ProjectedRequest second = first.next();
+ assertThat(second.getProjection(), is(sameInstance(projection[1])));
+ assertThat(second.getRequest(), is(sameInstance(projectedRequest[1])));
+ assertThat(second.isComplete(), is(true));
+ assertThat(second.hasNext(), is(true));
+ assertThat(second.next(), is(notNullValue()));
+
+ ProjectedRequest third = second.next();
+ assertThat(third.getProjection(), is(sameInstance(projection[2])));
+ assertThat(third.getRequest(), is(sameInstance(projectedRequest[2])));
+ assertThat(third.isComplete(), is(false));
+ assertThat(third.hasNext(), is(false));
+ assertThat(third.next(), is(nullValue()));
+ }
+
+ @Test
+ public void shouldCreateCountDownLatchUponFreezeIfNumberOfIncompleteProjectedRequestsIsNonZero() {
+ request.add(projectedRequest[0], false, false, projection[0]);
+ assertThat(request.getLatch(), is(nullValue()));
+ request.freeze();
+ assertThat(request.getLatch(), is(notNullValue()));
+ assertThat(request.getLatch().getCount(), is(1L));
+ assertThat(request.hasIncompleteRequests(), is(true));
+ }
+
+ @Test
+ public void shouldNotCreateCountDownLatchUponFreezeIfNumberOfIncompleteProjectedRequestsIsZero() {
+ request.add(projectedRequest[0], false, true, projection[0]);
+ assertThat(request.getLatch(), is(nullValue()));
+ request.freeze();
+ assertThat(request.getLatch(), is(nullValue()));
+ assertThat(request.hasIncompleteRequests(), is(false));
+ }
+
+ @Test
+ public void shouldNotBlockWhenAwaitingOnRequestWithNoIncompleteRequests() throws InterruptedException {
+ request.add(projectedRequest[0], false, true, projection[0]);
+ request.freeze();
+ assertThat(request.hasIncompleteRequests(), is(false));
+ request.await();
+ }
+
+ @Test
+ public void shouldBlockWhenAwaitingOnRequestWithIncompleteRequests() throws InterruptedException {
+ request.add(projectedRequest[0], false, false, projection[0]);
+ request.add(projectedRequest[1], false, false, projection[1]);
+ request.add(projectedRequest[2], false, false, projection[2]);
+ request.freeze();
+ assertThat(request.hasIncompleteRequests(), is(true));
+ CountDownLatch latch = request.getLatch();
+ assertThat(latch.getCount(), is(3L));
+ Stopwatch sw = new Stopwatch();
+ sw.start();
+ new Thread(new CountDownRunnable(latch, 100L)).start();
+ request.await(); // this blocks until the latch reaches 0
+ assertThat(latch.getCount(), is(0L));
+ sw.stop();
+ assertThat(sw.getTotalDuration().getDurationInMilliseconds().intValue() >= 250, is(true));
+ }
+
+ protected static class CountDownRunnable implements Runnable {
+ private final CountDownLatch latch;
+ private final long sleepTimeInMillis;
+
+ protected CountDownRunnable( CountDownLatch latch,
+ long sleepTimeInMillis ) {
+ assert latch != null;
+ this.latch = latch;
+ this.sleepTimeInMillis = sleepTimeInMillis;
+ }
+
+ public void run() {
+ while (latch.getCount() != 0) {
+ try {
+ Thread.sleep(sleepTimeInMillis);
+ } catch (InterruptedException err) {
+ System.err.println("Error: interrupted while sleeping before counting down in " + this.getClass().getName());
+ }
+ latch.countDown();
+ }
+ }
+ }
+}
Property changes on: trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/FederatedRequestTest.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/FederatedWorkspaceTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/FederatedWorkspaceTest.java (rev 0)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/FederatedWorkspaceTest.java 2009-05-18 15:36:58 UTC (rev 911)
@@ -0,0 +1,181 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ *
+ * JBoss DNA 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.graph.connector.federation;
+
+import static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsNull.notNullValue;
+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;
+import java.util.ArrayList;
+import java.util.List;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.cache.CachePolicy;
+import org.jboss.dna.graph.connector.RepositoryContext;
+import org.jboss.dna.graph.connector.federation.Projection.Rule;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.MockitoAnnotations;
+import org.mockito.MockitoAnnotations.Mock;
+
+/**
+ *
+ */
+public class FederatedWorkspaceTest {
+
+ private FederatedWorkspace workspace;
+ private ExecutionContext context;
+ private CachePolicy cachePolicy;
+ private String sourceName;
+ private String workspaceName;
+ private List<Projection> projections;
+ @Mock
+ private RepositoryContext repositoryContext;
+
+ @Before
+ public void beforeEach() {
+ MockitoAnnotations.initMocks(this);
+ sourceName = "federated";
+ workspaceName = "my workspace";
+ context = new ExecutionContext();
+ stub(repositoryContext.getExecutionContext()).toReturn(context);
+ projections = new ArrayList<Projection>();
+ projections.add(new Projection("source1", "workspace1", false, rule("/a => /a1")));
+ projections.add(new Projection("source2", "workspace2", false, rule("/a => /a2", "/b => /b")));
+ projections.add(new Projection("source2", "workspace4", false, rule("/x => /y")));
+ projections.add(new Projection("source3", "workspace3", false, rule("/c/d/e => /c1")));
+ workspace = new FederatedWorkspace(repositoryContext, sourceName, workspaceName, projections, cachePolicy);
+ }
+
+ protected Rule[] rule( String... rule ) {
+ Rule[] rules = new Rule[rule.length];
+ for (int i = 0; i != rule.length; ++i) {
+ rules[i] = Projection.fromString(rule[i], context);
+ }
+ return rules;
+ }
+
+ @Test
+ public void shouldHaveSameRepositoryContextPassedIntoConstructor() {
+ assertThat(workspace.getRepositoryContext(), is(sameInstance(repositoryContext)));
+ }
+
+ @Test
+ public void shouldHaveSameSourceNamePassedIntoConstructor() {
+ assertThat(workspace.getSourceName(), is(sameInstance(sourceName)));
+ }
+
+ @Test
+ public void shouldHaveSameWorkspaceNamePassedIntoConstructor() {
+ assertThat(workspace.getName(), is(sameInstance(workspaceName)));
+ }
+
+ @Test
+ public void shouldHaveSameCachePolicyPassedIntoContructor() {
+ assertThat(workspace.getCachePolicy(), is(sameInstance(cachePolicy)));
+ }
+
+ @Test
+ public void shouldHaveSameProjectionsPassedIntoConstructor() {
+ assertThat(workspace.getProjections(), is(projections));
+ }
+
+ @Test
+ public void shouldHaveCorrectMappingOfProjectionsBySource() {
+ assertThat(workspace.getProjectionsBySourceName().get("source1"), is(projections.subList(0, 1)));
+ assertThat(workspace.getProjectionsBySourceName().get("source2"), is(projections.subList(1, 3)));
+ assertThat(workspace.getProjectionsBySourceName().get("source3"), is(projections.subList(3, 4)));
+ }
+
+ @Test
+ public void shouldHaveProblemsContainer() {
+ assertThat(workspace.getProblems(), is(notNullValue()));
+ }
+
+ @Test
+ public void shouldCorrectlyDetermineContainmentBySourceAndWorkspaceName() {
+ assertThat(workspace.contains("source1", "workspace1"), is(true));
+ assertThat(workspace.contains("source2", "workspace2"), is(true));
+ assertThat(workspace.contains("source2", "workspace4"), is(true));
+ assertThat(workspace.contains("source3", "workspace3"), is(true));
+ }
+
+ @Test
+ public void shouldCorrectlyDetermineNonContainmentOfCaseSensitiveSourceNotInProjections() {
+ assertThat(workspace.contains("source x", "workspace3"), is(false));
+ assertThat(workspace.contains("Source1", "workspace3"), is(false));
+ assertThat(workspace.contains("", "workspace3"), is(false));
+ }
+
+ @Test
+ public void shouldCorrectlyDetermineNonContainmentOfCaseSensitiveWorkspaceNotInProjections() {
+ assertThat(workspace.contains("source1", "Workspace1"), is(false));
+ assertThat(workspace.contains("source1", "workspace 1"), is(false));
+ assertThat(workspace.contains("source1", "no workspace"), is(false));
+ }
+
+ @Test
+ public void shouldConsiderWorkspacesEqualIfSameSourceNameAndSameWorkspaceNameAndSameProjections() {
+ RepositoryContext otherReposContext = mock(RepositoryContext.class);
+ stub(otherReposContext.getExecutionContext()).toReturn(context);
+ CachePolicy otherPolicy = mock(CachePolicy.class);
+ FederatedWorkspace other = new FederatedWorkspace(otherReposContext, sourceName, workspaceName, projections, otherPolicy);
+ assertThat(workspace.equals(other), is(true));
+ }
+
+ @Test
+ public void shouldConsiderWorkspacesNotEqualIfDifferentSourceName() {
+ RepositoryContext otherReposContext = mock(RepositoryContext.class);
+ stub(otherReposContext.getExecutionContext()).toReturn(context);
+ CachePolicy otherPolicy = mock(CachePolicy.class);
+ FederatedWorkspace other = new FederatedWorkspace(otherReposContext, "diff", workspaceName, projections, otherPolicy);
+ assertThat(workspace.equals(other), is(false));
+ }
+
+ @Test
+ public void shouldConsiderWorkspacesNotEqualIfDifferentWorkspaceName() {
+ RepositoryContext otherReposContext = mock(RepositoryContext.class);
+ stub(otherReposContext.getExecutionContext()).toReturn(context);
+ CachePolicy otherPolicy = mock(CachePolicy.class);
+ FederatedWorkspace other = new FederatedWorkspace(otherReposContext, sourceName, "diff", projections, otherPolicy);
+ assertThat(workspace.equals(other), is(false));
+ }
+
+ @Test
+ public void shouldConsiderWorkspacesNotEqualIfDifferentProjections() {
+ RepositoryContext otherReposContext = mock(RepositoryContext.class);
+ stub(otherReposContext.getExecutionContext()).toReturn(context);
+ CachePolicy otherPolicy = mock(CachePolicy.class);
+ FederatedWorkspace other = new FederatedWorkspace(otherReposContext, sourceName, workspaceName,
+ projections.subList(0, 3), otherPolicy);
+ assertThat(workspace.equals(other), is(false));
+ }
+
+ @Test
+ public void shouldDetermineProjection() {
+
+ }
+
+}
Property changes on: trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/FederatedWorkspaceTest.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/ForkRequestProcessorChannelTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/ForkRequestProcessorChannelTest.java (rev 0)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/ForkRequestProcessorChannelTest.java 2009-05-18 15:36:58 UTC (rev 911)
@@ -0,0 +1,182 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ *
+ * JBoss DNA 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.graph.connector.federation;
+
+import static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsSame.sameInstance;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.stub;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.connector.MockRepositoryConnection;
+import org.jboss.dna.graph.connector.RepositoryConnection;
+import org.jboss.dna.graph.connector.RepositoryConnectionFactory;
+import org.jboss.dna.graph.connector.federation.ForkRequestProcessor.Channel;
+import org.jboss.dna.graph.request.Request;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.MockitoAnnotations;
+import org.mockito.MockitoAnnotations.Mock;
+
+/**
+ *
+ */
+public class ForkRequestProcessorChannelTest {
+
+ private ExecutionContext context;
+ private String sourceName;
+ private Channel channel;
+ private List<Request> requests;
+ private ExecutorService executor;
+ private LinkedList<Request> executedRequests;
+ private RepositoryConnection connection;
+ @Mock
+ private RepositoryConnectionFactory connectionFactory;
+
+ @Before
+ public void beforeEach() {
+ MockitoAnnotations.initMocks(this);
+ context = new ExecutionContext();
+ sourceName = "SourceA";
+ channel = new ForkRequestProcessor.Channel(sourceName);
+ requests = new ArrayList<Request>();
+ requests.add(mock(Request.class));
+ requests.add(mock(Request.class));
+ requests.add(mock(Request.class));
+ requests.add(mock(Request.class));
+
+ // Create the mock connection ...
+ executedRequests = new LinkedList<Request>(); // this is where requests submitted to the connection will go
+ connection = new MockRepositoryConnection(sourceName, executedRequests);
+
+ // Stub the connection factory ...
+ stub(connectionFactory.createConnection(sourceName)).toReturn(connection);
+
+ // Create the executor ...
+ executor = Executors.newSingleThreadExecutor();
+ }
+
+ @Test
+ public void shouldCreateEmptyIteratorIfDoneCalledBeforeObtainingIterator() {
+ channel.done();
+ Iterator<Request> iter = channel.createIterator();
+ assertThat(iter.hasNext(), is(false));
+ }
+
+ @Test
+ public void shouldCreateEmptyIteratorIfDoneCalledAfterObtainingIterator() {
+ Iterator<Request> iter = channel.createIterator();
+ channel.done();
+ assertThat(iter.hasNext(), is(false));
+ }
+
+ @Test
+ public void shouldCreateIteratorOverRequestsAddedToChannelAfterObtainingIterator() {
+ Iterator<Request> iter = channel.createIterator();
+ // Add the requests ...
+ for (Request request : requests) {
+ channel.add(request);
+ }
+ // Call done ...
+ channel.done();
+ // Start iterating ...
+ for (Request expected : requests) {
+ assertThat(iter.hasNext(), is(true));
+ assertThat(iter.next(), is(sameInstance(expected)));
+ }
+ assertThat(iter.hasNext(), is(false));
+ }
+
+ @Test
+ public void shouldCreateIteratorOverRequestsAddedToChannelAfterBeginningIteration() {
+ Iterator<Request> iter = channel.createIterator();
+ // Add the requests in a separate thread ...
+ new Thread(new AddRequestsRunnable(channel, requests, 100)).start();
+ // Start iterating ...
+ for (Request expected : requests) {
+ assertThat(iter.hasNext(), is(true)); // blocks
+ assertThat(iter.next(), is(sameInstance(expected)));
+ }
+ assertThat(iter.hasNext(), is(false));
+ }
+
+ @Test
+ public void shouldSubmitRequestsToConnection() throws Exception {
+ // Start the channel ...
+ channel.start(executor, context, connectionFactory);
+
+ // Submit the requests to the channel ...
+ for (Request request : requests) {
+ channel.add(request);
+ }
+
+ // Mark the channel as done ...
+ channel.done();
+
+ // Wait until the channel has completed ...
+ channel.await();
+
+ // Verify that all the requests to the channel were processed ...
+ Iterator<Request> iter = executedRequests.iterator();
+ for (Request expected : requests) {
+ assertThat(iter.hasNext(), is(true));
+ assertThat(iter.next(), is(sameInstance(expected)));
+ }
+ assertThat(iter.hasNext(), is(false));
+ }
+
+ protected static class AddRequestsRunnable implements Runnable {
+ private final Channel channel;
+ private final Iterator<Request> requests;
+ private final int intervalInMillis;
+
+ protected AddRequestsRunnable( Channel channel,
+ List<Request> requests,
+ int intervalInMillis ) {
+ this.requests = requests.iterator();
+ this.intervalInMillis = intervalInMillis;
+ this.channel = channel;
+ }
+
+ public void run() {
+ while (requests.hasNext()) {
+ try {
+ Thread.sleep(intervalInMillis);
+ } catch (InterruptedException err) {
+ fail(err.getMessage());
+ }
+ channel.add(requests.next());
+ }
+ // Call done ...
+ channel.done();
+ }
+ }
+}
Property changes on: trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/ForkRequestProcessorChannelTest.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/ForkRequestProcessorTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/ForkRequestProcessorTest.java (rev 0)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/ForkRequestProcessorTest.java 2009-05-18 15:36:58 UTC (rev 911)
@@ -0,0 +1,365 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ *
+ * JBoss DNA 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.graph.connector.federation;
+
+import static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsInstanceOf.instanceOf;
+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;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.Location;
+import org.jboss.dna.graph.connector.MockRepositoryConnection;
+import org.jboss.dna.graph.connector.RepositoryConnectionFactory;
+import org.jboss.dna.graph.connector.federation.Projection.Rule;
+import org.jboss.dna.graph.property.DateTime;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.Path;
+import org.jboss.dna.graph.property.PathNotFoundException;
+import org.jboss.dna.graph.property.Property;
+import org.jboss.dna.graph.property.Path.Segment;
+import org.jboss.dna.graph.request.InvalidWorkspaceException;
+import org.jboss.dna.graph.request.ReadNodeRequest;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.MockitoAnnotations;
+import org.mockito.MockitoAnnotations.Mock;
+
+/**
+ *
+ */
+public class ForkRequestProcessorTest {
+
+ private ForkRequestProcessor processor;
+ private ExecutionContext context;
+ private DateTime now;
+ private String sourceName;
+ private String workspaceName;
+ private String sourceNameA;
+ private String sourceNameB;
+ private String sourceNameC;
+ private String workspaceNameA;
+ private String workspaceNameB;
+ private String workspaceNameC;
+ private String nonExistantWorkspaceName;
+ private LinkedList<FederatedRequest> federatedRequests;
+ private ExecutorService executor;
+ @Mock
+ private FederatedWorkspace workspace;
+ @Mock
+ private FederatedRepository repository;
+ @Mock
+ private RepositoryConnectionFactory connectionFactory;
+ private Projection projectionA;
+ private Projection projectionB;
+ // private Projection projectionC;
+ private MockRepositoryConnection connectionForSourceA;
+ private MockRepositoryConnection connectionForSourceB;
+ private MockRepositoryConnection connectionForSourceC;
+ private Map<Name, Property> properties;
+ private List<Location> children;
+
+ @Before
+ public void beforeEach() {
+ MockitoAnnotations.initMocks(this);
+ executor = Executors.newSingleThreadExecutor();
+ sourceName = "MySource";
+ workspaceName = "MyWorkspace";
+ sourceNameA = "SourceA";
+ sourceNameB = "SourceB";
+ sourceNameC = "SourceC";
+ workspaceNameA = "WorkspaceA";
+ workspaceNameB = "WorkspaceB";
+ workspaceNameC = "WorkspaceC";
+ nonExistantWorkspaceName = "Non-Existant Workspace";
+ context = new ExecutionContext();
+ now = context.getValueFactories().getDateFactory().create();
+ federatedRequests = new LinkedList<FederatedRequest>();
+ children = new ArrayList<Location>();
+ properties = new HashMap<Name, Property>();
+
+ // Set up the connection factory and connection ...
+ connectionForSourceA = new MockRepositoryConnection(sourceNameA);
+ connectionForSourceB = new MockRepositoryConnection(sourceNameB);
+ connectionForSourceC = new MockRepositoryConnection(sourceNameC);
+ stub(connectionFactory.createConnection(sourceNameA)).toReturn(connectionForSourceA);
+ stub(connectionFactory.createConnection(sourceNameB)).toReturn(connectionForSourceB);
+ stub(connectionFactory.createConnection(sourceNameC)).toReturn(connectionForSourceC);
+
+ // Stub the FederatedRepository ...
+ stub(repository.getSourceName()).toReturn(sourceName);
+ stub(repository.getWorkspace(workspaceName)).toReturn(workspace);
+ stub(repository.getExecutor()).toReturn(executor);
+ stub(repository.getConnectionFactory()).toReturn(connectionFactory);
+ stub(repository.getWorkspace(nonExistantWorkspaceName)).toThrow(new InvalidWorkspaceException());
+
+ // Stub the FederatedWorkspace ...
+ stub(workspace.getName()).toReturn(workspaceName);
+ // workspace.project(context,location) needs to be stubbed ...
+
+ // Set up the projections ...
+ projectionA = new Projection(sourceNameA, workspaceNameA, false, rules("/a => /"));
+ projectionB = new Projection(sourceNameB, workspaceNameB, false, rules("/b => /"));
+ // projectionC = new Projection(sourceNameC, workspaceNameC, rules("/c => /"));
+
+ // Now set up the processor ...
+ processor = new ForkRequestProcessor(repository, context, now, federatedRequests);
+ }
+
+ protected Rule[] rules( String... rule ) {
+ Rule[] rules = new Rule[rule.length];
+ for (int i = 0; i != rule.length; ++i) {
+ rules[i] = Projection.fromString(rule[i], context);
+ }
+ return rules;
+ }
+
+ public Name name( String name ) {
+ return context.getValueFactories().getNameFactory().create(name);
+ }
+
+ public Path path( String path ) {
+ return context.getValueFactories().getPathFactory().create(path);
+ }
+
+ public Segment segment( String segment ) {
+ return context.getValueFactories().getPathFactory().createSegment(segment);
+ }
+
+ public Location location( String path ) {
+ return Location.create(path(path));
+ }
+
+ public void addProperty( String name,
+ Object... values ) {
+ Property property = context.getPropertyFactory().create(name(name), values);
+ properties.put(property.getName(), property);
+ }
+
+ public void addChild( Location parent,
+ String childName ) {
+ Path path = context.getValueFactories().getPathFactory().create(parent.getPath(), segment(childName));
+ children.add(Location.create(path));
+ }
+
+ @Test
+ public void shouldReturnImmediatelyFromAwaitIfNoChannelsAreMade() throws Exception {
+ processor.await();
+ }
+
+ @Test
+ public void shouldReturnFromAwaitAfterChannelsAreCompleted() throws Exception {
+ ReadNodeRequest requestA = new ReadNodeRequest(location("/a/some"), workspaceNameA);
+ ReadNodeRequest requestB = new ReadNodeRequest(location("/b/some"), workspaceNameB);
+ ReadNodeRequest requestC = new ReadNodeRequest(location("/c/some"), workspaceNameC);
+ processor.submit(requestA, sourceNameA);
+ processor.submit(requestB, sourceNameB);
+ processor.submit(requestC, sourceNameC);
+ processor.close();
+ processor.await();
+ }
+
+ @Test
+ public void shouldReturnReadableLocation() {
+ Location location = location("/dna:something/jcr:else");
+ String result = processor.readable(location("/dna:something/jcr:else"));
+ assertThat(result, is(location.getString(context.getNamespaceRegistry())));
+ }
+
+ @Test
+ public void shouldFindFederatedWorkspaceByName() {
+ ReadNodeRequest request = new ReadNodeRequest(location("/some"), this.workspace.getName());
+ FederatedWorkspace workspace = processor.getWorkspace(request, request.inWorkspace());
+ assertThat(workspace, is(sameInstance(this.workspace)));
+ }
+
+ @Test
+ public void shouldRecordErrorOnRequestIfFederatedWorkspaceCouldNotBeFoundByName() {
+ ReadNodeRequest request = new ReadNodeRequest(location("/some"), nonExistantWorkspaceName);
+ FederatedWorkspace workspace = processor.getWorkspace(request, request.inWorkspace());
+ assertThat(workspace, is(nullValue()));
+ assertThat(request.hasError(), is(true));
+ assertThat(request.getError(), is(instanceOf(InvalidWorkspaceException.class)));
+ }
+
+ @Test
+ public void shouldSubmitFederatedRequestToQueueIfFederatedRequestHasNoIncompleteRequests() {
+ FederatedRequest request = mock(FederatedRequest.class);
+ stub(request.hasIncompleteRequests()).toReturn(false);
+ processor.submit(request);
+ assertThat(federatedRequests.size(), is(1));
+ assertThat(federatedRequests.get(0), is(sameInstance(request)));
+ }
+
+ @Test
+ public void shouldSubmitToSourcesTheSingleRequestInFederatedRequestAndAddFederatedRequestToQueue() throws Exception {
+ ReadNodeRequest original = new ReadNodeRequest(location("/a/some"), this.workspace.getName());
+ FederatedRequest request = new FederatedRequest(original);
+ // Create the projection and the source request ...
+ ReadNodeRequest sourceRequest = new ReadNodeRequest(location("/some"), workspaceNameA);
+ request.add(sourceRequest, false, false, projectionA);
+ assertThat(request.getFirstProjectedRequest().getProjection(), is(sameInstance(projectionA)));
+ assertThat(request.getFirstProjectedRequest().hasNext(), is(false));
+
+ // Submit the federated request ...
+ processor.submit(request);
+ assertThat(federatedRequests.size(), is(1));
+ assertThat(federatedRequests.get(0), is(sameInstance(request)));
+ // Wait for the processor to complete all source channels ...
+ processor.close();
+ processor.await();
+ // The source should have received something like the original
+ assertThat(connectionForSourceA.getProcessedRequests().contains(sourceRequest), is(true));
+ }
+
+ @Test
+ public void shouldSubmitToSourcesTheMultipleRequestsInFederatedRequestAndAddFederatedRequestToQueue() throws Exception {
+ ReadNodeRequest original = new ReadNodeRequest(location("/some"), this.workspace.getName());
+ FederatedRequest request = new FederatedRequest(original);
+
+ // Create the first projection and the source request ...
+ ReadNodeRequest sourceRequestA = new ReadNodeRequest(location("/a/some/other"), workspaceNameA);
+ request.add(sourceRequestA, false, false, projectionA);
+ assertThat(request.getFirstProjectedRequest().getProjection(), is(sameInstance(projectionA)));
+
+ // Create the second projection and the source request ...
+ ReadNodeRequest sourceRequestB = new ReadNodeRequest(location("/b/some/other"), workspaceNameB);
+ request.add(sourceRequestB, false, false, projectionB);
+ assertThat(request.getFirstProjectedRequest().next().getProjection(), is(sameInstance(projectionB)));
+
+ assertThat(request.getFirstProjectedRequest().next().hasNext(), is(false));
+
+ // Submit the federated request ...
+ processor.submit(request);
+ assertThat(federatedRequests.size(), is(1));
+ assertThat(federatedRequests.get(0), is(sameInstance(request)));
+ // Wait for the processor to complete all source channels ...
+ processor.close();
+ processor.await();
+ // The source should have received something like the original
+ assertThat(connectionForSourceA.getProcessedRequests().contains(sourceRequestA), is(true));
+ assertThat(connectionForSourceB.getProcessedRequests().contains(sourceRequestB), is(true));
+ assertThat(connectionForSourceC.getProcessedRequests().isEmpty(), is(true));
+ }
+
+ // ----------------------------------------------------------------------------------------------------------------
+ // process(ReadNodeRequest)
+ // ----------------------------------------------------------------------------------------------------------------
+
+ @Test
+ public void shouldNotForkReadNodeRequestIfWorkspaceNameIsInvalid() {
+ // Stub the workspace to have no projection ...
+ Location locationInFed = location("/a/b");
+ stub(workspace.project(context, locationInFed, false)).toReturn(null);
+
+ ReadNodeRequest request = new ReadNodeRequest(locationInFed, nonExistantWorkspaceName);
+ processor.process(request);
+ assertThat(request.hasError(), is(true));
+ assertThat(request.getError(), is(instanceOf(InvalidWorkspaceException.class)));
+ }
+
+ @Test
+ public void shouldNotForkReadNodeRequestIfThereIsNoProjection() {
+ // Stub the workspace to have no projection ...
+ Location locationInFed = location("/a/b");
+ stub(workspace.project(context, locationInFed, false)).toReturn(null);
+
+ ReadNodeRequest request = new ReadNodeRequest(locationInFed, workspaceName);
+ processor.process(request);
+ assertThat(request.hasError(), is(true));
+ assertThat(request.getError(), is(instanceOf(PathNotFoundException.class)));
+ }
+
+ @Test
+ public void shouldSubmitSingleSourceRequestWhenProcessingSingleReadNodeRequest() throws Exception {
+ // Stub the workspace to have a projection ...
+ Location locationInFed = location("/a/x/y");
+ Location locationInSource = location("/x/y");
+ ProjectedNode projectedNode = new ProxyNode(projectionA, locationInSource, locationInFed, false);
+ stub(workspace.project(context, locationInFed, false)).toReturn(projectedNode);
+
+ ReadNodeRequest request = new ReadNodeRequest(locationInFed, workspaceName);
+ processor.process(request);
+ assertThat(request.hasError(), is(false));
+
+ // Check that the federated request has the right information ...
+ FederatedRequest fedRequest = federatedRequests.poll();
+ ReadNodeRequest projectedRequest = (ReadNodeRequest)fedRequest.getFirstProjectedRequest().getRequest();
+ assertThat(projectedRequest.at(), is(locationInSource));
+ assertThat(fedRequest.getFirstProjectedRequest().hasNext(), is(false));
+
+ // Close the processor ...
+ processor.close();
+ processor.await();
+
+ // Verify the source saw the expected read ...
+ ReadNodeRequest sourceRequest = (ReadNodeRequest)connectionForSourceA.getProcessedRequests().poll();
+ assertThat(sourceRequest.at().getPath(), is(locationInSource.getPath()));
+ assertThat(connectionForSourceB.getProcessedRequests().isEmpty(), is(true));
+ assertThat(connectionForSourceC.getProcessedRequests().isEmpty(), is(true));
+ }
+
+ @Test
+ public void shouldNotSubmitPlaceholderNodesWhenProcessingReadNodeRequest() throws Exception {
+ // Stub the workspace to have a projection ...
+ Location locationInFed = location("/a/x/y");
+ Location locationInSource = location("/a/x/y");
+ addProperty("propA", "valueA");
+ addProperty("propB", "valueB");
+ addChild(locationInSource, "child1");
+ addChild(locationInSource, "child2");
+ PlaceholderNode projectedNode = new PlaceholderNode(locationInSource, properties, children);
+ stub(workspace.project(context, locationInFed, false)).toReturn(projectedNode);
+
+ ReadNodeRequest request = new ReadNodeRequest(locationInFed, workspaceName);
+ processor.process(request);
+ assertThat(request.hasError(), is(false));
+
+ // Check that the federated request has the right information ...
+ FederatedRequest fedRequest = federatedRequests.poll();
+ ReadNodeRequest projectedRequest = (ReadNodeRequest)fedRequest.getFirstProjectedRequest().getRequest();
+ assertThat(projectedRequest.at(), is(locationInFed));
+ assertThat(projectedRequest.getChildren(), is(children));
+ assertThat(projectedRequest.getPropertiesByName(), is(properties));
+ assertThat(fedRequest.getFirstProjectedRequest().hasNext(), is(false));
+
+ // Close the processor ...
+ processor.close();
+ processor.await();
+
+ // Verify that no sources saw a request ...
+ assertThat(connectionForSourceA.getProcessedRequests().isEmpty(), is(true));
+ assertThat(connectionForSourceB.getProcessedRequests().isEmpty(), is(true));
+ assertThat(connectionForSourceC.getProcessedRequests().isEmpty(), is(true));
+ }
+}
Property changes on: trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/ForkRequestProcessorTest.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Copied: trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/GeneralProjectorTest.java (from rev 910, trunk/dna-graph/src/main/java/org/jboss/dna/graph/DnaLexicon.java)
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/GeneralProjectorTest.java (rev 0)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/GeneralProjectorTest.java 2009-05-18 15:36:58 UTC (rev 911)
@@ -0,0 +1,62 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ *
+ * JBoss DNA 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.graph.connector.federation;
+
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ *
+ */
+public class GeneralProjectorTest extends AbstractProjectorTest<GeneralProjector> {
+
+ private String sourceNameA;
+ private String sourceNameB;
+ private String sourceNameC;
+ private String workspaceNameA;
+ private String workspaceNameB;
+ private String workspaceNameC;
+
+ @Before
+ @Override
+ public void beforeEach() {
+ super.beforeEach();
+ sourceNameA = "Source A";
+ sourceNameB = "Source B";
+ sourceNameC = "Source C";
+ workspaceNameA = "Workspace A";
+ workspaceNameB = "Workspace B";
+ workspaceNameC = "Workspace C";
+ addProjection(sourceNameA, workspaceNameA, "/a => /");
+ addProjection(sourceNameB, workspaceNameB, "/b1/b2 => /");
+ addProjection(sourceNameC, workspaceNameC, "/c1/c2/c3 => /x/y");
+ this.projector = GeneralProjector.with(context, projections);
+ }
+
+ @Test
+ public void should() {
+
+ }
+
+}
Added: trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/JoinRequestProcessorTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/JoinRequestProcessorTest.java (rev 0)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/JoinRequestProcessorTest.java 2009-05-18 15:36:58 UTC (rev 911)
@@ -0,0 +1,350 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ *
+ * JBoss DNA 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.graph.connector.federation;
+
+import static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsSame.sameInstance;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.stub;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+import org.jboss.dna.graph.DnaLexicon;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.Location;
+import org.jboss.dna.graph.cache.CachePolicy;
+import org.jboss.dna.graph.connector.federation.Projection.Rule;
+import org.jboss.dna.graph.property.DateTime;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.Path;
+import org.jboss.dna.graph.property.Property;
+import org.jboss.dna.graph.property.Path.Segment;
+import org.jboss.dna.graph.request.ReadNodeRequest;
+import org.jboss.dna.graph.request.Request;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.MockitoAnnotations;
+import org.mockito.MockitoAnnotations.Mock;
+
+/**
+ *
+ */
+public class JoinRequestProcessorTest {
+
+ private JoinRequestProcessor joinProcessor;
+ private ExecutionContext context;
+ private DateTime now;
+ private String sourceName;
+ protected List<Request> unknownRequests;
+ protected BlockingQueue<FederatedRequest> joinQueue;
+ private Projection mirrorProjection;
+ private Projection projectionA;
+ @Mock
+ private FederatedRepository repository;
+ @Mock
+ private CachePolicy cachePolicy;
+
+ @Before
+ public void beforeEach() {
+ MockitoAnnotations.initMocks(this);
+ sourceName = "MySource";
+ stub(repository.getDefaultCachePolicy()).toReturn(cachePolicy);
+ stub(repository.getSourceName()).toReturn(sourceName);
+
+ unknownRequests = new ArrayList<Request>();
+ context = new ExecutionContext();
+ now = context.getValueFactories().getDateFactory().create();
+ joinProcessor = new JoinRequestProcessorWithUnknownHandler(repository, context, now);
+ joinQueue = new LinkedBlockingQueue<FederatedRequest>();
+
+ // Set up the projections ...
+ mirrorProjection = new Projection("sourceA", "workspaceM", false, rules("/ => /"));
+ projectionA = new Projection("sourceA", "workspaceA", false, rules("/a => /"));
+ }
+
+ protected Rule[] rules( String... rule ) {
+ Rule[] rules = new Rule[rule.length];
+ for (int i = 0; i != rule.length; ++i) {
+ rules[i] = Projection.fromString(rule[i], context);
+ }
+ return rules;
+ }
+
+ public Name name( String name ) {
+ return context.getValueFactories().getNameFactory().create(name);
+ }
+
+ public Path path( String path ) {
+ return context.getValueFactories().getPathFactory().create(path);
+ }
+
+ public Segment segment( String segment ) {
+ return context.getValueFactories().getPathFactory().createSegment(segment);
+ }
+
+ public Location location( String path ) {
+ return Location.create(path(path));
+ }
+
+ public Property property( String name,
+ Object... values ) {
+ return context.getPropertyFactory().create(name(name), values);
+ }
+
+ public Location child( Location parent,
+ String childName ) {
+ Path path = context.getValueFactories().getPathFactory().create(parent.getPath(), segment(childName));
+ return Location.create(path);
+ }
+
+ public static void pause( long millis ) {
+ try {
+ Thread.sleep(100L);
+ } catch (InterruptedException e) {
+ try {
+ fail("Interrupted while sleeping");
+ } finally {
+ Thread.interrupted();
+ }
+ }
+ }
+
+ @Test
+ public void shouldProcessFederatedRequestsUsingIteratable() {
+ List<FederatedRequest> requests = new ArrayList<FederatedRequest>();
+ Request original = mock(Request.class);
+ FederatedRequest request = mock(FederatedRequest.class);
+ stub(request.original()).toReturn(original);
+ requests.add(request);
+ joinProcessor.process(requests);
+ assertThat(joinProcessor.federatedRequest, is(sameInstance(request)));
+ assertThat(unknownRequests.size(), is(1));
+ assertThat(unknownRequests.get(0), is(sameInstance(original)));
+ }
+
+ @Test
+ public void shouldProcessFederatedRequestsUsingBlockingQueue() {
+ Request original = mock(Request.class);
+ // Create the original request, the projection, and the federated request ...
+ FederatedRequest request = new FederatedRequest(original);
+ request.add(original, false, false, projectionA);
+ // Freeze the request ...
+ request.freeze();
+ // And mark it as done by decrementing the latch ...
+ request.getLatch().countDown();
+ assertThat(request.getLatch().getCount(), is(0L));
+ // Create the queue and add the request ...
+ BlockingQueue<FederatedRequest> queue = new LinkedBlockingQueue<FederatedRequest>();
+ queue.add(request);
+ // Add a terminating request ...
+ queue.add(new NoMoreFederatedRequests());
+ joinProcessor.process(queue);
+ assertThat(joinProcessor.federatedRequest, is(sameInstance(request)));
+ assertThat(unknownRequests.size(), is(1));
+ assertThat(unknownRequests.get(0), is(sameInstance(original)));
+ }
+
+ @Test
+ public void shouldProcessFederatedRequestsUsingBlockingQueueThatIsTerminatedAfterProcessingBegins() {
+ final Request original = mock(Request.class);
+ final FederatedRequest request = new FederatedRequest(original);
+ Thread thread = new Thread() {
+ @Override
+ public void run() {
+ // Create the original request, the projection, and the federated request ...
+ Projection projection = mock(Projection.class);
+ request.add(original, false, false, projection);
+ // Freeze the request ...
+ request.freeze();
+ // Add the request ...
+ joinQueue.add(request);
+ // Pause ...
+ pause(100L);
+ // And mark it as done by decrementing the latch ...
+ request.getLatch().countDown();
+ assertThat(request.getLatch().getCount(), is(0L));
+ // Pause ...
+ pause(100L);
+ // Add a terminating request ...
+ joinQueue.add(new NoMoreFederatedRequests());
+ }
+ };
+ thread.start();
+ joinProcessor.process(joinQueue);
+ assertThat(joinProcessor.federatedRequest, is(sameInstance(request)));
+ assertThat(unknownRequests.size(), is(1));
+ assertThat(unknownRequests.get(0), is(sameInstance(original)));
+ }
+
+ @Test
+ public void shouldJoinSingleMirroredReadNodeRequest() {
+ // Create the original read node request ...
+ final ReadNodeRequest original = new ReadNodeRequest(location("/a/b/c"), "workspaceM");
+ // Create a single federated request...
+ final FederatedRequest request = new FederatedRequest(original);
+ // And "fork" the original request ...
+ final ReadNodeRequest projected = new ReadNodeRequest(location("/a/b/c"), "workspaceM");
+ projected.setActualLocationOfNode(Location.create(projected.at().getPath(), UUID.randomUUID()));
+ projected.addProperty(property("propA", "valueA"));
+ projected.addProperty(property("propB", "valueB"));
+ projected.addChild(child(projected.getActualLocationOfNode(), "child1"));
+ projected.addChild(child(projected.getActualLocationOfNode(), "child2"));
+ request.add(projected, true, false, mirrorProjection);
+ request.freeze();
+ request.getLatch().countDown();
+ joinQueue.add(request);
+ // Add a terminating request and join the request...
+ joinQueue.add(new NoMoreFederatedRequests());
+ joinProcessor.process(joinQueue);
+ // Check the results of the original has the same results of the projected...
+ assertThat(original.getChildren(), is(projected.getChildren()));
+ assertThat(original.getPropertiesByName(), is(projected.getPropertiesByName()));
+ assertThat(original.getActualLocationOfNode(), is(projected.getActualLocationOfNode()));
+ }
+
+ @Test
+ public void shouldJoinSingleOffsetReadNodeRequest() {
+ // Create the original read node request ...
+ final ReadNodeRequest original = new ReadNodeRequest(location("/a/b/c"), "workspaceM");
+ // Create a single federated request using the mirror projection...
+ final FederatedRequest request = new FederatedRequest(original);
+ // And "fork" the original request by creating a mirror
+ final ReadNodeRequest projected = new ReadNodeRequest(location("/b/c"), "workspaceM");
+ projected.setActualLocationOfNode(Location.create(projected.at().getPath(), UUID.randomUUID()));
+ projected.addProperty(property("propA", "valueA"));
+ projected.addProperty(property("propB", "valueB"));
+ projected.addChild(child(projected.getActualLocationOfNode(), "child1"));
+ projected.addChild(child(projected.getActualLocationOfNode(), "child2"));
+ request.add(projected, false, false, projectionA);
+ request.freeze();
+ request.getLatch().countDown();
+ joinQueue.add(request);
+ // Add a terminating request and join the request...
+ joinQueue.add(new NoMoreFederatedRequests());
+ joinProcessor.process(joinQueue);
+ // Check the results of the original has the same results of the projected...
+ assertThat(original.getPropertiesByName(), is(projected.getPropertiesByName()));
+ assertThat(original.getActualLocationOfNode().getPath(), is(path("/a/b/c")));
+ assertThat(original.getChildren().get(0).getPath(), is(path("/a/b/c/child1")));
+ assertThat(original.getChildren().get(1).getPath(), is(path("/a/b/c/child2")));
+ }
+
+ @Test
+ public void shouldJoinMultipleReadNodeRequest() {
+ // Create the original read node request ...
+ final ReadNodeRequest original = new ReadNodeRequest(location("/a/b/c"), "workspaceM");
+ // Create a single federated request using the mirror projection...
+ final FederatedRequest request = new FederatedRequest(original);
+ // And "fork" the original request into the first source request ...
+ final ReadNodeRequest projected1 = new ReadNodeRequest(location("/b/c"), "workspaceM");
+ projected1.setActualLocationOfNode(Location.create(projected1.at().getPath(), UUID.randomUUID()));
+ projected1.addProperty(property("propA", "valueA"));
+ projected1.addProperty(property("propB", "valueB"));
+ projected1.addChild(child(projected1.getActualLocationOfNode(), "child1"));
+ projected1.addChild(child(projected1.getActualLocationOfNode(), "child2"));
+ request.add(projected1, false, false, projectionA);
+ // And a second source request ...
+ final ReadNodeRequest projected2 = new ReadNodeRequest(location("/a/b/c"), "workspaceM");
+ projected2.setActualLocationOfNode(Location.create(projected2.at().getPath(), UUID.randomUUID()));
+ projected2.addProperty(property("propC", "valueC"));
+ projected2.addProperty(property("propD", "valueD"));
+ projected2.addChild(child(projected2.getActualLocationOfNode(), "child2"));
+ projected2.addChild(child(projected2.getActualLocationOfNode(), "child3"));
+ request.add(projected2, true, false, mirrorProjection);
+ request.freeze();
+ request.getLatch().countDown();
+ request.getLatch().countDown();
+ joinQueue.add(request);
+ // Add a terminating request and join the request...
+ joinQueue.add(new NoMoreFederatedRequests());
+ joinProcessor.process(joinQueue);
+ // Check the results of the original has the same results of the projected...
+ assertThat(original.getProperties().containsAll(projected1.getProperties()), is(true));
+ assertThat(original.getProperties().containsAll(projected2.getProperties()), is(true));
+ assertThat(original.getActualLocationOfNode().getPath(), is(path("/a/b/c")));
+ assertThat(original.getActualLocationOfNode().getUuid(), is(projected1.getActualLocationOfNode().getUuid()));
+ assertThat(original.getActualLocationOfNode().getIdProperty(DnaLexicon.UUID).isMultiple(), is(true));
+ assertThat(original.getActualLocationOfNode().getIdProperty(DnaLexicon.UUID).getValuesAsArray()[0],
+ is((Object)projected1.getActualLocationOfNode().getUuid()));
+ assertThat(original.getActualLocationOfNode().getIdProperty(DnaLexicon.UUID).getValuesAsArray()[1],
+ is((Object)projected2.getActualLocationOfNode().getUuid()));
+ assertThat(original.getChildren().get(0).getPath(), is(path("/a/b/c/child1")));
+ assertThat(original.getChildren().get(1).getPath(), is(path("/a/b/c/child2")));
+ assertThat(original.getChildren().get(2).getPath(), is(path("/a/b/c/child2[2]")));
+ assertThat(original.getChildren().get(3).getPath(), is(path("/a/b/c/child3")));
+ }
+
+ @Test
+ public void shouldCancelFederatedRequestIfOneOfSeveralMultipleReadNodeRequestIsCancelled() {
+ // Create the original read node request ...
+ final ReadNodeRequest original = new ReadNodeRequest(location("/a/b/c"), "workspaceM");
+ // Create a single federated request using the mirror projection...
+ final FederatedRequest request = new FederatedRequest(original);
+ // And "fork" the original request into the first source request ...
+ final ReadNodeRequest projected1 = new ReadNodeRequest(location("/b/c"), "workspaceM");
+ projected1.setActualLocationOfNode(Location.create(projected1.at().getPath(), UUID.randomUUID()));
+ projected1.addProperty(property("propA", "valueA"));
+ projected1.addProperty(property("propB", "valueB"));
+ projected1.addChild(child(projected1.getActualLocationOfNode(), "child1"));
+ projected1.addChild(child(projected1.getActualLocationOfNode(), "child2"));
+ request.add(projected1, false, false, projectionA);
+ // And a second source request (that was cancelled)...
+ final ReadNodeRequest projected2 = new ReadNodeRequest(location("/a/b/c"), "workspaceM");
+ projected2.cancel();
+ request.add(projected2, true, false, mirrorProjection);
+ request.freeze();
+ request.getLatch().countDown();
+ request.getLatch().countDown();
+ joinQueue.add(request);
+ // Add a terminating request and join the request...
+ joinQueue.add(new NoMoreFederatedRequests());
+ joinProcessor.process(joinQueue);
+ // Check the results of the original has the same results of the projected...
+ assertThat(original.getProperties().isEmpty(), is(true));
+ assertThat(original.getChildren().isEmpty(), is(true));
+ assertThat(original.isCancelled(), is(true));
+ }
+
+ /**
+ * A specialization of {@link JoinRequestProcessor} that simply records unknown request types into
+ * {@link JoinRequestProcessorTest#unknownRequests}.
+ */
+ protected class JoinRequestProcessorWithUnknownHandler extends JoinRequestProcessor {
+ protected JoinRequestProcessorWithUnknownHandler( FederatedRepository repository,
+ ExecutionContext context,
+ DateTime now ) {
+ super(repository, context, now);
+ }
+
+ @Override
+ protected void processUnknownRequest( Request request ) {
+ unknownRequests.add(request);
+ }
+ }
+
+}
Property changes on: trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/JoinRequestProcessorTest.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Copied: trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/MirrorProjectorTest.java (from rev 910, trunk/dna-graph/src/main/java/org/jboss/dna/graph/DnaLexicon.java)
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/MirrorProjectorTest.java (rev 0)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/MirrorProjectorTest.java 2009-05-18 15:36:58 UTC (rev 911)
@@ -0,0 +1,58 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ *
+ * JBoss DNA 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.graph.connector.federation;
+
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.assertThat;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ *
+ */
+public class MirrorProjectorTest extends AbstractProjectorTest<MirrorProjector> {
+
+ private String sourceName;
+ private String workspaceName;
+
+ @Before
+ @Override
+ public void beforeEach() {
+ super.beforeEach();
+ this.sourceName = "source1";
+ this.workspaceName = "workspace1";
+ addProjection(sourceName, workspaceName, "/ => /");
+ this.projector = MirrorProjector.with(context, projections);
+ }
+
+ @Test
+ public void shouldAlwaysReturnProxyNodeForSuppliedLocation() {
+ ProjectedNode node = projector.project(context, locationA, false);
+ assertThat(node.isProxy(), is(true));
+ ProxyNode proxy = node.asProxy();
+ assertThat(proxy.location(), is(locationA));
+ assertThat(proxy.source(), is(sourceName));
+ assertThat(proxy.workspaceName(), is(workspaceName));
+ }
+}
Added: trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/OffsetMirrorProjectorTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/OffsetMirrorProjectorTest.java (rev 0)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/OffsetMirrorProjectorTest.java 2009-05-18 15:36:58 UTC (rev 911)
@@ -0,0 +1,84 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ *
+ * JBoss DNA 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.graph.connector.federation;
+
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.assertThat;
+import org.jboss.dna.graph.Location;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ *
+ */
+public class OffsetMirrorProjectorTest extends AbstractProjectorTest<OffsetMirrorProjector> {
+
+ private String mirrorSourceName;
+ private String mirrorWorkspaceName;
+
+ @Before
+ @Override
+ public void beforeEach() {
+ super.beforeEach();
+ this.mirrorSourceName = "source1";
+ this.mirrorWorkspaceName = "workspace1";
+ addProjection(mirrorSourceName, mirrorWorkspaceName, "/a/b/c => /");
+ this.projector = OffsetMirrorProjector.with(context, projections);
+ }
+
+ protected void assertProjectedIntoMirror( String federatedPath,
+ String pathInSource ) {
+ Location location = location(federatedPath);
+ ProjectedNode node = projector.project(context, location, false);
+ assertThat(node.isProxy(), is(true));
+ ProxyNode proxy = node.asProxy();
+ assertThat(proxy.location().getPath(), is(path(pathInSource)));
+ assertThat(proxy.source(), is(mirrorSourceName));
+ assertThat(proxy.workspaceName(), is(mirrorWorkspaceName));
+ assertThat(proxy.hasNext(), is(false));
+ }
+
+ @Test
+ public void shouldAlwaysReturnProxyNodeForLocationAboveMirrorSource() {
+ assertPlacholderHasChildren("/", "a");
+ assertPlacholderHasChildren("/a", "b");
+ assertPlacholderHasChildren("/a/b", "c");
+ assertProjectedIntoMirror("/a/b/c", "/");
+ }
+
+ @Test
+ public void shouldAlwaysReturnProxyNodeForLocationWithinMirror() {
+ assertProjectedIntoMirror("/a/b/c", "/");
+ assertProjectedIntoMirror("/a/b/c/d", "/d");
+ assertProjectedIntoMirror("/a/b/c/d/e", "/d/e");
+ }
+
+ @Test
+ public void shouldReturnNoProjectedNodeForLocationOffPathToSourceBranch() {
+ assertNoProjectedNodeAt("/a[2]");
+ assertNoProjectedNodeAt("/a/b[2]");
+ assertNoProjectedNodeAt("/a/b/c[2]");
+ }
+
+}
Property changes on: trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/OffsetMirrorProjectorTest.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Copied: trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/ProjectionParserTest.java (from rev 910, trunk/dna-graph/src/main/java/org/jboss/dna/graph/DnaLexicon.java)
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/ProjectionParserTest.java (rev 0)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/ProjectionParserTest.java 2009-05-18 15:36:58 UTC (rev 911)
@@ -0,0 +1,43 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ *
+ * JBoss DNA 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.graph.connector.federation;
+
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.assertThat;
+import org.junit.Test;
+
+/**
+ * @author Randall Hauch
+ */
+public class ProjectionParserTest {
+
+ private ProjectionParser parser;
+
+ @Test
+ public void shouldInitializeSingletonWithParserMethods() {
+ parser = ProjectionParser.getInstance();
+ assertThat(parser.getParserMethods().size(), is(1));
+ }
+
+}
Added: trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/ProjectionPathRuleTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/ProjectionPathRuleTest.java (rev 0)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/ProjectionPathRuleTest.java 2009-05-18 15:36:58 UTC (rev 911)
@@ -0,0 +1,250 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ *
+ * JBoss DNA 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.graph.connector.federation;
+
+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.junit.matchers.JUnitMatchers.hasItems;
+import org.jboss.dna.common.text.TextEncoder;
+import org.jboss.dna.common.text.UrlEncoder;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.property.NamespaceRegistry;
+import org.jboss.dna.graph.property.Path;
+import org.jboss.dna.graph.property.PathFactory;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * @author Randall Hauch
+ */
+public class ProjectionPathRuleTest {
+
+ private ExecutionContext context;
+ private Projection.PathRule rule;
+ private PathFactory pathFactory;
+ private Path repositoryPath;
+ private Path sourcePath;
+ private Path[] validExceptions;
+ private NamespaceRegistry registry;
+ private TextEncoder encoder;
+
+ @Before
+ public void beforeEach() {
+ context = new ExecutionContext();
+ pathFactory = context.getValueFactories().getPathFactory();
+ registry = context.getNamespaceRegistry();
+ encoder = new UrlEncoder();
+ repositoryPath = pathFactory.create("/a/b/c");
+ sourcePath = pathFactory.create("/x/y");
+ validExceptions = new Path[] {pathFactory.create("e/f"), pathFactory.create("e/g")};
+ rule = new Projection.PathRule(repositoryPath, sourcePath, validExceptions);
+ }
+
+ @Test
+ public void shouldCreateInstanceWithValidRepositoryPathAndValidSourcePathAndNoExceptions() {
+ rule = new Projection.PathRule(repositoryPath, sourcePath);
+ assertThat(rule.getPathInRepository(), is(sameInstance(repositoryPath)));
+ assertThat(rule.getPathInSource(), is(sameInstance(sourcePath)));
+ assertThat(rule.hasExceptionsToRule(), is(false));
+ }
+
+ @Test
+ public void shouldCreateInstanceWithValidRepositoryPathAndValidSourcePathAndValidExceptions() {
+ rule = new Projection.PathRule(repositoryPath, sourcePath, validExceptions);
+ assertThat(rule.getPathInRepository(), is(sameInstance(repositoryPath)));
+ assertThat(rule.getPathInSource(), is(sameInstance(sourcePath)));
+ assertThat(rule.hasExceptionsToRule(), is(true));
+ assertThat(rule.getExceptionsToRule(), hasItems(validExceptions));
+ }
+
+ @Test( expected = AssertionError.class )
+ public void shouldFailToCreateInstanceWithNullRepositoryPathAndValidSourcePathAndNoExceptions() {
+ repositoryPath = null;
+ new Projection.PathRule(repositoryPath, sourcePath);
+ }
+
+ @Test( expected = AssertionError.class )
+ public void shouldFailToCreateInstanceWithValidRepositoryPathAndNullSourcePathAndNoExceptions() {
+ sourcePath = null;
+ new Projection.PathRule(repositoryPath, sourcePath);
+ }
+
+ @Test( expected = AssertionError.class )
+ public void shouldFailToCreateInstanceWithValidRepositoryPathAndValidSourcePathAndAbsoluteExceptions() {
+ Path relativePath = validExceptions[0];
+ Path absolutePath = pathFactory.create("/j/k/l/m");
+ new Projection.PathRule(repositoryPath, sourcePath, relativePath, absolutePath);
+ }
+
+ @Test
+ public void shouldIncludeRepositoryPathsAtPathInRepository() {
+ assertThat(rule.includes(sourcePath), is(true));
+ }
+
+ @Test
+ public void shouldIncludeRepositoryPathsBelowPathInRepositoryThatAreNotExcluded() {
+ assertThat(rule.includes(pathFactory.create(sourcePath, "m")), is(true));
+ assertThat(rule.includes(pathFactory.create(sourcePath, "m/n")), is(true));
+ assertThat(rule.includes(pathFactory.create(sourcePath, "o/p")), is(true));
+ assertThat(rule.includes(pathFactory.create(sourcePath, "e/e")), is(true));
+ assertThat(rule.includes(pathFactory.create(sourcePath, "e")), is(true));
+ }
+
+ @Test
+ public void shouldNotIncludeRepositoryPathsBelowPathInRepositoryThatAreExcluded() {
+ assertThat(rule.includes(pathFactory.create(sourcePath, "e/f")), is(false));
+ assertThat(rule.includes(pathFactory.create(sourcePath, "e/g")), is(false));
+ assertThat(rule.includes(pathFactory.create(sourcePath, "e/f/g")), is(false));
+ assertThat(rule.includes(pathFactory.create(sourcePath, "e/g/h")), is(false));
+ }
+
+ @Test
+ public void shouldNotIncludeRepositoryPathsNotBelowPathInRepository() {
+ assertThat(rule.includes(pathFactory.create("/m/n")), is(false));
+ assertThat(rule.includes(pathFactory.create("/x/y[3]")), is(false));
+ }
+
+ @Test
+ public void shouldProjectRepositoryPathIntoSourcePath() {
+ assertThat(rule.projectPathInRepositoryToPathInSource(repositoryPath, pathFactory), is(sourcePath));
+ }
+
+ @Test
+ public void shouldProjectPathBelowRepositoryPathIntoPathBelowSourcePath() {
+ Path pathInRepository = pathFactory.create(repositoryPath, "m/n");
+ Path pathInSource = pathFactory.create(sourcePath, "m/n");
+ assertThat(rule.projectPathInRepositoryToPathInSource(pathInRepository, pathFactory), is(pathInSource));
+
+ pathInRepository = pathFactory.create(repositoryPath, "m");
+ pathInSource = pathFactory.create(sourcePath, "m");
+ assertThat(rule.projectPathInRepositoryToPathInSource(pathInRepository, pathFactory), is(pathInSource));
+
+ pathInRepository = pathFactory.create(repositoryPath, "m/n[3]");
+ pathInSource = pathFactory.create(sourcePath, "m/n[3]");
+ assertThat(rule.projectPathInRepositoryToPathInSource(pathInRepository, pathFactory), is(pathInSource));
+ }
+
+ @Test
+ public void shouldProjectSourcePathIntoRepositoryPath() {
+ assertThat(rule.projectPathInSourceToPathInRepository(sourcePath, pathFactory), is(repositoryPath));
+ }
+
+ @Test
+ public void shouldProjectPathBelowSourcePathIntoPathBelowRepositoryPath() {
+ Path pathInRepository = pathFactory.create(repositoryPath, "m/n");
+ Path pathInSource = pathFactory.create(sourcePath, "m/n");
+ assertThat(rule.projectPathInSourceToPathInRepository(pathInSource, pathFactory), is(pathInRepository));
+
+ pathInRepository = pathFactory.create(repositoryPath, "m");
+ pathInSource = pathFactory.create(sourcePath, "m");
+ assertThat(rule.projectPathInSourceToPathInRepository(pathInSource, pathFactory), is(pathInRepository));
+
+ pathInRepository = pathFactory.create(repositoryPath, "m/n[3]");
+ pathInSource = pathFactory.create(sourcePath, "m/n[3]");
+ assertThat(rule.projectPathInSourceToPathInRepository(pathInSource, pathFactory), is(pathInRepository));
+ }
+
+ @Test
+ public void shouldGetPathsInRepositoryGivenPathsInSourceAtOrBelowSourcePathIfNotExcluded() {
+ assertThat(rule.getPathInRepository(sourcePath, pathFactory), is(repositoryPath));
+ assertThatGetPathInRepositoryReturnsCorrectPathInSource("");
+ assertThatGetPathInRepositoryReturnsCorrectPathInSource("m/n");
+ assertThatGetPathInRepositoryReturnsCorrectPathInSource("m[1]");
+ assertThatGetPathInRepositoryReturnsCorrectPathInSource("m[1]/n/o/p");
+ }
+
+ protected void assertThatGetPathInRepositoryReturnsCorrectPathInSource( String subpath ) {
+ assertThat(rule.getPathInRepository(pathFactory.create(sourcePath, subpath), pathFactory),
+ is(pathFactory.create(repositoryPath, subpath)));
+ }
+
+ @Test
+ public void shouldGetNullPathInRepositoryGivenPathsInSourceAtOrBelowSourcePathIfExcluded() {
+ assertThat(rule.getPathInRepository(pathFactory.create(sourcePath, "e/f"), pathFactory), is(nullValue()));
+ assertThat(rule.getPathInRepository(pathFactory.create(sourcePath, "e/g"), pathFactory), is(nullValue()));
+ assertThat(rule.getPathInRepository(pathFactory.create(sourcePath, "e/f/h"), pathFactory), is(nullValue()));
+ assertThat(rule.getPathInRepository(pathFactory.create(sourcePath, "e/g/h"), pathFactory), is(nullValue()));
+ }
+
+ @Test
+ public void shouldGetNullPathInRepositoryGivenPathsInRepositoryNotAtOrBelowSourcePath() {
+ assertThat(rule.getPathInRepository(pathFactory.create("/m/n"), pathFactory), is(nullValue()));
+ }
+
+ @Test
+ public void shouldGetPathsInSourceGivenPathsInRepositoryAtOrBelowRepositoryPathIfNotExcluded() {
+ assertThat(rule.getPathInSource(repositoryPath, pathFactory), is(sourcePath));
+ assertThatGetPathInSourceReturnsCorrectPathInRepository("");
+ assertThatGetPathInSourceReturnsCorrectPathInRepository("m/n");
+ assertThatGetPathInSourceReturnsCorrectPathInRepository("m[1]");
+ assertThatGetPathInSourceReturnsCorrectPathInRepository("m[1]/n/o/p");
+
+ }
+
+ protected void assertThatGetPathInSourceReturnsCorrectPathInRepository( String subpath ) {
+ assertThat(rule.getPathInSource(pathFactory.create(repositoryPath, subpath), pathFactory),
+ is(pathFactory.create(sourcePath, subpath)));
+ }
+
+ @Test
+ public void shouldGetNullPathInSourceGivenPathsInRepositoryAtOrBelowRepositoryPathIfExcluded() {
+ assertThat(rule.getPathInSource(pathFactory.create(repositoryPath, "e/f"), pathFactory), is(nullValue()));
+ assertThat(rule.getPathInSource(pathFactory.create(repositoryPath, "e/g"), pathFactory), is(nullValue()));
+ assertThat(rule.getPathInSource(pathFactory.create(repositoryPath, "e/f/h"), pathFactory), is(nullValue()));
+ assertThat(rule.getPathInSource(pathFactory.create(repositoryPath, "e/g/h"), pathFactory), is(nullValue()));
+ }
+
+ @Test
+ public void shouldGetNullPathInSourceGivenPathsInRepositoryNotAtOrBelowRepositoryPath() {
+ assertThat(rule.getPathInSource(pathFactory.create("/m/n"), pathFactory), is(nullValue()));
+ }
+
+ @Test
+ public void shouldConvertToString() {
+ assertThat(rule.getString(registry, encoder), is("/a/b/c => /x/y $ e/f $ e/g"));
+
+ repositoryPath = pathFactory.create("/a/b/c");
+ sourcePath = pathFactory.create("/");
+ rule = new Projection.PathRule(repositoryPath, sourcePath, validExceptions);
+ assertThat(rule.getString(registry, encoder), is("/a/b/c => / $ e/f $ e/g"));
+
+ repositoryPath = pathFactory.create("/");
+ sourcePath = pathFactory.create("/");
+ rule = new Projection.PathRule(repositoryPath, sourcePath, validExceptions);
+ assertThat(rule.getString(registry, encoder), is("/ => / $ e/f $ e/g"));
+ }
+
+ @Test
+ public void shouldHaveToString() {
+ assertThat(rule.toString(), is("/{}a/{}b/{}c => /{}x/{}y $ {}e/{}f $ {}e/{}g"));
+
+ repositoryPath = pathFactory.create("/a/b/c");
+ sourcePath = pathFactory.create("/");
+ rule = new Projection.PathRule(repositoryPath, sourcePath, validExceptions);
+ assertThat(rule.toString(), is("/{}a/{}b/{}c => / $ {}e/{}f $ {}e/{}g"));
+ }
+
+}
Property changes on: trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/ProjectionPathRuleTest.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/ProjectionTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/ProjectionTest.java (rev 0)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/ProjectionTest.java 2009-05-18 15:36:58 UTC (rev 911)
@@ -0,0 +1,290 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ *
+ * JBoss DNA 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.graph.connector.federation;
+
+import static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsInstanceOf.instanceOf;
+import static org.hamcrest.core.IsNull.nullValue;
+import static org.junit.Assert.assertThat;
+import static org.junit.matchers.JUnitMatchers.hasItems;
+import static org.mockito.Mockito.stub;
+import java.util.Set;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.property.Path;
+import org.jboss.dna.graph.property.PathFactory;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.MockitoAnnotations;
+import org.mockito.MockitoAnnotations.Mock;
+
+/**
+ * @author Randall Hauch
+ */
+public class ProjectionTest {
+
+ private ExecutionContext context;
+ private String sourceName;
+ private String workspaceName;
+ private Projection.Rule[] rules;
+ private Projection projection;
+ private PathFactory pathFactory;
+ private boolean readOnly;
+ @Mock
+ private Projection.Rule mockRule1;
+ @Mock
+ private Projection.Rule mockRule2;
+ @Mock
+ private Projection.Rule mockRule3;
+
+ @Before
+ public void beforeEach() {
+ MockitoAnnotations.initMocks(this);
+ context = new ExecutionContext();
+ pathFactory = context.getValueFactories().getPathFactory();
+ sourceName = "Valid name";
+ workspaceName = "Valid workspace";
+ readOnly = false;
+ rules = new Projection.Rule[] {mockRule1, mockRule2, mockRule3};
+ projection = new Projection(sourceName, workspaceName, readOnly, rules);
+ }
+
+ @Test
+ public void shouldCreateInstanceWithValidNameAndValidRules() {
+ projection = new Projection(sourceName, workspaceName, readOnly, rules);
+ assertThat(projection.getSourceName(), is(sourceName));
+ assertThat(projection.getWorkspaceName(), is(workspaceName));
+ assertThat(projection.getRules().size(), is(rules.length));
+ assertThat(projection.getRules(), hasItems(mockRule1, mockRule2, mockRule3));
+ }
+
+ @Test( expected = IllegalArgumentException.class )
+ public void shouldFailToCreateInstanceWithNullNameAndValidRules() {
+ sourceName = null;
+ projection = new Projection(sourceName, workspaceName, readOnly, rules);
+ }
+
+ @Test( expected = IllegalArgumentException.class )
+ public void shouldFailToCreateInstanceWithNullWorkspaceNameAndValidSourceNameAndRules() {
+ sourceName = null;
+ projection = new Projection(sourceName, workspaceName, readOnly, rules);
+ }
+
+ @Test( expected = IllegalArgumentException.class )
+ public void shouldFailToCreateInstanceWithEmptyNameAndValidRules() {
+ sourceName = "";
+ projection = new Projection(sourceName, workspaceName, readOnly, rules);
+ }
+
+ @Test( expected = IllegalArgumentException.class )
+ public void shouldFailToCreateInstanceWithBlankNameAndValidRules() {
+ sourceName = " \t ";
+ projection = new Projection(sourceName, workspaceName, readOnly, rules);
+ }
+
+ @Test( expected = IllegalArgumentException.class )
+ public void shouldFailToCreateInstanceWithValidNameAndNullRules() {
+ rules = null;
+ projection = new Projection(sourceName, workspaceName, readOnly, rules);
+ }
+
+ @Test( expected = IllegalArgumentException.class )
+ public void shouldFailToCreateInstanceWithValidNameAndEmptyRules() {
+ rules = new Projection.Rule[] {};
+ projection = new Projection(sourceName, workspaceName, readOnly, rules);
+ }
+
+ @Test( expected = IllegalArgumentException.class )
+ public void shouldFailToCreateInstanceWithValidNameAndRulesArrayContainingAllNulls() {
+ projection = new Projection(sourceName, null, readOnly, null, null);
+ }
+
+ @Test
+ public void shouldCreateInstanceWithValidNameAndRulesAndShouldPruneNullRuleReferences() {
+ projection = new Projection(sourceName, workspaceName, readOnly, mockRule1, null, mockRule3);
+ assertThat(projection.getRules().size(), is(2));
+ assertThat(projection.getRules(), hasItems(mockRule1, mockRule3));
+ }
+
+ @Test( expected = IllegalArgumentException.class )
+ public void shouldFailToGetPathsInSourceGivenValidPathAndNullPathFactory() {
+ Path pathInRepository = pathFactory.create("/a/b/c");
+ projection.getPathsInSource(pathInRepository, null);
+ }
+
+ @Test
+ public void shouldGetNoPathsInSourceGivenNullPathInRepository() {
+ Set<Path> pathsInSource = projection.getPathsInSource(null, pathFactory);
+ assertThat(pathsInSource.isEmpty(), is(true));
+ }
+
+ @Test
+ public void shouldGetNoPathsInSourceGivenPathInRepositoryAndNoApplicableRules() {
+ Path pathInRepository = pathFactory.create("/a/b/c");
+ stub(mockRule1.getPathInSource(pathInRepository, pathFactory)).toReturn(null);
+ stub(mockRule2.getPathInSource(pathInRepository, pathFactory)).toReturn(null);
+ stub(mockRule3.getPathInSource(pathInRepository, pathFactory)).toReturn(null);
+ Set<Path> pathsInSource = projection.getPathsInSource(pathInRepository, pathFactory);
+ assertThat(pathsInSource.isEmpty(), is(true));
+ }
+
+ @Test
+ public void shouldGetPathInSourceGivenPathInRepositoryAndOneApplicableRules() {
+ Path pathInRepository = pathFactory.create("/a/b/c");
+ Path pathInSource = pathFactory.create("/d/e/f");
+ stub(mockRule1.getPathInSource(pathInRepository, pathFactory)).toReturn(pathInSource);
+ stub(mockRule2.getPathInSource(pathInRepository, pathFactory)).toReturn(null);
+ stub(mockRule3.getPathInSource(pathInRepository, pathFactory)).toReturn(null);
+ Set<Path> pathsInSource = projection.getPathsInSource(pathInRepository, pathFactory);
+ assertThat(pathsInSource, hasItems(pathInSource));
+ }
+
+ @Test
+ public void shouldGetPathsInSourceGivenPathInRepositoryAndMultipleApplicableRules() {
+ Path pathInRepository = pathFactory.create("/a/b/c");
+ Path pathInSource1 = pathFactory.create("/d/e/f");
+ Path pathInSource2 = pathFactory.create("/d/e/g");
+ Path pathInSource3 = pathFactory.create("/d/e/h");
+ stub(mockRule1.getPathInSource(pathInRepository, pathFactory)).toReturn(pathInSource1);
+ stub(mockRule2.getPathInSource(pathInRepository, pathFactory)).toReturn(pathInSource2);
+ stub(mockRule3.getPathInSource(pathInRepository, pathFactory)).toReturn(pathInSource3);
+ Set<Path> pathsInSource = projection.getPathsInSource(pathInRepository, pathFactory);
+ assertThat(pathsInSource, hasItems(pathInSource1, pathInSource2, pathInSource3));
+ }
+
+ @Test
+ public void shouldGetPathsInSourceGivenPathInRepositoryAndMultipleApplicableRulesReturningDuplicatePathsInSource() {
+ Path pathInRepository = pathFactory.create("/a/b/c");
+ Path pathInSource1 = pathFactory.create("/d/e/f");
+ Path pathInSource23 = pathFactory.create("/d/e/g");
+ stub(mockRule1.getPathInSource(pathInRepository, pathFactory)).toReturn(pathInSource1);
+ stub(mockRule2.getPathInSource(pathInRepository, pathFactory)).toReturn(pathInSource23);
+ stub(mockRule3.getPathInSource(pathInRepository, pathFactory)).toReturn(pathInSource23);
+ Set<Path> pathsInSource = projection.getPathsInSource(pathInRepository, pathFactory);
+ assertThat(pathsInSource, hasItems(pathInSource1, pathInSource23));
+ }
+
+ @Test( expected = IllegalArgumentException.class )
+ public void shouldFailToGetPathsInRepositoryGivenValidPathAndNullPathFactory() {
+ Path pathInSource = pathFactory.create("/a/b/c");
+ projection.getPathsInRepository(pathInSource, null);
+ }
+
+ @Test
+ public void shouldGetNoPathsInRepositoryGivenNullPathInSource() {
+ Set<Path> pathsInRepository = projection.getPathsInRepository(null, pathFactory);
+ assertThat(pathsInRepository.isEmpty(), is(true));
+ }
+
+ @Test
+ public void shouldGetNoPathsInRepositoryGivenPathInSourceAndNoApplicableRules() {
+ Path pathInSource = pathFactory.create("/d/e/f");
+ stub(mockRule1.getPathInRepository(pathInSource, pathFactory)).toReturn(null);
+ stub(mockRule2.getPathInRepository(pathInSource, pathFactory)).toReturn(null);
+ stub(mockRule3.getPathInRepository(pathInSource, pathFactory)).toReturn(null);
+ Set<Path> pathsInRepository = projection.getPathsInRepository(pathInSource, pathFactory);
+ assertThat(pathsInRepository.isEmpty(), is(true));
+ }
+
+ @Test
+ public void shouldGetPathInRepositoryGivenPathInSourceAndOneApplicableRules() {
+ Path pathInRepository = pathFactory.create("/a/b/c");
+ Path pathInSource = pathFactory.create("/d/e/f");
+ stub(mockRule1.getPathInRepository(pathInSource, pathFactory)).toReturn(pathInRepository);
+ stub(mockRule2.getPathInRepository(pathInSource, pathFactory)).toReturn(null);
+ stub(mockRule3.getPathInRepository(pathInSource, pathFactory)).toReturn(null);
+ Set<Path> pathsInRepository = projection.getPathsInRepository(pathInSource, pathFactory);
+ assertThat(pathsInRepository, hasItems(pathInRepository));
+ }
+
+ @Test
+ public void shouldGetPathsInRepositoryGivenPathInSourceAndMultipleApplicableRules() {
+ Path pathInSource = pathFactory.create("/a/b/c");
+ Path pathInRepository1 = pathFactory.create("/d/e/f");
+ Path pathInRepository2 = pathFactory.create("/d/e/g");
+ Path pathInRepository3 = pathFactory.create("/d/e/h");
+ stub(mockRule1.getPathInRepository(pathInSource, pathFactory)).toReturn(pathInRepository1);
+ stub(mockRule2.getPathInRepository(pathInSource, pathFactory)).toReturn(pathInRepository2);
+ stub(mockRule3.getPathInRepository(pathInSource, pathFactory)).toReturn(pathInRepository3);
+ Set<Path> pathsInRepository = projection.getPathsInRepository(pathInSource, pathFactory);
+ assertThat(pathsInRepository, hasItems(pathInRepository1, pathInRepository2, pathInRepository3));
+ }
+
+ @Test
+ public void shouldGetPathsInRepositoryGivenPathInSourceAndMultipleApplicableRulesReturningDuplicatePathsInRepository() {
+ Path pathInSource = pathFactory.create("/a/b/c");
+ Path pathInRepository1 = pathFactory.create("/d/e/f");
+ Path pathInRepository23 = pathFactory.create("/d/e/g");
+ stub(mockRule1.getPathInRepository(pathInSource, pathFactory)).toReturn(pathInRepository1);
+ stub(mockRule2.getPathInRepository(pathInSource, pathFactory)).toReturn(pathInRepository23);
+ stub(mockRule3.getPathInRepository(pathInSource, pathFactory)).toReturn(pathInRepository23);
+ Set<Path> pathsInRepository = projection.getPathsInRepository(pathInSource, pathFactory);
+ assertThat(pathsInRepository, hasItems(pathInRepository1, pathInRepository23));
+ }
+
+ @Test
+ public void shouldParsePathRuleFromDefinitionWithNonRootRepositoryPathAndNonRootSourcePath() {
+ Projection.Rule rule = Projection.parsePathRule("/a => /b", context);
+ assertThat(rule, is(instanceOf(Projection.PathRule.class)));
+ Projection.PathRule pathRule = (Projection.PathRule)rule;
+ assertThat(pathRule.getPathInRepository(), is(pathFactory.create("/a")));
+ assertThat(pathRule.getPathInSource(), is(pathFactory.create("/b")));
+ }
+
+ @Test
+ public void shouldParsePathRuleFromDefinitionWithRootRepositoryPathAndNonRootSourcePath() {
+ Projection.Rule rule = Projection.parsePathRule("/ => /b", context);
+ assertThat(rule, is(instanceOf(Projection.PathRule.class)));
+ Projection.PathRule pathRule = (Projection.PathRule)rule;
+ assertThat(pathRule.getPathInRepository(), is(pathFactory.createRootPath()));
+ assertThat(pathRule.getPathInSource(), is(pathFactory.create("/b")));
+ }
+
+ @Test
+ public void shouldParsePathRuleFromDefinitionWithNonRootRepositoryPathAndRootSourcePath() {
+ Projection.Rule rule = Projection.parsePathRule("/a => /", context);
+ assertThat(rule, is(instanceOf(Projection.PathRule.class)));
+ Projection.PathRule pathRule = (Projection.PathRule)rule;
+ assertThat(pathRule.getPathInRepository(), is(pathFactory.create("/a")));
+ assertThat(pathRule.getPathInSource(), is(pathFactory.createRootPath()));
+ }
+
+ @Test
+ public void shouldParsePathRuleFromDefinitionWithRootRepositoryPathAndRootSourcePath() {
+ Projection.Rule rule = Projection.parsePathRule("/ => /", context);
+ assertThat(rule, is(instanceOf(Projection.PathRule.class)));
+ Projection.PathRule pathRule = (Projection.PathRule)rule;
+ assertThat(pathRule.getPathInRepository(), is(pathFactory.createRootPath()));
+ assertThat(pathRule.getPathInSource(), is(pathFactory.createRootPath()));
+ }
+
+ @Test
+ public void shouldNotParsePathRuleFromDefinitionWithRootRepositoryPathAndNoSourcePath() {
+ assertThat(Projection.parsePathRule("/", context), is(nullValue()));
+ }
+
+ @Test
+ public void shouldNotParsePathRuleFromDefinitionWithNonRootRepositoryPathAndNoSourcePath() {
+ assertThat(Projection.parsePathRule("a/", context), is(nullValue()));
+ }
+}
Property changes on: trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/ProjectionTest.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/ProjectorWithPlaceholdersTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/ProjectorWithPlaceholdersTest.java (rev 0)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/ProjectorWithPlaceholdersTest.java 2009-05-18 15:36:58 UTC (rev 911)
@@ -0,0 +1,182 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ *
+ * JBoss DNA 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.graph.connector.federation;
+
+import static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsNull.notNullValue;
+import static org.hamcrest.core.IsNull.nullValue;
+import static org.junit.Assert.assertThat;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.Location;
+import org.jboss.dna.graph.connector.federation.Projection.Rule;
+import org.jboss.dna.graph.property.Path;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ *
+ */
+public class ProjectorWithPlaceholdersTest {
+
+ protected ExecutionContext context;
+ protected List<Projection> projections;
+ protected Collection<PlaceholderNode> placeholders;
+
+ @Before
+ public void beforeEach() {
+ this.context = new ExecutionContext();
+ this.projections = new ArrayList<Projection>();
+ this.placeholders = new ArrayList<PlaceholderNode>();
+ }
+
+ protected Rule[] rule( String... rule ) {
+ Rule[] rules = new Rule[rule.length];
+ for (int i = 0; i != rule.length; ++i) {
+ rules[i] = Projection.fromString(rule[i], context);
+ }
+ return rules;
+ }
+
+ protected Path path( String path ) {
+ return context.getValueFactories().getPathFactory().create(path);
+ }
+
+ protected Path path( Path parent,
+ String relativePath ) {
+ return context.getValueFactories().getPathFactory().create(parent, relativePath);
+ }
+
+ protected void addProjection( String sourceName,
+ String workspaceName,
+ String... rules ) {
+ projections.add(new Projection(sourceName, workspaceName, false, rule(rules)));
+ }
+
+ protected PlaceholderNode getPlaceholder( Path path ) {
+ for (PlaceholderNode placeholder : placeholders) {
+ if (placeholder.location().getPath().equals(path)) return placeholder;
+ }
+ return null;
+ }
+
+ protected void assertPlacholderHasChildren( String parent,
+ String... childSegments ) {
+ Path parentPath = path(parent);
+ ProjectedNode node = getPlaceholder(parentPath);
+ assertThat(node, is(notNullValue()));
+ assertThat(node.isPlaceholder(), is(true));
+ PlaceholderNode placeholder = node.asPlaceholder();
+ List<Location> locations = new ArrayList<Location>();
+ for (String childSegment : childSegments) {
+ Path childPath = path(parentPath, childSegment);
+ locations.add(Location.create(childPath));
+ }
+ assertThat(placeholder.children(), is(locations));
+ }
+
+ protected void assertNoPlacholder( String parent ) {
+ assertThat(getPlaceholder(path(parent)), is(nullValue()));
+ }
+
+ @Test
+ public void shouldLoadPlaceholderNodesForWorkspaceWithOneProjectionNotAtRoot() {
+ addProjection("source1", "workspace1", "/a/b => /c/d");
+ ProjectorWithPlaceholders.loadPlaceholderNodes(context, projections, placeholders);
+ assertPlacholderHasChildren("/", "a");
+ assertPlacholderHasChildren("/a", "b");
+ assertThat(placeholders.size(), is(2));
+ }
+
+ @Test
+ public void shouldLoadPlaceholderNodesForWorkspaceWithOneProjectionAtRoot() {
+ addProjection("source1", "workspace1", "/ => /");
+ ProjectorWithPlaceholders.loadPlaceholderNodes(context, projections, placeholders);
+ assertNoPlacholder("/");
+ assertThat(placeholders.size(), is(0));
+ }
+
+ @Test
+ public void shouldLoadPlaceholderNodesForWorkspaceWithMultipleProjectionsNotAtRoot() {
+ addProjection("source1", "workspace1", "/a/b => /a1/b1");
+ addProjection("source2", "workspace2", "/c/d => /c1/d1");
+ ProjectorWithPlaceholders.loadPlaceholderNodes(context, projections, placeholders);
+ assertPlacholderHasChildren("/", "a", "c");
+ assertPlacholderHasChildren("/a", "b");
+ assertPlacholderHasChildren("/c", "d");
+ assertThat(placeholders.size(), is(3));
+ }
+
+ @Test
+ public void shouldLoadPlaceholderNodesForWorkspaceWithMultipleProjectionsAtRoot() {
+ addProjection("source1", "workspace1", "/ => /");
+ addProjection("source2", "workspace2", "/ => /");
+ ProjectorWithPlaceholders.loadPlaceholderNodes(context, projections, placeholders);
+ assertNoPlacholder("/");
+ assertThat(placeholders.size(), is(0));
+ }
+
+ @Test
+ public void shouldLoadPlaceholderNodesForWorkspaceWithMirrorAndBranchProjections() {
+ addProjection("source1", "workspace1", "/ => /");
+ addProjection("source2", "workspace2", "/a => /a");
+ ProjectorWithPlaceholders.loadPlaceholderNodes(context, projections, placeholders);
+ assertPlacholderHasChildren("/", "a"); // placeholder for root contains only branch root
+ assertThat(placeholders.size(), is(1));
+ assertNoPlacholder("/a");
+ assertNoPlacholder("/b");
+ assertNoPlacholder("/c/d");
+ assertNoPlacholder("/a/e");
+ }
+
+ @Test
+ public void shouldLoadPlaceholderNodesForWorkspaceWithMirrorAndOffsetBranchProjections() {
+ addProjection("source1", "workspace1", "/ => /");
+ addProjection("source2", "workspace2", "/a/b/c => /a/b");
+ ProjectorWithPlaceholders.loadPlaceholderNodes(context, projections, placeholders);
+ assertPlacholderHasChildren("/", "a"); // placeholder for root contains only branch root
+ assertPlacholderHasChildren("/a", "b");
+ assertPlacholderHasChildren("/a/b", "c");
+ assertThat(placeholders.size(), is(3));
+ assertNoPlacholder("/x");
+ assertNoPlacholder("/y/z");
+ assertNoPlacholder("/a/b/c");
+ }
+
+ @Test
+ public void shouldLoadPlaceholderNodesForWorkspaceWithOffsetMirrorProjection() {
+ addProjection("source1", "workspace1", "/a/b/c => /");
+ ProjectorWithPlaceholders.loadPlaceholderNodes(context, projections, placeholders);
+ assertPlacholderHasChildren("/", "a"); // placeholder for root contains only branch root
+ assertPlacholderHasChildren("/a", "b");
+ assertPlacholderHasChildren("/a/b", "c");
+ assertThat(placeholders.size(), is(3));
+ assertNoPlacholder("/a[2]");
+ assertNoPlacholder("/a/b[2]");
+ assertNoPlacholder("/a/b/c[2]");
+ }
+
+}
Property changes on: trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/ProjectorWithPlaceholdersTest.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/inmemory/InMemoryConnectorReadableTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/inmemory/InMemoryConnectorReadableTest.java 2009-05-15 19:28:48 UTC (rev 910)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/inmemory/InMemoryConnectorReadableTest.java 2009-05-18 15:36:58 UTC (rev 911)
@@ -79,7 +79,7 @@
Location root = graph.getCurrentWorkspace().getRoot();
Subgraph subgraph1 = graph.getSubgraphOfDepth(10).at(root);
Subgraph subgraph2 = graph.getSubgraphOfDepth(10).at(root);
- assertEquivalentSubgraphs(subgraph1, subgraph2, true);
+ assertEquivalentSubgraphs(subgraph1, subgraph2, true, true);
}
}
Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/test/AbstractConnectorTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/test/AbstractConnectorTest.java 2009-05-15 19:28:48 UTC (rev 910)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/test/AbstractConnectorTest.java 2009-05-18 15:36:58 UTC (rev 911)
@@ -613,16 +613,22 @@
* @param subgraph2 the second subgraph; may not be null
* @param idPropertiesShouldMatch true if the identification properties of each corresponding node should match, or false if
* the identification properties should be ignored
+ * @param pathsShouldMatch true if the absolute paths of the subgraphs should match, or false if only the relative paths
+ * within the subgraphs should match
*/
- public void assertEquivalentSubgraphs( Subgraph subgraph1,
- Subgraph subgraph2,
- boolean idPropertiesShouldMatch ) {
+ public static void assertEquivalentSubgraphs( Subgraph subgraph1,
+ Subgraph subgraph2,
+ boolean idPropertiesShouldMatch,
+ boolean pathsShouldMatch ) {
assertThat(subgraph1, is(notNullValue()));
assertThat(subgraph2, is(notNullValue()));
// Shortcut ...
if (subgraph1.getLocation().equals(subgraph2.getLocation())) return;
+ Path rootPath1 = subgraph1.getRoot().getLocation().getPath();
+ Path rootPath2 = subgraph2.getRoot().getLocation().getPath();
+
// Iterate over each subgraph. Note that because each location should have a path, the path can be used
// to ensure the structure matches.
Iterator<SubgraphNode> iter1 = subgraph1.iterator();
@@ -631,10 +637,19 @@
Node node1 = iter1.next();
Node node2 = iter2.next();
+ assertThat(node1, is(notNullValue()));
+ assertThat(node2, is(notNullValue()));
+
// Each node should have equivalent paths ..
assertThat(node1.getLocation().hasPath(), is(true));
assertThat(node2.getLocation().hasPath(), is(true));
- assertThat(node1.getLocation().getPath(), is(node2.getLocation().getPath()));
+ if (pathsShouldMatch) {
+ assertThat(node1.getLocation().getPath(), is(node2.getLocation().getPath()));
+ } else {
+ Path relativeNode1 = node1.getLocation().getPath().relativeTo(rootPath1);
+ Path relativeNode2 = node2.getLocation().getPath().relativeTo(rootPath2);
+ assertThat(relativeNode1, is(relativeNode2));
+ }
// Each node should have the same Identification properties ...
if (idPropertiesShouldMatch) {
@@ -654,7 +669,7 @@
properties2.remove(idProperty.getName());
}
}
- assertThat(properties1.values(), is(properties2.values()));
+ assertThat(properties1, is(properties2));
// Each node should have the same children. We can check this, tho this will be enforced when comparing paths ...
assertThat(node1.getChildrenSegments(), is(node2.getChildrenSegments()));
@@ -672,8 +687,8 @@
* @param node1 the first node; may not be null
* @param node2 the second node; may not be null
*/
- public void assertSameNode( Node node1,
- Node node2 ) {
+ public static void assertSameNode( Node node1,
+ Node node2 ) {
assertThat(node1, is(notNullValue()));
assertThat(node2, is(notNullValue()));
@@ -701,8 +716,8 @@
* @param node the node; may not be null
* @param properties the expected properties
*/
- public void assertSameProperties( Node node,
- Map<Name, Property> properties ) {
+ public static void assertSameProperties( Node node,
+ Map<Name, Property> properties ) {
assertThat(node, is(notNullValue()));
assertThat(properties, is(notNullValue()));
Set<Name> names = new HashSet<Name>(properties.keySet());
@@ -720,8 +735,8 @@
* @param node the node; may not be null
* @param properties the expected properties
*/
- public void assertSameProperties( Node node,
- Iterable<Property> properties ) {
+ public static void assertSameProperties( Node node,
+ Iterable<Property> properties ) {
assertThat(node, is(notNullValue()));
assertThat(properties, is(notNullValue()));
Set<Name> names = new HashSet<Name>(node.getPropertiesByName().keySet());
Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/test/ReadableConnectorTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/test/ReadableConnectorTest.java 2009-05-15 19:28:48 UTC (rev 910)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/test/ReadableConnectorTest.java 2009-05-18 15:36:58 UTC (rev 911)
@@ -305,7 +305,7 @@
Subgraph subgraph1 = graph.getSubgraphOfDepth(10).at(root);
for (int i = 0; i != 4; ++i) {
Subgraph subgraph2 = graph.getSubgraphOfDepth(10).at(root);
- assertEquivalentSubgraphs(subgraph1, subgraph2, true);
+ assertEquivalentSubgraphs(subgraph1, subgraph2, true, true);
}
}
}
Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/test/WorkspaceConnectorTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/test/WorkspaceConnectorTest.java 2009-05-15 19:28:48 UTC (rev 910)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/test/WorkspaceConnectorTest.java 2009-05-18 15:36:58 UTC (rev 911)
@@ -283,7 +283,7 @@
// Verify that the content of 'workspace1' matches that of 'workspace2'
Subgraph subgraph1 = graph.getSubgraphOfDepth(100000).at(workspace1.getRoot());
Subgraph subgraph2 = graph.getSubgraphOfDepth(100000).at(workspace2.getRoot());
- assertEquivalentSubgraphs(subgraph1, subgraph2, true);
+ assertEquivalentSubgraphs(subgraph1, subgraph2, true, true);
} catch (InvalidRequestException error) {
// Updates may not be supported, but if they are then this is a failure ...
if (source.getCapabilities().supportsUpdates()) throw error;
Modified: trunk/pom.xml
===================================================================
--- trunk/pom.xml 2009-05-15 19:28:48 UTC (rev 910)
+++ trunk/pom.xml 2009-05-18 15:36:58 UTC (rev 911)
@@ -144,7 +144,7 @@
<module>extensions/dna-common-jdbc</module>
<module>extensions/dna-connector-jdbc-metadata</module>
<module>dna-integration-tests</module>
- <module>docs/examples/gettingstarted</module>
+ <!--module>docs/examples/gettingstarted</module-->
</modules>
<profiles>
16 years, 11 months
DNA SVN: r910 - in trunk: dna-graph/src/main/java/org/jboss/dna/graph/request/processor and 1 other directories.
by dna-commits@lists.jboss.org
Author: rhauch
Date: 2009-05-15 15:28:48 -0400 (Fri, 15 May 2009)
New Revision: 910
Added:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/DeleteChildrenRequest.java
Modified:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/processor/RequestProcessor.java
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/model/basic/BasicRequestProcessor.java
Log:
DNA-409 Add to connector API ability to delete all nodes below a node
Added the DeleteChildrenRequest class and provided a default process(DeleteChildrenRequest) method in RequestProcessor. Therefore, all connectors immediately inherit this functionality. The JPA connector can implement this much more efficiently than if it inherits the default implementation, so this connector was changed to provide its own implementation.
Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/DeleteChildrenRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/DeleteChildrenRequest.java (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/DeleteChildrenRequest.java 2009-05-15 19:28:48 UTC (rev 910)
@@ -0,0 +1,159 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ *
+ * JBoss DNA 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.graph.request;
+
+import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.graph.GraphI18n;
+import org.jboss.dna.graph.Location;
+import org.jboss.dna.graph.property.Path;
+
+/**
+ * Instruction that all nodes below a supplied node be deleted. This is similar to {@link DeleteBranchRequest}, except that the
+ * parent node (top node in the branch) is not deleted.
+ */
+public class DeleteChildrenRequest extends Request implements ChangeRequest {
+
+ private static final long serialVersionUID = 1L;
+
+ private final Location at;
+ private final String workspaceName;
+ private Location actualLocation;
+
+ /**
+ * Create a request to delete all children of the supplied node. The supplied parent node will not be deleted.
+ *
+ * @param at the location of the parent node
+ * @param workspaceName the name of the workspace containing the parent
+ * @throws IllegalArgumentException if the location or workspace name is null
+ */
+ public DeleteChildrenRequest( Location at,
+ String workspaceName ) {
+ CheckArg.isNotNull(at, "at");
+ CheckArg.isNotNull(workspaceName, "workspaceName");
+ this.workspaceName = workspaceName;
+ this.at = at;
+ }
+
+ /**
+ * Get the location defining the top of the branch to be deleted
+ *
+ * @return the location of the branch; never null
+ */
+ public Location at() {
+ return at;
+ }
+
+ /**
+ * Get the name of the workspace in which the branch exists.
+ *
+ * @return the name of the workspace; never null
+ */
+ public String inWorkspace() {
+ return workspaceName;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.request.Request#isReadOnly()
+ */
+ @Override
+ public boolean isReadOnly() {
+ return false;
+ }
+
+ /**
+ * Sets the actual and complete location of the node being deleted. This method must be called when processing the request,
+ * and the actual location must have a {@link Location#getPath() path}.
+ *
+ * @param actual the actual location of the node being deleted, or null if the {@link #at() current location} should be used
+ * @throws IllegalArgumentException if the actual location does not represent the {@link Location#isSame(Location) same
+ * location} as the {@link #at() current location}, or if the actual location does not have a path.
+ */
+ public void setActualLocationOfNode( Location actual ) {
+ if (!at.isSame(actual)) { // not same if actual is null
+ throw new IllegalArgumentException(GraphI18n.actualLocationIsNotSameAsInputLocation.text(actual, at));
+ }
+ assert actual != null;
+ if (!actual.hasPath()) {
+ throw new IllegalArgumentException(GraphI18n.actualLocationMustHavePath.text(actual));
+ }
+ this.actualLocation = actual;
+ }
+
+ /**
+ * Get the actual location of the node that was deleted.
+ *
+ * @return the actual location, or null if the actual location was not set
+ */
+ public Location getActualLocationOfNode() {
+ return actualLocation;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.request.ChangeRequest#changes(java.lang.String, org.jboss.dna.graph.property.Path)
+ */
+ public boolean changes( String workspace,
+ Path path ) {
+ return this.workspaceName.equals(workspace) && at.hasPath() && at.getPath().isAtOrBelow(path);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.request.ChangeRequest#changedLocation()
+ */
+ public Location changedLocation() {
+ return at;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals( Object obj ) {
+ if (obj == this) return true;
+ if (this.getClass().isInstance(obj)) {
+ DeleteChildrenRequest that = (DeleteChildrenRequest)obj;
+ if (!this.at().equals(that.at())) return false;
+ if (!this.inWorkspace().equals(that.inWorkspace())) return false;
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return "delete nodes below " + at() + " in the \"" + workspaceName + "\" workspace";
+ }
+}
Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/DeleteChildrenRequest.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/processor/RequestProcessor.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/processor/RequestProcessor.java 2009-05-15 18:36:03 UTC (rev 909)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/processor/RequestProcessor.java 2009-05-15 19:28:48 UTC (rev 910)
@@ -47,6 +47,7 @@
import org.jboss.dna.graph.request.CreateNodeRequest;
import org.jboss.dna.graph.request.CreateWorkspaceRequest;
import org.jboss.dna.graph.request.DeleteBranchRequest;
+import org.jboss.dna.graph.request.DeleteChildrenRequest;
import org.jboss.dna.graph.request.DestroyWorkspaceRequest;
import org.jboss.dna.graph.request.GetWorkspacesRequest;
import org.jboss.dna.graph.request.InvalidRequestException;
@@ -352,6 +353,39 @@
public abstract void process( DeleteBranchRequest request );
/**
+ * Process a request to delete all of the child nodes under the supplied existing node.
+ * <p>
+ * This method does nothing if the request is null.
+ * </p>
+ *
+ * @param request the delete request
+ * @throws ReferentialIntegrityException if the delete could not be performed because some references to deleted nodes would
+ * have remained after the delete operation completed
+ */
+ public void process( DeleteChildrenRequest request ) {
+ if (request == null) return;
+ if (request.isCancelled()) return;
+ // First get all of the children under the node ...
+ ReadAllChildrenRequest readChildren = new ReadAllChildrenRequest(request.at(), request.inWorkspace());
+ process(readChildren);
+ if (readChildren.hasError()) {
+ request.setError(readChildren.getError());
+ return;
+ }
+ if (readChildren.isCancelled()) return;
+
+ // Issue a DeleteBranchRequest for each child ...
+ for (Location child : readChildren) {
+ if (request.isCancelled()) return;
+ DeleteBranchRequest deleteChild = new DeleteBranchRequest(child, request.inWorkspace());
+ process(deleteChild);
+ }
+
+ // Set the actual location of the parent node ...
+ request.setActualLocationOfNode(readChildren.getActualLocationOfNode());
+ }
+
+ /**
* Process a request to move a branch at a specified location into a different location.
* <p>
* This method does nothing if the request is null.
Modified: trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/model/basic/BasicRequestProcessor.java
===================================================================
--- trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/model/basic/BasicRequestProcessor.java 2009-05-15 18:36:03 UTC (rev 909)
+++ trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/model/basic/BasicRequestProcessor.java 2009-05-15 19:28:48 UTC (rev 910)
@@ -85,6 +85,7 @@
import org.jboss.dna.graph.request.CreateNodeRequest;
import org.jboss.dna.graph.request.CreateWorkspaceRequest;
import org.jboss.dna.graph.request.DeleteBranchRequest;
+import org.jboss.dna.graph.request.DeleteChildrenRequest;
import org.jboss.dna.graph.request.DestroyWorkspaceRequest;
import org.jboss.dna.graph.request.GetWorkspacesRequest;
import org.jboss.dna.graph.request.InvalidRequestException;
@@ -1243,15 +1244,34 @@
@Override
public void process( DeleteBranchRequest request ) {
logger.trace(request.toString());
+ Location location = delete(request, request.at(), request.inWorkspace(), true);
+ if (location != null) request.setActualLocationOfNode(location);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.DeleteChildrenRequest)
+ */
+ @Override
+ public void process( DeleteChildrenRequest request ) {
+ logger.trace(request.toString());
+ Location location = delete(request, request.at(), request.inWorkspace(), false);
+ if (location != null) request.setActualLocationOfNode(location);
+ }
+
+ protected Location delete( Request request,
+ Location location,
+ String workspaceName,
+ boolean deleteTopOfBranch ) {
Location actualLocation = null;
try {
// Find the workspace ...
- WorkspaceEntity workspace = getExistingWorkspace(request.inWorkspace(), request);
- if (workspace == null) return;
+ WorkspaceEntity workspace = getExistingWorkspace(workspaceName, request);
+ if (workspace == null) return null;
Long workspaceId = workspace.getId();
assert workspaceId != null;
- Location location = request.at();
ActualLocation actual = getActualLocation(workspaceId, location);
actualLocation = actual.location;
Path path = actualLocation.getPath();
@@ -1274,7 +1294,7 @@
List<Location> deletedLocations = query.getNodeLocations(true, true);
// Now delete the subgraph ...
- query.deleteSubgraph(true);
+ query.deleteSubgraph(deleteTopOfBranch);
// Verify referential integrity: that none of the deleted nodes are referenced by nodes not being deleted.
List<ReferenceEntity> invalidReferences = query.getInwardReferences();
@@ -1299,14 +1319,16 @@
throw new ReferentialIntegrityException(invalidRefs, msg);
}
- // And adjust the SNS index and indexes ...
- ChildEntity.adjustSnsIndexesAndIndexesAfterRemoving(entities,
- workspaceId,
- parentUuidString,
- childName,
- nsId,
- indexInParent);
- entities.flush();
+ if (deleteTopOfBranch) {
+ // And adjust the SNS index and indexes ...
+ ChildEntity.adjustSnsIndexesAndIndexesAfterRemoving(entities,
+ workspaceId,
+ parentUuidString,
+ childName,
+ nsId,
+ indexInParent);
+ entities.flush();
+ }
// Remove from the cache of children locations all entries for deleted nodes ...
cache.removeBranch(workspaceId, deletedLocations);
@@ -1317,9 +1339,9 @@
} catch (Throwable e) { // Includes PathNotFoundException
request.setError(e);
- return;
+ return null;
}
- request.setActualLocationOfNode(actualLocation);
+ return actualLocation;
}
/**
16 years, 11 months
DNA SVN: r909 - trunk/dna-jcr/src/main/java/org/jboss/dna/jcr.
by dna-commits@lists.jboss.org
Author: rhauch
Date: 2009-05-15 14:36:03 -0400 (Fri, 15 May 2009)
New Revision: 909
Modified:
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrConfiguration.java
Log:
DNA-411 JcrRepository.Options Is Plural - Coding Convention Violation
Added a @SuppressWarnings for synthetic access.
Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrConfiguration.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrConfiguration.java 2009-05-15 18:28:51 UTC (rev 908)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrConfiguration.java 2009-05-15 18:36:03 UTC (rev 909)
@@ -39,20 +39,19 @@
import org.jboss.dna.repository.Configurator.RepositoryDetails;
/**
- * A configuration builder for a {@link JcrEngine}. This class is an internal domain-specific language (DSL),
- * and is designed to be used in a traditional way or in a method-chained manner:
+ * A configuration builder for a {@link JcrEngine}. This class is an internal domain-specific language (DSL), and is designed to
+ * be used in a traditional way or in a method-chained manner:
+ *
* <pre>
- * configuration.addRepository("Source1").usingClass(InMemoryRepositorySource.class).describedAs("description");
- * configuration.addMimeTypeDetector("detector")
- * .usingClass(ExtensionBasedMimeTypeDetector.class)
- * .describedAs("default detector");
- * configuration.addSequencer("MicrosoftDocs")
- * .usingClass("org.jboss.dna.sequencer.msoffice.MSOfficeMetadataSequencer")
+ * configuration.addRepository("Source1").usingClass(InMemoryRepositorySource.class).describedAs("description");
+ * configuration.addMimeTypeDetector("detector").usingClass(ExtensionBasedMimeTypeDetector.class).describedAs("default detector");
+ * configuration.addSequencer("MicrosoftDocs")
+ * .usingClass("org.jboss.dna.sequencer.msoffice.MSOfficeMetadataSequencer")
* .loadedFromClasspath()
- * .named("Microsoft Document sequencer")
- * .describedAs("Our primary sequencer for all .doc files")
- * .sequencingFrom("/public//(*.(doc|xml|ppt)[*]/jcr:content[@jcr:data]")
- * .andOutputtingTo("/documents/$1");
+ * .named("Microsoft Document sequencer")
+ * .describedAs("Our primary sequencer for all .doc files")
+ * .sequencingFrom("/public//(*.(doc|xml|ppt)[*]/jcr:content[@jcr:data]")
+ * .andOutputtingTo("/documents/$1");
* configuration.save();
* </pre>
*/
@@ -98,15 +97,15 @@
return builder.withConfigurationRepository();
}
-// /**
-// * {@inheritDoc}
-// *
-// * @see org.jboss.dna.repository.Configurator.SequencerConfigurator#addSequencer(java.lang.String)
-// */
-// public ChooseClass<Sequencer, SequencerDetails<JcrConfiguration>> addSequencer( String id ) {
-// CheckArg.isNotEmpty(id, "id");
-// return builder.addSequencer(id);
-// }
+ // /**
+ // * {@inheritDoc}
+ // *
+ // * @see org.jboss.dna.repository.Configurator.SequencerConfigurator#addSequencer(java.lang.String)
+ // */
+ // public ChooseClass<Sequencer, SequencerDetails<JcrConfiguration>> addSequencer( String id ) {
+ // CheckArg.isNotEmpty(id, "id");
+ // return builder.addSequencer(id);
+ // }
/**
* {@inheritDoc}
@@ -212,13 +211,11 @@
ReturnType builder ) {
super(context, builder);
}
-
+
protected Graph getGraph() {
return graph();
}
-
-
/**
* {@inheritDoc}
*
@@ -240,17 +237,18 @@
super(path, returnObject);
}
+ @SuppressWarnings( "synthetic-access" )
public OptionSetter<JcrRepositoryDetails<RT>> with( final JcrRepository.Option option ) {
final Path optionsPath = createOrReplaceNode(path(), DnaLexicon.OPTIONS);
final JcrRepositoryDetails<RT> details = this;
-
+
return new OptionSetter<JcrRepositoryDetails<RT>>() {
public JcrRepositoryDetails<RT> setTo( String value ) {
Path optionPath = createOrReplaceNode(optionsPath, option.name());
configuration().set(DnaLexicon.VALUE).to(value).on(optionPath);
-
- return details;
+
+ return details;
}
};
}
16 years, 11 months
DNA SVN: r908 - trunk/dna-jcr/src/main/java/org/jboss/dna/jcr.
by dna-commits@lists.jboss.org
Author: bcarothers
Date: 2009-05-15 14:28:51 -0400 (Fri, 15 May 2009)
New Revision: 908
Modified:
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrConfiguration.java
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrRepository.java
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java
Log:
DNA-411 JcrRepository.Options Is Plural - Coding Convention Violation
Fixed Javadoc
Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrConfiguration.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrConfiguration.java 2009-05-15 16:39:22 UTC (rev 907)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrConfiguration.java 2009-05-15 18:28:51 UTC (rev 908)
@@ -168,7 +168,7 @@
}
/**
- * Interface for configuring the {@link JcrRepository.Options JCR repository options} for a {@link JcrRepository JCR
+ * Interface for configuring the {@link JcrRepository.Option JCR repository options} for a {@link JcrRepository JCR
* repository}.
*
* @param <ReturnType> the interface returned after the option has been set.
@@ -184,10 +184,10 @@
}
/**
- * The interface used to set the value for a {@link JcrRepository.Options JCR repository option}.
+ * The interface used to set the value for a {@link JcrRepository.Option JCR repository option}.
*
* @param <ReturnType> the interface returned from these methods
- * @see JcrConfiguration.SetOptions#with(org.jboss.dna.jcr.JcrRepository.Options)
+ * @see JcrConfiguration.SetOptions#with(org.jboss.dna.jcr.JcrRepository.Option)
*/
public interface OptionSetter<ReturnType> {
/**
Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrRepository.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrRepository.java 2009-05-15 16:39:22 UTC (rev 907)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrRepository.java 2009-05-15 18:28:51 UTC (rev 908)
@@ -83,7 +83,7 @@
* Flag that defines whether or not the node types should be exposed as content under the "{@code
* /jcr:system/jcr:nodeTypes}" node. Value is either "<code>true</code>" or "<code>false</code>" (default).
*
- * @see DefaultOptions#PROJECT_NODE_TYPES
+ * @see DefaultOption#PROJECT_NODE_TYPES
*/
PROJECT_NODE_TYPES,
/**
@@ -94,16 +94,16 @@
}
/**
- * The default values for each of the {@link Options}.
+ * The default values for each of the {@link Option}.
*/
public static class DefaultOption {
/**
- * The default value for the {@link Options#PROJECT_NODE_TYPES} option is {@value} .
+ * The default value for the {@link Option#PROJECT_NODE_TYPES} option is {@value} .
*/
public static final String PROJECT_NODE_TYPES = Boolean.FALSE.toString();
/**
- * The default value for the {@link Options#JAAS_LOGIN_CONFIG_NAME} option is {@value} .
+ * The default value for the {@link Option#JAAS_LOGIN_CONFIG_NAME} option is {@value} .
*/
public static final String JAAS_LOGIN_CONFIG_NAME = "dna-jcr";
}
@@ -152,7 +152,7 @@
* @param connectionFactory the factory for repository connections
* @param repositorySourceName the name of the repository source (in the connection factory) that should be used
* @param descriptors the {@link #getDescriptorKeys() descriptors} for this repository; may be <code>null</code>.
- * @param options the optional {@link Options settings} for this repository; may be null
+ * @param options the optional {@link Option settings} for this repository; may be null
* @throws IllegalArgumentException If <code>executionContextFactory</code> or <code>connectionFactory</code> is
* <code>null</code>.
*/
Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java 2009-05-15 16:39:22 UTC (rev 907)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java 2009-05-15 18:28:51 UTC (rev 908)
@@ -665,7 +665,13 @@
throw new ConstraintViolationException();
}
}
-
+
+ for (UUID changedUuid : uuidsUnderBranch) {
+ if (!this.deletedNodes.containsKey(changedUuid)) {
+ checkAgainstTypeDefinitions(changedUuid, false);
+ }
+ }
+
// Now execute the branch ...
Graph.Batch branchBatch = store.batch(new BatchRequestBuilder(branchRequests));
try {
16 years, 11 months
DNA SVN: r907 - in trunk/dna-jcr/src: test/java/org/jboss/dna/jcr and 1 other directory.
by dna-commits@lists.jboss.org
Author: bcarothers
Date: 2009-05-15 12:39:22 -0400 (Fri, 15 May 2009)
New Revision: 907
Modified:
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrConfiguration.java
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrEngine.java
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrRepository.java
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrWorkspace.java
trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/InMemoryRepositoryStub.java
trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrConfigurationTest.java
trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrRepositoryTest.java
trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/MixinTest.java
trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/RepositoryNodeTypeManagerTest.java
Log:
DNA-411 JcrRepository.Options Is Plural - Coding Convention Violation
Applied patch that renamed JcrRepository.Options to Option and JcrRepository.DefaultOptions to DefaultOption. The patch also propagates the name changes through the JCR source and test packages as needed.
Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrConfiguration.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrConfiguration.java 2009-05-15 16:17:10 UTC (rev 906)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrConfiguration.java 2009-05-15 16:39:22 UTC (rev 907)
@@ -180,7 +180,7 @@
* @param option the option to be set
* @return the interface used to set the value for the property; never null
*/
- OptionSetter<ReturnType> with( JcrRepository.Options option );
+ OptionSetter<ReturnType> with( JcrRepository.Option option );
}
/**
@@ -240,7 +240,7 @@
super(path, returnObject);
}
- public OptionSetter<JcrRepositoryDetails<RT>> with( final JcrRepository.Options option ) {
+ public OptionSetter<JcrRepositoryDetails<RT>> with( final JcrRepository.Option option ) {
final Path optionsPath = createOrReplaceNode(path(), DnaLexicon.OPTIONS);
final JcrRepositoryDetails<RT> details = this;
Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrEngine.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrEngine.java 2009-05-15 16:17:10 UTC (rev 906)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrEngine.java 2009-05-15 16:39:22 UTC (rev 907)
@@ -41,7 +41,7 @@
import org.jboss.dna.graph.property.PathFactory;
import org.jboss.dna.graph.property.PathNotFoundException;
import org.jboss.dna.graph.property.Property;
-import org.jboss.dna.jcr.JcrRepository.Options;
+import org.jboss.dna.jcr.JcrRepository.Option;
import org.jboss.dna.repository.DnaEngine;
import org.jboss.dna.repository.RepositoryService;
import org.jboss.dna.repository.observation.ObservationService;
@@ -142,7 +142,7 @@
* Extract the JCR options from the configuration graph
*/
String configurationName = dnaEngine.getRepositoryService().getConfigurationSourceName();
- Map<Options, String> options = new HashMap<Options, String>();
+ Map<Option, String> options = new HashMap<Option, String>();
PathFactory pathFactory = getExecutionContext().getValueFactories().getPathFactory();
Graph configuration = Graph.create(connectionFactory.createConnection(configurationName), getExecutionContext());
@@ -167,7 +167,7 @@
Node optionNode = configuration.getNodeAt(optionLocation);
Property valueProperty = optionNode.getProperty(DnaLexicon.VALUE);
- options.put(Options.valueOf(segment.getName().getLocalName()),
+ options.put(Option.valueOf(segment.getName().getLocalName()),
valueProperty.getFirstValue().toString());
}
Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrRepository.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrRepository.java 2009-05-15 16:17:10 UTC (rev 906)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrRepository.java 2009-05-15 16:39:22 UTC (rev 907)
@@ -77,7 +77,7 @@
/**
* The available options for the {@code JcrRepository}.
*/
- public enum Options {
+ public enum Option {
/**
* Flag that defines whether or not the node types should be exposed as content under the "{@code
@@ -96,7 +96,7 @@
/**
* The default values for each of the {@link Options}.
*/
- public static class DefaultOptions {
+ public static class DefaultOption {
/**
* The default value for the {@link Options#PROJECT_NODE_TYPES} option is {@value} .
*/
@@ -111,14 +111,14 @@
/**
* The static unmodifiable map of default options, which are initialized in the static initializer.
*/
- protected static final Map<Options, String> DEFAULT_OPTIONS;
+ protected static final Map<Option, String> DEFAULT_OPTIONS;
static {
// Initialize the unmodifiable map of default options ...
- EnumMap<Options, String> defaults = new EnumMap<Options, String>(Options.class);
- defaults.put(Options.PROJECT_NODE_TYPES, DefaultOptions.PROJECT_NODE_TYPES);
- defaults.put(Options.JAAS_LOGIN_CONFIG_NAME, DefaultOptions.JAAS_LOGIN_CONFIG_NAME);
- DEFAULT_OPTIONS = Collections.<Options, String>unmodifiableMap(defaults);
+ EnumMap<Option, String> defaults = new EnumMap<Option, String>(Option.class);
+ defaults.put(Option.PROJECT_NODE_TYPES, DefaultOption.PROJECT_NODE_TYPES);
+ defaults.put(Option.JAAS_LOGIN_CONFIG_NAME, DefaultOption.JAAS_LOGIN_CONFIG_NAME);
+ DEFAULT_OPTIONS = Collections.<Option, String>unmodifiableMap(defaults);
}
private final String sourceName;
@@ -126,7 +126,7 @@
private final ExecutionContext executionContext;
private final RepositoryConnectionFactory connectionFactory;
private final RepositoryNodeTypeManager repositoryTypeManager;
- private final Map<Options, String> options;
+ private final Map<Option, String> options;
/**
* Creates a JCR repository that uses the supplied {@link RepositoryConnectionFactory repository connection factory} to
@@ -160,7 +160,7 @@
RepositoryConnectionFactory connectionFactory,
String repositorySourceName,
Map<String, String> descriptors,
- Map<Options, String> options ) {
+ Map<Option, String> options ) {
CheckArg.isNotNull(executionContext, "executionContext");
CheckArg.isNotNull(connectionFactory, "connectionFactory");
CheckArg.isNotNull(repositorySourceName, "repositorySourceName");
@@ -217,7 +217,7 @@
this.options = DEFAULT_OPTIONS;
} else {
// Initialize with defaults, then add supplied options ...
- EnumMap<Options, String> localOptions = new EnumMap<Options, String>(DEFAULT_OPTIONS);
+ EnumMap<Option, String> localOptions = new EnumMap<Option, String>(DEFAULT_OPTIONS);
localOptions.putAll(options);
this.options = Collections.unmodifiableMap(localOptions);
}
@@ -237,7 +237,7 @@
*
* @return the unmodifiable options; never null
*/
- public Map<Options, String> getOptions() {
+ public Map<Option, String> getOptions() {
return options;
}
@@ -360,7 +360,7 @@
} catch (NoSuchMethodException error2) {
if (credentials instanceof SimpleCredentials) {
SimpleCredentials simple = (SimpleCredentials)credentials;
- execContext = executionContext.with(options.get(Options.JAAS_LOGIN_CONFIG_NAME),
+ execContext = executionContext.with(options.get(Option.JAAS_LOGIN_CONFIG_NAME),
simple.getUserID(),
simple.getPassword());
} else {
Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrWorkspace.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrWorkspace.java 2009-05-15 16:17:10 UTC (rev 906)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrWorkspace.java 2009-05-15 16:39:22 UTC (rev 907)
@@ -59,7 +59,7 @@
import org.jboss.dna.graph.property.basic.GraphNamespaceRegistry;
import org.jboss.dna.jcr.JcrContentHandler.EnclosingSAXException;
import org.jboss.dna.jcr.JcrContentHandler.SaveMode;
-import org.jboss.dna.jcr.JcrRepository.Options;
+import org.jboss.dna.jcr.JcrRepository.Option;
import org.xml.sax.ContentHandler;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
@@ -171,7 +171,7 @@
this.nodeTypeManager = new JcrNodeTypeManager(session.getExecutionContext(), repoTypeManager);
this.queryManager = new JcrQueryManager(this.session);
- if (Boolean.valueOf(repository.getOptions().get(Options.PROJECT_NODE_TYPES))) {
+ if (Boolean.valueOf(repository.getOptions().get(Option.PROJECT_NODE_TYPES))) {
Path parentOfTypeNodes = context.getValueFactories().getPathFactory().create(systemPath, JcrLexicon.NODE_TYPES);
repoTypeManager.projectOnto(this.graph, parentOfTypeNodes);
}
Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/InMemoryRepositoryStub.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/InMemoryRepositoryStub.java 2009-05-15 16:17:10 UTC (rev 906)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/InMemoryRepositoryStub.java 2009-05-15 16:39:22 UTC (rev 907)
@@ -34,7 +34,7 @@
import org.jboss.dna.graph.connector.inmemory.InMemoryRepositorySource;
import org.jboss.dna.graph.io.GraphImporter;
import org.jboss.dna.graph.property.Path;
-import org.jboss.dna.jcr.JcrRepository.Options;
+import org.jboss.dna.jcr.JcrRepository.Option;
import org.jboss.security.config.IDTrustConfiguration;
/**
@@ -72,7 +72,7 @@
.addRepository("JCR Repository")
.usingClass(InMemoryRepositorySource.class.getName())
.loadedFromClasspath()
- .with(Options.PROJECT_NODE_TYPES).setTo(Boolean.FALSE.toString())
+ .with(Option.PROJECT_NODE_TYPES).setTo(Boolean.FALSE.toString())
.describedAs("JCR Repository")
.with("name").setTo(REPOSITORY_SOURCE_NAME)
.and().build();
Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrConfigurationTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrConfigurationTest.java 2009-05-15 16:17:10 UTC (rev 906)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrConfigurationTest.java 2009-05-15 16:39:22 UTC (rev 907)
@@ -39,7 +39,7 @@
import org.jboss.dna.graph.connector.inmemory.InMemoryRepositorySource;
import org.jboss.dna.graph.mimetype.ExtensionBasedMimeTypeDetector;
import org.jboss.dna.graph.property.Path;
-import org.jboss.dna.jcr.JcrRepository.Options;
+import org.jboss.dna.jcr.JcrRepository.Option;
import org.jboss.dna.repository.DnaConfiguration;
import org.jboss.dna.repository.DnaEngine;
import org.jboss.dna.repository.RepositoryLibrary;
@@ -265,7 +265,7 @@
.addRepository("Source2")
.usingClass(InMemoryRepositorySource.class.getName())
.loadedFromClasspath()
- .with(Options.JAAS_LOGIN_CONFIG_NAME).setTo("test")
+ .with(Option.JAAS_LOGIN_CONFIG_NAME).setTo("test")
.describedAs("description")
.with("name").setTo("JCR Repository")
.and()
@@ -282,9 +282,9 @@
JcrRepository repository = engine.getRepository("JCR Repository");
- Map<Options, String> options = new HashMap<Options, String>();
- options.put(Options.JAAS_LOGIN_CONFIG_NAME, "test");
- options.put(Options.PROJECT_NODE_TYPES, "false");
+ Map<Option, String> options = new HashMap<Option, String>();
+ options.put(Option.JAAS_LOGIN_CONFIG_NAME, "test");
+ options.put(Option.PROJECT_NODE_TYPES, "false");
assertThat(repository.getOptions(), is(options));
}
Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrRepositoryTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrRepositoryTest.java 2009-05-15 16:17:10 UTC (rev 906)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrRepositoryTest.java 2009-05-15 16:39:22 UTC (rev 907)
@@ -156,8 +156,8 @@
@Test
public void shouldHaveDefaultOptionsWhenNotOverridden() {
JcrRepository repository = new JcrRepository(context, connectionFactory, sourceName, descriptors, null);
- assertThat(repository.getOptions().get(JcrRepository.Options.PROJECT_NODE_TYPES),
- is(JcrRepository.DefaultOptions.PROJECT_NODE_TYPES));
+ assertThat(repository.getOptions().get(JcrRepository.Option.PROJECT_NODE_TYPES),
+ is(JcrRepository.DefaultOption.PROJECT_NODE_TYPES));
}
@Test
Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/MixinTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/MixinTest.java 2009-05-15 16:17:10 UTC (rev 906)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/MixinTest.java 2009-05-15 16:39:22 UTC (rev 907)
@@ -93,7 +93,7 @@
private RepositoryConnectionFactory connectionFactory;
private RepositoryNodeTypeManager repoTypeManager;
private Map<String, Object> sessionAttributes;
- private Map<JcrRepository.Options, String> options;
+ private Map<JcrRepository.Option, String> options;
private NamespaceRegistry registry;
@Mock
private JcrRepository repository;
@@ -152,8 +152,8 @@
stub(repository.getConnectionFactory()).toReturn(connectionFactory);
// Stub out the repository options ...
- options = new EnumMap<JcrRepository.Options, String>(JcrRepository.Options.class);
- options.put(JcrRepository.Options.PROJECT_NODE_TYPES, Boolean.FALSE.toString());
+ options = new EnumMap<JcrRepository.Option, String>(JcrRepository.Option.class);
+ options.put(JcrRepository.Option.PROJECT_NODE_TYPES, Boolean.FALSE.toString());
stub(repository.getOptions()).toReturn(options);
// Set up the session attributes ...
Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/RepositoryNodeTypeManagerTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/RepositoryNodeTypeManagerTest.java 2009-05-15 16:17:10 UTC (rev 906)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/RepositoryNodeTypeManagerTest.java 2009-05-15 16:39:22 UTC (rev 907)
@@ -73,7 +73,7 @@
private RepositoryConnectionFactory connectionFactory;
private RepositoryNodeTypeManager repoTypeManager;
private Map<String, Object> sessionAttributes;
- private Map<JcrRepository.Options, String> options;
+ private Map<JcrRepository.Option, String> options;
@Mock
private JcrRepository repository;
@@ -135,8 +135,8 @@
stub(repository.getConnectionFactory()).toReturn(connectionFactory);
// Stub out the repository options ...
- options = new EnumMap<JcrRepository.Options, String>(JcrRepository.Options.class);
- options.put(JcrRepository.Options.PROJECT_NODE_TYPES, Boolean.TRUE.toString());
+ options = new EnumMap<JcrRepository.Option, String>(JcrRepository.Option.class);
+ options.put(JcrRepository.Option.PROJECT_NODE_TYPES, Boolean.TRUE.toString());
stub(repository.getOptions()).toReturn(options);
// Set up the session attributes ...
16 years, 11 months
DNA SVN: r906 - in trunk: dna-jcr/src/test/java/org/jboss/dna/jcr and 1 other directories.
by dna-commits@lists.jboss.org
Author: bcarothers
Date: 2009-05-15 12:17:10 -0400 (Fri, 15 May 2009)
New Revision: 906
Added:
trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrConfigurationTest.java
Modified:
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrConfiguration.java
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrEngine.java
trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/InMemoryRepositoryStub.java
trunk/dna-repository/src/main/java/org/jboss/dna/repository/Configurator.java
trunk/dna-repository/src/main/java/org/jboss/dna/repository/DnaConfiguration.java
trunk/dna-repository/src/main/java/org/jboss/dna/repository/DnaLexicon.java
Log:
DNA-400 JcrConfiguration Could Support Setting Options
Applied patch that adds support for with(<Option>).setTo(<Value>) syntax for JcrConfiguration and then converted InMemoryRepositoryStub to use JcrEngine/JcrConfiguration.
Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrConfiguration.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrConfiguration.java 2009-05-15 14:01:49 UTC (rev 905)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrConfiguration.java 2009-05-15 16:17:10 UTC (rev 906)
@@ -25,11 +25,14 @@
import org.jboss.dna.common.util.CheckArg;
import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.Graph;
import org.jboss.dna.graph.connector.RepositorySource;
import org.jboss.dna.graph.mimetype.MimeTypeDetector;
+import org.jboss.dna.graph.property.Path;
import org.jboss.dna.repository.Configurator;
import org.jboss.dna.repository.DnaConfiguration;
import org.jboss.dna.repository.DnaConfigurationException;
+import org.jboss.dna.repository.DnaLexicon;
import org.jboss.dna.repository.Configurator.ChooseClass;
import org.jboss.dna.repository.Configurator.ConfigRepositoryDetails;
import org.jboss.dna.repository.Configurator.MimeTypeDetectorDetails;
@@ -58,7 +61,7 @@
Configurator.RepositoryConfigurator<JcrConfiguration>, Configurator.MimeDetectorConfigurator<JcrConfiguration>,
Configurator.Builder<JcrEngine> {
- private final DnaConfiguration.Builder<JcrConfiguration> builder;
+ private final JcrConfiguration.Builder<JcrConfiguration> builder;
/**
* Create a new configuration for DNA.
@@ -74,7 +77,7 @@
* @throws IllegalArgumentException if the supplied context reference is null
*/
public JcrConfiguration( ExecutionContext context ) {
- this.builder = new DnaConfiguration.Builder<JcrConfiguration>(context, this);
+ this.builder = new JcrConfiguration.Builder<JcrConfiguration>(context, this);
}
/**
@@ -110,7 +113,7 @@
*
* @see org.jboss.dna.repository.Configurator.RepositoryConfigurator#addRepository(java.lang.String)
*/
- public ChooseClass<RepositorySource, RepositoryDetails<JcrConfiguration>> addRepository( String id ) {
+ public ChooseClass<RepositorySource, JcrRepositoryDetails<JcrConfiguration>> addRepository( String id ) {
CheckArg.isNotEmpty(id, "id");
return builder.addRepository(id);
}
@@ -145,6 +148,10 @@
return builder.save();
}
+ protected Graph graph() {
+ return builder.getGraph();
+ }
+
/**
* {@inheritDoc}
*
@@ -154,4 +161,100 @@
save();
return new JcrEngine(builder.buildDnaEngine());
}
+
+ public interface JcrRepositoryDetails<ReturnType>
+ extends RepositoryDetails<ReturnType>, SetOptions<JcrRepositoryDetails<ReturnType>> {
+
+ }
+
+ /**
+ * Interface for configuring the {@link JcrRepository.Options JCR repository options} for a {@link JcrRepository JCR
+ * repository}.
+ *
+ * @param <ReturnType> the interface returned after the option has been set.
+ */
+ public interface SetOptions<ReturnType> {
+ /**
+ * Specify the repository option that is to be set. The value may be set using the interface returned by this method.
+ *
+ * @param option the option to be set
+ * @return the interface used to set the value for the property; never null
+ */
+ OptionSetter<ReturnType> with( JcrRepository.Options option );
+ }
+
+ /**
+ * The interface used to set the value for a {@link JcrRepository.Options JCR repository option}.
+ *
+ * @param <ReturnType> the interface returned from these methods
+ * @see JcrConfiguration.SetOptions#with(org.jboss.dna.jcr.JcrRepository.Options)
+ */
+ public interface OptionSetter<ReturnType> {
+ /**
+ * Set the property value to an integer.
+ *
+ * @param value the new value for the property
+ * @return the next component to continue configuration; never null
+ */
+ ReturnType setTo( String value );
+ }
+
+ public static class Builder<ReturnType> extends DnaConfiguration.Builder<ReturnType> {
+
+ /**
+ * Specify a new {@link ExecutionContext} that should be used for this DNA instance.
+ *
+ * @param context the new context, or null if a default-constructed execution context should be used
+ * @param builder the builder object returned from all the methods
+ * @throws IllegalArgumentException if the supplied context reference is null
+ */
+ public Builder( ExecutionContext context,
+ ReturnType builder ) {
+ super(context, builder);
+ }
+
+ protected Graph getGraph() {
+ return graph();
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.repository.Configurator.RepositoryConfigurator#addRepository(java.lang.String)
+ */
+ @Override
+ public ChooseClass<RepositorySource, JcrRepositoryDetails<ReturnType>> addRepository( String id ) {
+ CheckArg.isNotEmpty(id, "id");
+ // Now create the "dna:source" node with the supplied id ...
+ Path path = createOrReplaceNode(sourcesPath(), id);
+ JcrRepositoryDetails<ReturnType> details = new JcrGraphRepositoryDetails<ReturnType>(path, builder);
+ return new ClassChooser<RepositorySource, JcrRepositoryDetails<ReturnType>>(path, details);
+ }
+
+ public class JcrGraphRepositoryDetails<RT> extends GraphRepositoryDetails<RT> implements JcrRepositoryDetails<RT> {
+
+ protected JcrGraphRepositoryDetails( Path path,
+ RT returnObject ) {
+ super(path, returnObject);
+ }
+
+ public OptionSetter<JcrRepositoryDetails<RT>> with( final JcrRepository.Options option ) {
+ final Path optionsPath = createOrReplaceNode(path(), DnaLexicon.OPTIONS);
+
+ final JcrRepositoryDetails<RT> details = this;
+
+ return new OptionSetter<JcrRepositoryDetails<RT>>() {
+ public JcrRepositoryDetails<RT> setTo( String value ) {
+ Path optionPath = createOrReplaceNode(optionsPath, option.name());
+ configuration().set(DnaLexicon.VALUE).to(value).on(optionPath);
+
+ return details;
+ }
+ };
+ }
+ }
+
+ }
}
Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrEngine.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrEngine.java 2009-05-15 14:01:49 UTC (rev 905)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrEngine.java 2009-05-15 16:17:10 UTC (rev 906)
@@ -23,7 +23,6 @@
*/
package org.jboss.dna.jcr;
-import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.Lock;
@@ -33,8 +32,15 @@
import org.jboss.dna.common.collection.Problems;
import org.jboss.dna.common.util.CheckArg;
import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.Graph;
+import org.jboss.dna.graph.Location;
+import org.jboss.dna.graph.Node;
import org.jboss.dna.graph.connector.RepositoryConnectionFactory;
import org.jboss.dna.graph.connector.RepositorySource;
+import org.jboss.dna.graph.property.Path;
+import org.jboss.dna.graph.property.PathFactory;
+import org.jboss.dna.graph.property.PathNotFoundException;
+import org.jboss.dna.graph.property.Property;
import org.jboss.dna.jcr.JcrRepository.Options;
import org.jboss.dna.repository.DnaEngine;
import org.jboss.dna.repository.RepositoryService;
@@ -108,7 +114,7 @@
* @throws IllegalArgumentException if the repository name is null, blank or invalid
* @throws RepositoryException if there is no repository with the specified name
*/
- public final Repository getRepository( String repositoryName ) throws RepositoryException {
+ public final JcrRepository getRepository( String repositoryName ) throws RepositoryException {
CheckArg.isNotEmpty(repositoryName, "repositoryName");
try {
repositoriesLock.lock();
@@ -131,7 +137,49 @@
protected JcrRepository doCreateJcrRepository( String repositoryName ) {
RepositoryConnectionFactory connectionFactory = getRepositoryConnectionFactory();
Map<String, String> descriptors = null;
- Map<Options, String> options = Collections.singletonMap(Options.PROJECT_NODE_TYPES, "false");
+
+ /*
+ * Extract the JCR options from the configuration graph
+ */
+ String configurationName = dnaEngine.getRepositoryService().getConfigurationSourceName();
+ Map<Options, String> options = new HashMap<Options, String>();
+
+ PathFactory pathFactory = getExecutionContext().getValueFactories().getPathFactory();
+ Graph configuration = Graph.create(connectionFactory.createConnection(configurationName), getExecutionContext());
+
+ try {
+ Node sources = configuration.getNodeAt(pathFactory.create(DnaLexicon.SOURCES));
+
+ /*
+ * Hopefully, this can all get cleaned up when the connector layer supports queries
+ */
+ for (Location childLocation : sources.getChildren()) {
+ Node source = configuration.getNodeAt(childLocation);
+
+ Property nameProperty = source.getProperty("name");
+ if (nameProperty != null && nameProperty.getFirstValue().toString().equals(repositoryName)) {
+ for (Location optionsLocation : source.getChildren()) {
+ if (DnaLexicon.OPTIONS.equals(optionsLocation.getPath().getLastSegment().getName())) {
+ Node optionsNode = configuration.getNodeAt(optionsLocation);
+
+ for (Location optionLocation : optionsNode.getChildren()) {
+ Path.Segment segment = optionLocation.getPath().getLastSegment();
+ Node optionNode = configuration.getNodeAt(optionLocation);
+ Property valueProperty = optionNode.getProperty(DnaLexicon.VALUE);
+
+ options.put(Options.valueOf(segment.getName().getLocalName()),
+ valueProperty.getFirstValue().toString());
+
+ }
+
+ }
+ }
+ }
+ }
+ } catch (PathNotFoundException pnfe) {
+ // Must not be any configuration set up
+ }
+
return new JcrRepository(getExecutionContext(), connectionFactory, repositoryName, descriptors, options);
}
Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/InMemoryRepositoryStub.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/InMemoryRepositoryStub.java 2009-05-15 14:01:49 UTC (rev 905)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/InMemoryRepositoryStub.java 2009-05-15 16:17:10 UTC (rev 906)
@@ -25,16 +25,12 @@
import java.io.File;
import java.net.URI;
-import java.util.Collections;
-import java.util.Map;
import java.util.Properties;
import org.apache.jackrabbit.test.RepositoryStub;
import org.jboss.dna.common.collection.Problem;
import org.jboss.dna.graph.ExecutionContext;
import org.jboss.dna.graph.Graph;
import org.jboss.dna.graph.Location;
-import org.jboss.dna.graph.connector.RepositoryConnection;
-import org.jboss.dna.graph.connector.RepositoryConnectionFactory;
import org.jboss.dna.graph.connector.inmemory.InMemoryRepositorySource;
import org.jboss.dna.graph.io.GraphImporter;
import org.jboss.dna.graph.property.Path;
@@ -45,6 +41,8 @@
* Concrete implementation of {@link RepositoryStub} based on DNA-specific configuration.
*/
public class InMemoryRepositoryStub extends RepositoryStub {
+ private static final String REPOSITORY_SOURCE_NAME = "Test Repository Source";
+
private JcrRepository repository;
static {
@@ -64,32 +62,34 @@
super(env);
// Create the in-memory (DNA) repository
- final InMemoryRepositorySource source = new InMemoryRepositorySource();
-
- // Various calls will fail if you do not set a non-null name for the source
- source.setName("TestRepositorySource");
-
- ExecutionContext executionContext = new ExecutionContext();
+ JcrEngine engine = new JcrConfiguration()
+ .withConfigurationRepository()
+ .usingClass(InMemoryRepositorySource.class.getName())
+ .loadedFromClasspath()
+ .describedAs("configuration repository")
+ .with("name").setTo("configuration")
+ .and()
+ .addRepository("JCR Repository")
+ .usingClass(InMemoryRepositorySource.class.getName())
+ .loadedFromClasspath()
+ .with(Options.PROJECT_NODE_TYPES).setTo(Boolean.FALSE.toString())
+ .describedAs("JCR Repository")
+ .with("name").setTo(REPOSITORY_SOURCE_NAME)
+ .and().build();
+ engine.start();
+
+ ExecutionContext executionContext = engine.getExecutionContext();
executionContext.getNamespaceRegistry().register(TestLexicon.Namespace.PREFIX, TestLexicon.Namespace.URI);
- RepositoryConnectionFactory connectionFactory = new RepositoryConnectionFactory() {
- public RepositoryConnection createConnection( String sourceName ) {
- return source.getConnection();
- }
- };
+ try {
+ repository = engine.getRepository(REPOSITORY_SOURCE_NAME);
+ RepositoryNodeTypeManager nodeTypes = repository.getRepositoryTypeManager();
- // Wrap a connection to the in-memory (DNA) repository in a (JCR) repository
- Map<Options, String> options = Collections.singletonMap(Options.PROJECT_NODE_TYPES, "false");
+ // Set up some sample nodes in the graph to match the expected test configuration
+ Graph graph = Graph.create(repository.getRepositorySourceName(), engine.getRepositoryConnectionFactory(), executionContext);
+ GraphImporter importer = new GraphImporter(graph);
+ Path destinationPath = executionContext.getValueFactories().getPathFactory().createRootPath();
- repository = new JcrRepository(executionContext, connectionFactory, source.getName(), null, options);
- RepositoryNodeTypeManager nodeTypes = repository.getRepositoryTypeManager();
-
- // Set up some sample nodes in the graph to match the expected test configuration
- Graph graph = Graph.create(source.getName(), connectionFactory, executionContext);
- GraphImporter importer = new GraphImporter(graph);
- Path destinationPath = executionContext.getValueFactories().getPathFactory().createRootPath();
-
- try {
CndNodeTypeSource nodeTypeSource = new CndNodeTypeSource("/tck_test_types.cnd");
for (Problem problem : nodeTypeSource.getProblems()) {
Added: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrConfigurationTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrConfigurationTest.java (rev 0)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrConfigurationTest.java 2009-05-15 16:17:10 UTC (rev 906)
@@ -0,0 +1,312 @@
+/*
+ * 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.jcr;
+
+import static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsInstanceOf.instanceOf;
+import static org.hamcrest.core.IsNull.notNullValue;
+import static org.jboss.dna.graph.IsNodeWithChildren.hasChild;
+import static org.jboss.dna.graph.IsNodeWithProperty.hasProperty;
+import static org.junit.Assert.assertThat;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+import java.util.concurrent.TimeUnit;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.Subgraph;
+import org.jboss.dna.graph.cache.CachePolicy;
+import org.jboss.dna.graph.cache.ImmutableCachePolicy;
+import org.jboss.dna.graph.connector.RepositoryConnection;
+import org.jboss.dna.graph.connector.inmemory.InMemoryRepositorySource;
+import org.jboss.dna.graph.mimetype.ExtensionBasedMimeTypeDetector;
+import org.jboss.dna.graph.property.Path;
+import org.jboss.dna.jcr.JcrRepository.Options;
+import org.jboss.dna.repository.DnaConfiguration;
+import org.jboss.dna.repository.DnaEngine;
+import org.jboss.dna.repository.RepositoryLibrary;
+import org.junit.Before;
+import org.junit.Test;
+
+public class JcrConfigurationTest {
+
+ private ExecutionContext context;
+ private JcrConfiguration configuration;
+
+ @Before
+ public void beforeEach() {
+ context = new ExecutionContext();
+ configuration = new JcrConfiguration();
+ }
+
+ protected Path.Segment segment( String segment ) {
+ return context.getValueFactories().getPathFactory().createSegment(segment);
+ }
+
+ @Test
+ public void shouldAllowCreatingWithNoArguments() {
+ configuration = new JcrConfiguration();
+ }
+
+ @Test
+ public void shouldAllowCreatingWithSpecifiedExecutionContext() {
+ configuration = new JcrConfiguration(context);
+ }
+
+ @Test
+ public void shouldHaveDefaultConfigurationSourceIfNotSpecified() {
+ assertThat(configuration.graph(), is(notNullValue()));
+ }
+
+ @Test
+ public void shouldAllowAddingRepositorySourceInstance() {
+ UUID rootUuid = UUID.randomUUID();
+ CachePolicy cachePolicy = new ImmutableCachePolicy(100);
+ InMemoryRepositorySource newSource = new InMemoryRepositorySource();
+ newSource.setName("name");
+ newSource.setDefaultCachePolicy(cachePolicy);
+ newSource.setDefaultWorkspaceName("default workspace name");
+ newSource.setRetryLimit(100);
+ newSource.setRootNodeUuid(rootUuid);
+
+ // Update the configuration and save it ...
+ configuration.addRepository(newSource).save();
+
+ // Verify that the graph has been updated correctly ...
+ Subgraph subgraph = configuration.graph().getSubgraphOfDepth(3).at("/");
+ assertThat(subgraph.getNode("/dna:sources"), is(notNullValue()));
+ assertThat(subgraph.getNode("/dna:sources/name"), is(notNullValue()));
+ assertThat(subgraph.getNode("/dna:sources/name"), hasProperty(DnaLexicon.READABLE_NAME, "name"));
+ assertThat(subgraph.getNode("/dna:sources/name"), hasProperty(DnaLexicon.RETRY_LIMIT, 100));
+ assertThat(subgraph.getNode("/dna:sources/name"), hasProperty(DnaLexicon.DEFAULT_CACHE_POLICY, cachePolicy));
+ assertThat(subgraph.getNode("/dna:sources/name"), hasProperty("defaultWorkspaceName", "default workspace name"));
+ assertThat(subgraph.getNode("/dna:sources/name"), hasProperty("rootNodeUuid", rootUuid));
+ }
+
+ @Test
+ public void shouldAllowAddingRepositorySourceByClassNameAndSettingProperties() {
+ // Update the configuration and save it ...
+ configuration.addRepository("Source1")
+ .usingClass(InMemoryRepositorySource.class.getName())
+ .loadedFromClasspath()
+ .describedAs("description")
+ .with("retryLimit")
+ .setTo(5)
+ .and()
+ .save();
+
+ // Verify that the graph has been updated correctly ...
+ Subgraph subgraph = configuration.graph().getSubgraphOfDepth(3).at("/");
+ assertThat(subgraph.getNode("/dna:sources"), is(notNullValue()));
+ assertThat(subgraph.getNode("/dna:sources/Source1"), is(notNullValue()));
+ assertThat(subgraph.getNode("/dna:sources/Source1"), hasProperty(DnaLexicon.READABLE_NAME, "Source1"));
+ assertThat(subgraph.getNode("/dna:sources/Source1"), hasProperty(DnaLexicon.RETRY_LIMIT, 5));
+ assertThat(subgraph.getNode("/dna:sources/Source1"), hasProperty(DnaLexicon.CLASSNAME,
+ InMemoryRepositorySource.class.getName()));
+ }
+
+ @Test
+ public void shouldAllowAddingRepositorySourceByClassNameAndClasspathAndSettingProperties() {
+ // Update the configuration and save it ...
+ configuration.addRepository("Source1")
+ .usingClass(InMemoryRepositorySource.class.getName())
+ .loadedFrom("cp1", "cp2")
+ .describedAs("description")
+ .with("retryLimit")
+ .setTo(5)
+ .and()
+ .save();
+
+ // Verify that the graph has been updated correctly ...
+ Subgraph subgraph = configuration.graph().getSubgraphOfDepth(3).at("/");
+ assertThat(subgraph.getNode("/dna:sources"), is(notNullValue()));
+ assertThat(subgraph.getNode("/dna:sources/Source1"), is(notNullValue()));
+ assertThat(subgraph.getNode("/dna:sources/Source1"), hasProperty(DnaLexicon.READABLE_NAME, "Source1"));
+ assertThat(subgraph.getNode("/dna:sources/Source1"), hasProperty(DnaLexicon.RETRY_LIMIT, 5));
+ assertThat(subgraph.getNode("/dna:sources/Source1"), hasProperty(DnaLexicon.CLASSNAME,
+ InMemoryRepositorySource.class.getName()));
+ assertThat(subgraph.getNode("/dna:sources/Source1"), hasProperty(DnaLexicon.CLASSPATH, "cp1", "cp2"));
+ }
+
+ @Test
+ public void shouldAllowAddingRepositorySourceByClassReferenceAndSettingProperties() {
+ // Update the configuration and save it ...
+ configuration.addRepository("Source1")
+ .usingClass(InMemoryRepositorySource.class)
+ .describedAs("description")
+ .with("retryLimit")
+ .setTo(5)
+ .and()
+ .save();
+
+ // Verify that the graph has been updated correctly ...
+ Subgraph subgraph = configuration.graph().getSubgraphOfDepth(3).at("/");
+ assertThat(subgraph.getNode("/dna:sources"), is(notNullValue()));
+ assertThat(subgraph.getNode("/dna:sources/Source1"), is(notNullValue()));
+ assertThat(subgraph.getNode("/dna:sources/Source1"), hasProperty(DnaLexicon.READABLE_NAME, "Source1"));
+ assertThat(subgraph.getNode("/dna:sources/Source1"), hasProperty(DnaLexicon.RETRY_LIMIT, 5));
+ assertThat(subgraph.getNode("/dna:sources/Source1"), hasProperty(DnaLexicon.DESCRIPTION, "description"));
+ assertThat(subgraph.getNode("/dna:sources/Source1"), hasProperty(DnaLexicon.CLASSNAME,
+ InMemoryRepositorySource.class.getName()));
+ }
+
+ @Test
+ public void shouldAllowOverwritingRepositorySourceByRepositoryName() {
+ // Update the configuration and save it ...
+ configuration.addRepository("Source1")
+ .usingClass(InMemoryRepositorySource.class)
+ .describedAs("description")
+ .with("retryLimit")
+ .setTo(3)
+ .and()
+ .addRepository("Source1")
+ .usingClass(InMemoryRepositorySource.class)
+ .describedAs("new description")
+ .with("retryLimit")
+ .setTo(6)
+ .and()
+ .save();
+
+ // Verify that the graph has been updated correctly ...
+ Subgraph subgraph = configuration.graph().getSubgraphOfDepth(3).at("/");
+ assertThat(subgraph.getNode("/dna:sources"), is(notNullValue()));
+ assertThat(subgraph.getNode("/dna:sources").getChildren(), hasChild(segment("Source1")));
+ assertThat(subgraph.getNode("/dna:sources/Source1"), is(notNullValue()));
+ assertThat(subgraph.getNode("/dna:sources/Source1"), hasProperty(DnaLexicon.READABLE_NAME, "Source1"));
+ assertThat(subgraph.getNode("/dna:sources/Source1"), hasProperty(DnaLexicon.RETRY_LIMIT, 6));
+ assertThat(subgraph.getNode("/dna:sources/Source1"), hasProperty(DnaLexicon.DESCRIPTION, "new description"));
+ assertThat(subgraph.getNode("/dna:sources/Source1"), hasProperty(DnaLexicon.CLASSNAME,
+ InMemoryRepositorySource.class.getName()));
+ }
+
+ @Test
+ public void shouldAllowAddingMimeTypeDetector() {
+ // Update the configuration and save it ...
+ configuration.addRepository("Source1")
+ .usingClass(InMemoryRepositorySource.class)
+ .describedAs("description")
+ .and()
+ .addMimeTypeDetector("detector")
+ .usingClass(ExtensionBasedMimeTypeDetector.class)
+ .describedAs("default detector")
+ .and()
+ .save();
+
+ // Verify that the graph has been updated correctly ...
+ Subgraph subgraph = configuration.graph().getSubgraphOfDepth(3).at("/");
+ assertThat(subgraph.getNode("/dna:sources").getChildren(), hasChild(segment("Source1")));
+ assertThat(subgraph.getNode("/dna:sources/Source1"), is(notNullValue()));
+ assertThat(subgraph.getNode("/dna:sources/Source1"), hasProperty(DnaLexicon.READABLE_NAME, "Source1"));
+ assertThat(subgraph.getNode("/dna:sources/Source1"), hasProperty(DnaLexicon.DESCRIPTION, "description"));
+ assertThat(subgraph.getNode("/dna:sources/Source1"), hasProperty(DnaLexicon.CLASSNAME,
+ InMemoryRepositorySource.class.getName()));
+ assertThat(subgraph.getNode("/dna:mimeTypeDetectors").getChildren(), hasChild(segment("detector")));
+ assertThat(subgraph.getNode("/dna:mimeTypeDetectors/detector"), is(notNullValue()));
+ assertThat(subgraph.getNode("/dna:mimeTypeDetectors/detector"), hasProperty(DnaLexicon.READABLE_NAME, "detector"));
+ assertThat(subgraph.getNode("/dna:mimeTypeDetectors/detector"), hasProperty(DnaLexicon.DESCRIPTION, "default detector"));
+ assertThat(subgraph.getNode("/dna:mimeTypeDetectors/detector"),
+ hasProperty(DnaLexicon.CLASSNAME, ExtensionBasedMimeTypeDetector.class.getName()));
+ }
+
+ @Test
+ public void shouldAllowConfigurationInMultipleSteps() {
+ configuration.addRepository("Source1").usingClass(InMemoryRepositorySource.class).describedAs("description");
+ configuration.addMimeTypeDetector("detector")
+ .usingClass(ExtensionBasedMimeTypeDetector.class)
+ .describedAs("default detector");
+ configuration.save();
+
+ // Verify that the graph has been updated correctly ...
+ Subgraph subgraph = configuration.graph().getSubgraphOfDepth(3).at("/");
+ assertThat(subgraph.getNode("/dna:sources").getChildren(), hasChild(segment("Source1")));
+ assertThat(subgraph.getNode("/dna:sources/Source1"), is(notNullValue()));
+ assertThat(subgraph.getNode("/dna:sources/Source1"), hasProperty(DnaLexicon.READABLE_NAME, "Source1"));
+ assertThat(subgraph.getNode("/dna:sources/Source1"), hasProperty(DnaLexicon.DESCRIPTION, "description"));
+ assertThat(subgraph.getNode("/dna:sources/Source1"), hasProperty(DnaLexicon.CLASSNAME,
+ InMemoryRepositorySource.class.getName()));
+ assertThat(subgraph.getNode("/dna:mimeTypeDetectors").getChildren(), hasChild(segment("detector")));
+ assertThat(subgraph.getNode("/dna:mimeTypeDetectors/detector"), is(notNullValue()));
+ assertThat(subgraph.getNode("/dna:mimeTypeDetectors/detector"), hasProperty(DnaLexicon.READABLE_NAME, "detector"));
+ assertThat(subgraph.getNode("/dna:mimeTypeDetectors/detector"), hasProperty(DnaLexicon.DESCRIPTION, "default detector"));
+ assertThat(subgraph.getNode("/dna:mimeTypeDetectors/detector"),
+ hasProperty(DnaLexicon.CLASSNAME, ExtensionBasedMimeTypeDetector.class.getName()));
+ }
+
+ @Test
+ public void shouldAllowSpecifyingOptions() throws Exception {
+ // Update the configuration and save it ...
+
+ JcrEngine engine = configuration.withConfigurationRepository()
+ .usingClass(InMemoryRepositorySource.class.getName())
+ .loadedFromClasspath()
+ .describedAs("Configuration Repository")
+ .with("name").setTo("configuration")
+ .with("retryLimit")
+ .setTo(5)
+ .and()
+ .addRepository("Source2")
+ .usingClass(InMemoryRepositorySource.class.getName())
+ .loadedFromClasspath()
+ .with(Options.JAAS_LOGIN_CONFIG_NAME).setTo("test")
+ .describedAs("description")
+ .with("name").setTo("JCR Repository")
+ .and()
+ .build();
+ engine.start();
+
+ // Verify that the graph has been updated correctly ...
+ Subgraph subgraph = configuration.graph().getSubgraphOfDepth(3).at("/");
+ assertThat(subgraph.getNode("/dna:sources"), is(notNullValue()));
+ assertThat(subgraph.getNode("/dna:sources/Source2"), is(notNullValue()));
+ assertThat(subgraph.getNode("/dna:sources/Source2"), hasProperty(DnaLexicon.READABLE_NAME, "Source2"));
+ assertThat(subgraph.getNode("/dna:sources/Source2"), hasProperty(DnaLexicon.CLASSNAME,
+ InMemoryRepositorySource.class.getName()));
+
+ JcrRepository repository = engine.getRepository("JCR Repository");
+
+ Map<Options, String> options = new HashMap<Options, String>();
+ options.put(Options.JAAS_LOGIN_CONFIG_NAME, "test");
+ options.put(Options.PROJECT_NODE_TYPES, "false");
+ assertThat(repository.getOptions(), is(options));
+ }
+
+ @Test
+ public void shouldAllowCreatingWithConfigRepository() throws InterruptedException {
+ DnaEngine engine = new DnaConfiguration().withConfigurationRepository()
+ .usingClass(InMemoryRepositorySource.class)
+ .describedAs("Configuration Repository")
+ .with("name")
+ .setTo("config repo")
+ .and()
+ .build();
+
+ assertThat(engine.getRepositorySource("config repo"), is(notNullValue()));
+ assertThat(engine.getRepositorySource("config repo"), is(instanceOf(InMemoryRepositorySource.class)));
+
+ RepositoryLibrary library = engine.getRepositoryService().getRepositorySourceManager();
+ assertThat(library.getConnectionPool("config repo").getInUseCount(), is(0));
+
+ RepositoryConnection connection = library.getConnectionPool("config repo").getConnection();
+ assertThat(connection.ping(500, TimeUnit.MILLISECONDS), is(true));
+ connection.close();
+
+ }
+}
Property changes on: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrConfigurationTest.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Modified: trunk/dna-repository/src/main/java/org/jboss/dna/repository/Configurator.java
===================================================================
--- trunk/dna-repository/src/main/java/org/jboss/dna/repository/Configurator.java 2009-05-15 14:01:49 UTC (rev 905)
+++ trunk/dna-repository/src/main/java/org/jboss/dna/repository/Configurator.java 2009-05-15 16:17:10 UTC (rev 906)
@@ -98,7 +98,7 @@
* @throws IllegalArgumentException if the repository name is null, empty, or otherwise invalid
* @see #addRepository(RepositorySource)
*/
- public ChooseClass<RepositorySource, RepositoryDetails<ReturnType>> addRepository( final String id );
+ public ChooseClass<RepositorySource, ? extends RepositoryDetails<ReturnType>> addRepository( final String id );
/**
* Add a new {@link RepositorySource repository} for this configuration. The new repository will have the supplied name,
@@ -518,8 +518,16 @@
Path path = pathFactory().create(parentPath, id);
configuration().create(path).with(DnaLexicon.READABLE_NAME, id).and();
return path;
+
}
+ protected Path createOrReplaceNode( Path parentPath,
+ Name id ) {
+ Path path = pathFactory().create(parentPath, id);
+ configuration().create(path).with(DnaLexicon.READABLE_NAME, id).and();
+ return path;
+ }
+
protected void recordBeanPropertiesInGraph( Path path,
Object javaBean ) {
Reflection reflector = new Reflection(javaBean.getClass());
@@ -652,7 +660,7 @@
protected final Path pathOfComponentNode;
protected final ReturnType returnObject;
- protected ClassChooser( Path pathOfComponentNode,
+ public ClassChooser( Path pathOfComponentNode,
ReturnType returnObject ) {
assert pathOfComponentNode != null;
assert returnObject != null;
@@ -835,6 +843,10 @@
this.returnObject = returnObject;
}
+ protected Path path() {
+ return this.path;
+ }
+
/**
* {@inheritDoc}
*
Modified: trunk/dna-repository/src/main/java/org/jboss/dna/repository/DnaConfiguration.java
===================================================================
--- trunk/dna-repository/src/main/java/org/jboss/dna/repository/DnaConfiguration.java 2009-05-15 14:01:49 UTC (rev 905)
+++ trunk/dna-repository/src/main/java/org/jboss/dna/repository/DnaConfiguration.java 2009-05-15 16:17:10 UTC (rev 906)
@@ -105,7 +105,7 @@
*
* @see org.jboss.dna.repository.Configurator.RepositoryConfigurator#addRepository(java.lang.String)
*/
- public ChooseClass<RepositorySource, RepositoryDetails<DnaConfiguration>> addRepository( String id ) {
+ public ChooseClass<RepositorySource, ? extends RepositoryDetails<DnaConfiguration>> addRepository( String id ) {
return builder.addRepository(id);
}
@@ -248,7 +248,7 @@
*
* @see org.jboss.dna.repository.Configurator.RepositoryConfigurator#addRepository(java.lang.String)
*/
- public ChooseClass<RepositorySource, RepositoryDetails<ReturnType>> addRepository( String id ) {
+ public ChooseClass<RepositorySource, ? extends RepositoryDetails<ReturnType>> addRepository( String id ) {
CheckArg.isNotEmpty(id, "id");
// Now create the "dna:source" node with the supplied id ...
Path path = createOrReplaceNode(sourcesPath(), id);
Modified: trunk/dna-repository/src/main/java/org/jboss/dna/repository/DnaLexicon.java
===================================================================
--- trunk/dna-repository/src/main/java/org/jboss/dna/repository/DnaLexicon.java 2009-05-15 14:01:49 UTC (rev 905)
+++ trunk/dna-repository/src/main/java/org/jboss/dna/repository/DnaLexicon.java 2009-05-15 16:17:10 UTC (rev 906)
@@ -40,6 +40,8 @@
public static final Name PATH_EXPRESSIONS = new BasicName(Namespace.URI, "pathExpressions");
public static final Name MIME_TYPE_DETECTORS = new BasicName(Namespace.URI, "mimeTypeDetectors");
public static final Name MIME_TYPE_DETECTOR = new BasicName(Namespace.URI, "mimeTypeDetector");
+ public static final Name OPTIONS = new BasicName(Namespace.URI, "options");
+ public static final Name VALUE = new BasicName(Namespace.URI, "value");
public static final Name RETRY_LIMIT = new BasicName(Namespace.URI, "retryLimit");
public static final Name DEFAULT_CACHE_POLICY = new BasicName(Namespace.URI, "defaultCachePolicy");
}
16 years, 11 months
DNA SVN: r905 - in trunk/dna-jcr/src: test/resources and 1 other directory.
by dna-commits@lists.jboss.org
Author: bcarothers
Date: 2009-05-15 10:01:49 -0400 (Fri, 15 May 2009)
New Revision: 905
Modified:
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrQueryManager.java
trunk/dna-jcr/src/test/resources/repositoryStubImpl.properties
Log:
DNA-410 Several JCR Query Persistence Tests Are Failing
Applied patch which makes the following changes:
- Makes JcrQueryManager.AbstractJcrQuery update its internal storedPath field when storeAsNode() is called successfully
- Removes @Immutable annotation from JcrQueryManager.AbstractJcrQuery and JcrQueryManager.XPathQuery, as the storedPath field is now mutable.
- Adds check in storeAsNode to throw RepositoryException if a malformed path is provided as an argument
- Fixes test configuration to use a special node type for the SaveTest class that does not allow children. This needs to be overridden on a per-test-class basis as many other test classes assume that the same node type does allow children.
Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrQueryManager.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrQueryManager.java 2009-05-15 13:17:40 UTC (rev 904)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrQueryManager.java 2009-05-15 14:01:49 UTC (rev 905)
@@ -34,6 +34,7 @@
import javax.jcr.query.QueryManager;
import javax.jcr.query.QueryResult;
import net.jcip.annotations.Immutable;
+import net.jcip.annotations.NotThreadSafe;
import org.jboss.dna.graph.property.NamespaceRegistry;
import org.jboss.dna.graph.property.Path;
@@ -112,12 +113,12 @@
return new String[] {Query.XPATH};
}
- @Immutable
+ @NotThreadSafe
protected abstract class AbstractJcrQuery implements Query {
private final JcrSession session;
private final String language;
private final String statement;
- private final Path storedPath;
+ private Path storedPath;
protected AbstractJcrQuery( JcrSession session,
String statement,
@@ -184,7 +185,13 @@
throws PathNotFoundException, ConstraintViolationException, RepositoryException {
NamespaceRegistry namespaces = this.session.namespaces();
- Path path = session.getExecutionContext().getValueFactories().getPathFactory().create(absPath);
+ Path path;
+ try {
+ path = session.getExecutionContext().getValueFactories().getPathFactory().create(absPath);
+ }
+ catch (IllegalArgumentException iae) {
+ throw new RepositoryException(JcrI18n.invalidPathParameter.text("absPath", absPath));
+ }
Path parentPath = path.getParent();
Node parentNode = session.getNode(parentPath);
@@ -194,12 +201,14 @@
queryNode.setProperty(JcrLexicon.LANGUAGE.getString(namespaces), this.language);
queryNode.setProperty(JcrLexicon.STATEMENT.getString(namespaces), this.statement);
+ this.storedPath = path;
+
return queryNode;
}
}
- @Immutable
+ @NotThreadSafe
protected class XPathQuery extends AbstractJcrQuery {
XPathQuery( JcrSession session,
Modified: trunk/dna-jcr/src/test/resources/repositoryStubImpl.properties
===================================================================
--- trunk/dna-jcr/src/test/resources/repositoryStubImpl.properties 2009-05-15 13:17:40 UTC (rev 904)
+++ trunk/dna-jcr/src/test/resources/repositoryStubImpl.properties 2009-05-15 14:01:49 UTC (rev 905)
@@ -43,6 +43,8 @@
javax.jcr.tck.NodeTest.testSaveContstraintViolationException.nodetype2=dnatest\:nodeWithMandatoryProperty
javax.jcr.tck.NodeOrderableChildNodesTest.testOrderBeforeUnsupportedRepositoryOperationException.nodetype2=dnatest\:unorderableUnstructured
javax.jcr.tck.NodeOrderableChildNodesTest.testOrderBeforeUnsupportedRepositoryOperationException.nodetype3=dnatest\:unorderableUnstructured
+# For some reason, this test assumes testNodeType doesn't allow children - most other tests assume that it does
+javax.jcr.tck.SaveTest.nodetype=nt\:query
# Test users
javax.jcr.tck.superuser.name=superuser
16 years, 11 months