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 }