View Javadoc

1   /*
2    * ModeShape (http://www.modeshape.org)
3    * See the COPYRIGHT.txt file distributed with this work for information
4    * regarding copyright ownership.  Some portions may be licensed
5    * to Red Hat, Inc. under one or more contributor license agreements.
6    * See the AUTHORS.txt file in the distribution for a full listing of 
7    * individual contributors.
8    *
9    * ModeShape is free software. Unless otherwise indicated, all code in ModeShape
10   * is licensed to you under the terms of the GNU Lesser General Public License as
11   * published by the Free Software Foundation; either version 2.1 of
12   * the License, or (at your option) any later version.
13   * 
14   * ModeShape is distributed in the hope that it will be useful,
15   * but WITHOUT ANY WARRANTY; without even the implied warranty of
16   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17   * Lesser General Public License for more details.
18   *
19   * You should have received a copy of the GNU Lesser General Public
20   * License along with this software; if not, write to the Free
21   * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
22   * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
23   */
24  package org.modeshape.graph.connector.base;
25  
26  import java.util.List;
27  import java.util.UUID;
28  import java.util.concurrent.locks.ReadWriteLock;
29  import net.jcip.annotations.NotThreadSafe;
30  import org.modeshape.graph.Location;
31  import org.modeshape.graph.property.Path;
32  
33  /**
34   * The {@link Workspace} implementation that represents all nodes as {@link PathNode} objects and stores them in an internal data
35   * structure that allows for nodes to be accessed via a {@link Path}.
36   * <p>
37   * Subclasses are required to provide thread-safe access and modification of the state within the encapsulated data structure,
38   * since multiple {@link Transaction} implementations may be {@link Transaction#commit() committing} changes to the data structure
39   * at the same time. However, this class does not provide any thread-safety, since the nature of the thread-safety will almost
40   * certainly depend on the actual implementation. For example, a subclass may use a {@link ReadWriteLock lock}, or it may use a
41   * map implementation that provides the thread-safety.
42   * </p>
43   * 
44   * @param <NodeType> the type of node
45   */
46  @NotThreadSafe
47  public abstract class PathWorkspace<NodeType extends PathNode> implements Workspace {
48  
49      private final String name;
50      private final UUID rootNodeUuid;
51  
52      /**
53       * Create a new instance of the workspace.
54       * 
55       * @param name the workspace name; may not be null
56       * @param rootNodeUuid the root node that is expected to already exist in the map
57       */
58      public PathWorkspace( String name,
59                            UUID rootNodeUuid ) {
60          this.name = name;
61          this.rootNodeUuid = rootNodeUuid;
62          assert this.name != null;
63          assert this.rootNodeUuid != null;
64      }
65  
66      /**
67       * Create a new instance of the workspace.
68       * 
69       * @param name the workspace name; may not be null
70       * @param originalToClone the workspace that is to be cloned; may not be null
71       */
72      public PathWorkspace( String name,
73                            PathWorkspace<NodeType> originalToClone ) {
74          this.name = name;
75          this.rootNodeUuid = originalToClone.getRootNode().getUuid();
76          assert this.name != null;
77          assert this.rootNodeUuid != null;
78          throw new UnsupportedOperationException("Need to implement the ability to clone a workspace");
79      }
80  
81      /**
82       * {@inheritDoc}
83       * 
84       * @see org.modeshape.graph.connector.base.Workspace#getName()
85       */
86      public String getName() {
87          return name;
88      }
89  
90      protected UUID getRootNodeUuid() {
91          return rootNodeUuid;
92      }
93  
94      /**
95       * Get the root node in this workspace.
96       * 
97       * @return the root node; never null
98       */
99      public abstract NodeType getRootNode();
100 
101     /**
102      * Get the node with the supplied path.
103      * 
104      * @param path the path to the node
105      * @return the node state as known by this workspace, or null if no such node exists in this workspace
106      */
107     public abstract NodeType getNode( Path path );
108 
109     /**
110      * Verify that the supplied node exists.
111      * 
112      * @param path the path of the node; may not be null
113      * @return the location of the node if it exists, or null if it does not
114      */
115     public Location verifyNodeExists( Path path ) {
116         NodeType node = getNode(path);
117         return node != null ? Location.create(path) : null;
118     }
119 
120     /**
121      * Save this node into the workspace, overwriting any previous record of the node. This method should be overridden by
122      * writable path workspace implementations that use the default {@link ChangeCommand} implementations.
123      * 
124      * @param node the new node; may not be null
125      * @return the previous node state, or null if the node is new to this workspace
126      * @throws UnsupportedOperationException by default, subclasses should override this method so that this exception is not
127      *         thrown
128      * @see #createMoveCommand(PathNode, PathNode)
129      * @see #createPutCommand(PathNode, PathNode)
130      * @see #createRemoveCommand(Path)
131      */
132     public NodeType putNode( NodeType node ) {
133         throw new UnsupportedOperationException();
134     }
135 
136     /**
137      * Move the node from it's previous location to the new location, overwriting any previous node at that location. This method
138      * should be overridden by writable path workspace implementations that use the default {@link ChangeCommand} implementations.
139      * <p>
140      * The move operation is intended to reflect changes to the node's {@link PathNode#getName() name} or
141      * {@link PathNode#getParent() parent} only. Changes to the children or properties of the node should be reflected separately
142      * in a {@link #putNode(PathNode) put command} of some sort. The details of the put command are implementation-specific.
143      * </p>
144      * 
145      * @param source the original version of the node to be moved; may not be null
146      * @param target the new version (implying a change to the name or parent) of the node to be moved; may not be null
147      * @return the new node state;may not be null
148      * @throws UnsupportedOperationException by default, subclasses should override this method so that this exception is not
149      *         thrown
150      * @see #createMoveCommand(PathNode, PathNode)
151      * @see #createPutCommand(PathNode, PathNode)
152      * @see #createRemoveCommand(Path)
153      */
154     public NodeType moveNode( NodeType source,
155                               NodeType target ) {
156         throw new UnsupportedOperationException();
157     }
158 
159     /**
160      * Remove this node and its descendants from the workspace. This method should be overridden by writable path workspace
161      * implementations that use the default {@link ChangeCommand} implementations.
162      * 
163      * @param path the path to the node to be removed; may not be null
164      * @return the previous node state, or null if the node does not exist in this workspace
165      * @throws UnsupportedOperationException by default, subclasses should override this method so that this exception is not
166      *         thrown
167      * @see #createMoveCommand(PathNode, PathNode)
168      * @see #createPutCommand(PathNode, PathNode)
169      * @see #createRemoveCommand(Path)
170      */
171     public NodeType removeNode( Path path ) {
172         throw new UnsupportedOperationException();
173     }
174 
175     /**
176      * Remove all of the nodes in this workspace, and make sure there is a single root node with no properties and no children.
177      */
178     public void removeAll() {
179         throw new UnsupportedOperationException();
180 
181     }
182 
183     /**
184      * {@inheritDoc}
185      * 
186      * @see java.lang.Object#toString()
187      */
188     @Override
189     public String toString() {
190         return name;
191     }
192 
193     /**
194      * Successively (and in order) apply the changes from the list of pending commands
195      * <p>
196      * All validation for each of the objects (including validation of resource availability in the underlying persistent store)
197      * should be performed prior to invoking this method.
198      * </p>
199      * 
200      * @param commands the list of commands to apply
201      */
202     public void commit( List<ChangeCommand<NodeType>> commands ) {
203         for (ChangeCommand<NodeType> command : commands) {
204             command.apply();
205         }
206     }
207 
208     /**
209      * Create a change command for the required update to the given node
210      * 
211      * @param oldNode the prior version of the node; may be null if this is a new node
212      * @param node the new version of the node; may not be null
213      * @return a {@link ChangeCommand} instance that reflects the changes to the node
214      * @see #commit(List)
215      */
216     public ChangeCommand<NodeType> createPutCommand( NodeType oldNode,
217                                                      NodeType node ) {
218         return new PutCommand(node);
219     }
220 
221     /**
222      * Create a change command for the removal of the given node and its descendants
223      * 
224      * @param path the path to the node at the root of the branch to be removed; may not be null
225      * @return a {@link ChangeCommand} instance that reflects the changes to the node
226      * @see #createPutCommand(PathNode, PathNode)
227      * @see #commit(List)
228      */
229     public ChangeCommand<NodeType> createRemoveCommand( Path path ) {
230         return new RemoveCommand(path);
231     }
232 
233     /**
234      * Create a change command that represents the movement of a node. The movement record will only reflect the changes to the
235      * node's name and/or parent. Changes to the node's properties or children should be ignored. A separate
236      * {@link #createPutCommand(PathNode, PathNode) put command} should be used to reflect these changes.
237      * 
238      * @param source the original version of the node; may not be null
239      * @param target the new version of the node; may not be null
240      * @return a {@link ChangeCommand} instance that reflects the changes to the node
241      * @see #createMoveCommand(PathNode, PathNode)
242      * @see #commit(List)
243      */
244     public ChangeCommand<NodeType> createMoveCommand( NodeType source,
245                                                       NodeType target ) {
246         return new MoveCommand(source, target);
247     }
248 
249     /**
250      * A specific operation that mutates the underlying persistent repository.
251      * 
252      * @param <NodeType> the type of node against which this change should apply
253      */
254     public interface ChangeCommand<NodeType extends PathNode> {
255         /**
256          * Make the change represented by this command permanent.
257          */
258         void apply();
259     }
260 
261     private class PutCommand implements ChangeCommand<NodeType> {
262         private NodeType node;
263 
264         protected PutCommand( NodeType node ) {
265             super();
266             this.node = node;
267         }
268 
269         public void apply() {
270             PathWorkspace.this.putNode(node);
271         }
272 
273         @Override
274         public String toString() {
275             return "Put: { " + node + "}";
276         }
277     }
278 
279     private class RemoveCommand implements ChangeCommand<NodeType> {
280         private Path path;
281 
282         protected RemoveCommand( Path path ) {
283             super();
284             this.path = path;
285         }
286 
287         public void apply() {
288             PathWorkspace.this.removeNode(path);
289         }
290 
291         @Override
292         public String toString() {
293             return "Remove: { " + path.getString() + "}";
294         }
295     }
296 
297     private class MoveCommand implements ChangeCommand<NodeType> {
298         private NodeType node;
299         private NodeType newNode;
300 
301         protected MoveCommand( NodeType node,
302                                NodeType newNode ) {
303             super();
304             this.node = node;
305             this.newNode = newNode;
306         }
307 
308         public void apply() {
309             PathWorkspace.this.moveNode(node, newNode);
310         }
311 
312         @Override
313         public String toString() {
314             return "Move: { " + node + " to " + newNode + "}";
315         }
316     }
317 
318 }