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.jcr;
25  
26  import java.util.ArrayList;
27  import java.util.Collection;
28  import javax.jcr.AccessDeniedException;
29  import javax.jcr.InvalidItemStateException;
30  import javax.jcr.Property;
31  import javax.jcr.PropertyIterator;
32  import javax.jcr.RepositoryException;
33  import javax.jcr.nodetype.ConstraintViolationException;
34  import net.jcip.annotations.NotThreadSafe;
35  import org.modeshape.graph.Location;
36  import org.modeshape.graph.property.Path;
37  import org.modeshape.graph.session.GraphSession.Node;
38  import org.modeshape.jcr.SessionCache.JcrNodePayload;
39  import org.modeshape.jcr.SessionCache.JcrPropertyPayload;
40  
41  /**
42   * A concrete {@link javax.jcr.Node JCR Node} implementation that is used for all nodes that are part of a shared set but not the
43   * original node that was shared. In essense, all instances of JcrShareableNode are proxies that have their own name and location,
44   * but that delegate nearly all other operations to the referenced node.
45   * <p>
46   * Instances of this class are created for each node in a workspace that have a primary type of "{@link ModeShapeLexicon#SHARE
47   * mode:share}". These nodes each have a single "{@link ModeShapeLexicon#SHARED_UUID mode:sharedUuid}" REFERENCE property that
48   * points to the original shareable node. Thus, with the help of this class, JCR clients do not ever see this "mode:share" node,
49   * but instead see the original sharable node.
50   * </p>
51   * 
52   * @see JcrRootNode
53   * @see JcrNode
54   */
55  @NotThreadSafe
56  class JcrSharedNode extends JcrNode {
57  
58      /** The UUID of the "mode:share" proxy node, hidden from the user. */
59      private AbstractJcrNode original;
60      private AbstractJcrNode proxy;
61  
62      JcrSharedNode( AbstractJcrNode proxy,
63                     AbstractJcrNode original ) {
64          // Set the super's nodeId and location to be that of the original, not the proxy. We'll override
65          // all the methods that need the proxy information ....
66          super(proxy.cache, original.nodeId, original.location);
67          this.proxy = proxy;
68          this.original = original;
69          assert proxy.cache == original.cache : "Only able to share nodes within the same cache";
70          assert !proxy.isRoot() : "The root node can never be a shared node";
71          assert !original.isRoot() : "The root node can never be shareable";
72      }
73  
74      /**
75       * Get the node that represents the proxy, and is a true representation of the underlying node with a primary type of
76       * {@link ModeShapeLexicon#SHARE mode:share} and lone {@link ModeShapeLexicon#SHARED_UUID mode:sharedUuid} property.
77       * 
78       * @return the proxy node
79       */
80      AbstractJcrNode proxyNode() {
81          return proxy;
82      }
83  
84      /**
85       * Get the node that represents the original node that is being shared by this proxy.
86       * 
87       * @return the original node
88       */
89      AbstractJcrNode originalNode() {
90          return original;
91      }
92  
93      /**
94       * {@inheritDoc}
95       * 
96       * @see org.modeshape.jcr.AbstractJcrNode#isShareable()
97       */
98      @Override
99      boolean isShareable() {
100         return original != null;
101     }
102 
103     /**
104      * {@inheritDoc}
105      * 
106      * @see org.modeshape.jcr.AbstractJcrNode#isShared()
107      */
108     @Override
109     boolean isShared() {
110         return true;
111     }
112 
113     /**
114      * {@inheritDoc}
115      * 
116      * @see org.modeshape.jcr.AbstractJcrNode#setLocation(org.modeshape.graph.Location)
117      */
118     @Override
119     void setLocation( Location location ) {
120         proxyNode().setLocation(location);
121     }
122 
123     @Override
124     protected Location locationToDestroy() {
125         return proxyNode().location();
126     }
127 
128     @Override
129     protected void doDestroy() throws AccessDeniedException, RepositoryException {
130         proxyNode().editor().destroy();
131     }
132 
133     @Override
134     Node<JcrNodePayload, JcrPropertyPayload> nodeInfo()
135         throws InvalidItemStateException, AccessDeniedException, RepositoryException {
136         return original.nodeInfo();
137     }
138 
139     Node<JcrNodePayload, JcrPropertyPayload> proxyInfo()
140         throws InvalidItemStateException, AccessDeniedException, RepositoryException {
141         return proxy.nodeInfo();
142     }
143 
144     /**
145      * {@inheritDoc}
146      * <p>
147      * The parent of this shared node is the parent of the proxy, not of the original shareable node.
148      * </p>
149      * 
150      * @see org.modeshape.jcr.AbstractJcrNode#parentNodeInfo()
151      */
152     @Override
153     Node<JcrNodePayload, JcrPropertyPayload> parentNodeInfo()
154         throws InvalidItemStateException, AccessDeniedException, RepositoryException {
155         return proxyInfo().getParent();
156     }
157 
158     /**
159      * {@inheritDoc}
160      * <p>
161      * The path of this shared node is the path of the proxy, not of the original shareable node.
162      * </p>
163      * 
164      * @see org.modeshape.jcr.AbstractJcrNode#path()
165      */
166     @Override
167     Path path() throws RepositoryException {
168         return proxyInfo().getPath();
169     }
170 
171     /**
172      * {@inheritDoc}
173      * <p>
174      * The segment of this shared node is the segment of the proxy, not of the original shareable node.
175      * </p>
176      * 
177      * @see org.modeshape.jcr.AbstractJcrNode#path()
178      */
179     @Override
180     Path.Segment segment() throws RepositoryException {
181         return proxyInfo().getSegment();
182     }
183 
184     /**
185      * {@inheritDoc}
186      * 
187      * @see org.modeshape.jcr.AbstractJcrNode#getCorrespondenceId()
188      */
189     @Override
190     protected CorrespondenceId getCorrespondenceId() throws RepositoryException {
191         return original.getCorrespondenceId();
192     }
193 
194     /**
195      * {@inheritDoc}
196      * 
197      * @see org.modeshape.jcr.AbstractJcrNode#removeMixin(java.lang.String)
198      */
199     @Override
200     public void removeMixin( String mixinName ) throws RepositoryException {
201         if (cache.stringFactory().create(JcrMixLexicon.SHAREABLE).equals(mixinName)) {
202             // Per section 14.15 of the JCR 2.0 specification, we can do a few things.
203             // We could remove this shared node via removeShare(),
204             // or do something else to the content to adjust to the missing mixin name,
205             // or we could throw an exception ...
206             throw new ConstraintViolationException();
207         }
208         super.removeMixin(mixinName);
209     }
210 
211     /**
212      * {@inheritDoc}
213      * 
214      * @see org.modeshape.jcr.AbstractJcrNode#getProperty(java.lang.String)
215      */
216     @Override
217     public Property getProperty( String relativePath ) throws RepositoryException {
218         return adapt(super.getProperty(relativePath));
219     }
220 
221     /**
222      * {@inheritDoc}
223      * 
224      * @see org.modeshape.jcr.AbstractJcrNode#getProperties()
225      */
226     @Override
227     public PropertyIterator getProperties() throws RepositoryException {
228         return adapt(super.getProperties());
229     }
230 
231     /**
232      * {@inheritDoc}
233      * 
234      * @see org.modeshape.jcr.AbstractJcrNode#getProperties(java.lang.String)
235      */
236     @Override
237     public PropertyIterator getProperties( String namePattern ) throws RepositoryException {
238         return adapt(super.getProperties(namePattern));
239     }
240 
241     /**
242      * {@inheritDoc}
243      * 
244      * @see org.modeshape.jcr.AbstractJcrNode#getProperties(java.lang.String[])
245      */
246     @Override
247     public PropertyIterator getProperties( String[] nameGlobs ) throws RepositoryException {
248         return adapt(super.getProperties(nameGlobs));
249     }
250 
251     /**
252      * Adapt the property objects so that their owner is this node, not the original's.
253      * 
254      * @param property the property from the original
255      * @return the adapted property
256      */
257     protected Property adapt( Property property ) {
258         if (property instanceof JcrSingleValueProperty) {
259             JcrSingleValueProperty original = (JcrSingleValueProperty)property;
260             return new JcrSingleValueProperty(cache, this, original.name());
261         }
262         if (property instanceof JcrMultiValueProperty) {
263             JcrMultiValueProperty original = (JcrMultiValueProperty)property;
264             return new JcrMultiValueProperty(cache, this, original.name());
265         }
266         return property;
267     }
268 
269     protected PropertyIterator adapt( PropertyIterator propertyIter ) {
270         Collection<Property> props = new ArrayList<Property>((int)propertyIter.getSize());
271         while (propertyIter.hasNext()) {
272             props.add(adapt(propertyIter.nextProperty()));
273         }
274         return new JcrPropertyIterator(props);
275     }
276 }