001/* 002 * Copyright 2010 Anyware Services 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.ametys.plugins.repository.jcr; 018 019import javax.jcr.Node; 020import javax.jcr.NodeIterator; 021import javax.jcr.RepositoryException; 022import javax.jcr.nodetype.ConstraintViolationException; 023 024import org.apache.jackrabbit.util.Text; 025 026import org.ametys.plugins.repository.AbstractAmetysObject; 027import org.ametys.plugins.repository.AmetysObject; 028import org.ametys.plugins.repository.AmetysRepositoryException; 029import org.ametys.plugins.repository.RepositoryIntegrityViolationException; 030import org.ametys.plugins.repository.metadata.ModifiableCompositeMetadata; 031import org.ametys.plugins.repository.metadata.jcr.JCRCompositeMetadata; 032 033/** 034 * Default implementation of an {@link AmetysObject}, backed by a JCR node.<br> 035 * This implementation heavily relies on its {@link SimpleAmetysObjectFactory} counterpart. 036 * @param <F> the actual type of factory. 037 */ 038public class SimpleAmetysObject<F extends SimpleAmetysObjectFactory> extends AbstractAmetysObject implements JCRAmetysObject 039{ 040 /** The corresponding {@link SimpleAmetysObjectFactory} */ 041 private F _factory; 042 043 private Node _node; 044 045 private String _name; 046 private String _parentPath; 047 048 /** 049 * Creates an {@link SimpleAmetysObject}. 050 * @param node the node backing this {@link AmetysObject} 051 * @param parentPath the parentPath in the Ametys hierarchy 052 * @param factory the DefaultTraversableAmetysObjectFactory which created the AmetysObject 053 */ 054 public SimpleAmetysObject(Node node, String parentPath, F factory) 055 { 056 _node = node; 057 _parentPath = parentPath; 058 _factory = factory; 059 060 try 061 { 062 _name = Text.unescapeIllegalJcrChars(_node.getName()); 063 } 064 catch (RepositoryException e) 065 { 066 throw new AmetysRepositoryException("Unable to get node name", e); 067 } 068 } 069 070 /** 071 * Retrieves the factory. 072 * @return the factory. 073 */ 074 protected F _getFactory() 075 { 076 return _factory; 077 } 078 079 public String getName() throws AmetysRepositoryException 080 { 081 return _name; 082 } 083 084 public ModifiableCompositeMetadata getMetadataHolder() 085 { 086 return new JCRCompositeMetadata(getNode(), _getFactory()._getResolver()); 087 } 088 089 public String getParentPath() throws AmetysRepositoryException 090 { 091 if (_parentPath == null) 092 { 093 _parentPath = getParent().getPath(); 094 } 095 096 return _parentPath; 097 } 098 099 /** 100 * Invalidates cached parent path. 101 */ 102 protected void _invalidateParentPath() 103 { 104 _parentPath = null; 105 } 106 107 /** 108 * Recompute the name from the Node.<br> 109 * To be used when the node name changesd internally. 110 */ 111 protected void _invalidateName() 112 { 113 try 114 { 115 _name = Text.unescapeIllegalJcrChars(getNode().getName()); 116 } 117 catch (RepositoryException e) 118 { 119 throw new AmetysRepositoryException(e); 120 } 121 } 122 123 public String getPath() throws AmetysRepositoryException 124 { 125 return getParentPath() + "/" + getName(); 126 } 127 128 public Node getNode() 129 { 130 return _node; 131 } 132 133 public String getId() 134 { 135 try 136 { 137 return _factory.getScheme() + "://" + _node.getIdentifier(); 138 } 139 catch (RepositoryException e) 140 { 141 throw new AmetysRepositoryException("Unable to get node UUID", e); 142 } 143 } 144 145 @Override 146 public void rename(String newName) throws AmetysRepositoryException 147 { 148 try 149 { 150 Node parentNode = getNode().getParent(); 151 boolean order = parentNode.getPrimaryNodeType().hasOrderableChildNodes(); 152 Node nextSibling = null; 153 154 if (order) 155 { 156 // iterate over the siblings to find the following 157 NodeIterator siblings = parentNode.getNodes(); 158 boolean iterate = true; 159 160 while (siblings.hasNext() && iterate) 161 { 162 Node sibling = siblings.nextNode(); 163 iterate = !sibling.getName().equals(getNode().getName()); 164 } 165 166 // iterator is currently on the node 167 while (siblings.hasNext() && nextSibling == null) 168 { 169 Node sibling = siblings.nextNode(); 170 String path = sibling.getPath(); 171 if (getNode().getSession().itemExists(path)) 172 { 173 nextSibling = sibling; 174 } 175 } 176 } 177 178 getNode().getSession().move(getNode().getPath(), getNode().getParent().getPath() + "/" + newName); 179 180 if (order) 181 { 182 // nextSibling is either null meaning that the Node must be ordered last or is equals to the following sibling 183 if (nextSibling != null) 184 { 185 parentNode.orderBefore(newName, nextSibling.getName()); 186 } 187 else 188 { 189 parentNode.orderBefore(newName, null); 190 } 191 } 192 193 _name = Text.unescapeIllegalJcrChars(getNode().getName()); 194 } 195 catch (RepositoryException e) 196 { 197 throw new AmetysRepositoryException(e); 198 } 199 } 200 201 public void remove() throws AmetysRepositoryException, RepositoryIntegrityViolationException 202 { 203 try 204 { 205 getNode().remove(); 206 } 207 catch (ConstraintViolationException e) 208 { 209 throw new RepositoryIntegrityViolationException(e); 210 } 211 catch (RepositoryException e) 212 { 213 throw new AmetysRepositoryException(e); 214 } 215 } 216 217 @SuppressWarnings("unchecked") 218 public <A extends AmetysObject> A getParent() throws AmetysRepositoryException 219 { 220 return (A) _factory.getParent(this); 221 } 222 223 @Override 224 public boolean needsSave() throws AmetysRepositoryException 225 { 226 try 227 { 228 return _node.getSession().hasPendingChanges(); 229 } 230 catch (RepositoryException e) 231 { 232 throw new AmetysRepositoryException(e); 233 } 234 } 235 236 public void saveChanges() throws AmetysRepositoryException 237 { 238 try 239 { 240 getNode().getSession().save(); 241 } 242 catch (javax.jcr.RepositoryException e) 243 { 244 throw new AmetysRepositoryException("Unable to save changes", e); 245 } 246 } 247 248 @Override 249 public void revertChanges() throws AmetysRepositoryException 250 { 251 try 252 { 253 getNode().refresh(false); 254 } 255 catch (javax.jcr.RepositoryException e) 256 { 257 throw new AmetysRepositoryException("Unable to revert changes.", e); 258 } 259 } 260 261}